XmlStreamStore.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / System / Windows / Annotations / Storage / XmlStreamStore.cs / 1 / XmlStreamStore.cs

                            //------------------------------------------------------------------------------ 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//     An AnnotationStore subclass based on XML streams.  Processes XML 
//     streams and returns CAF 2.0 OM objects.  Useful to other
//     AnnotationStore subclass who can get an XML stream of their 
//     content.
//     Spec: http://team/sites/ag/Specifications/CAF%20Storage%20Spec.doc
//
// History: 
//  10/04/2002: rruiz:    Added header comment to XmlStreamStore.cs.
//  05/29/2003: LGolding: Ported to WCP tree. 
//  07/17/2003: rruiz:    Made this a real AnnotationStore subclass; updated with 
//                        new APIs; renamed class.
//  01/13/2004  ssimova:  Added revert events 
//  02/12/2004  ssimova:  Added locks for thread safety
//  08/18/2004: magedz:   Modify the add/delete/querying capabilities to use the map
//
//----------------------------------------------------------------------------- 
using System;
using System.Collections; 
using System.Collections.Generic; 
using System.Globalization;
using System.Diagnostics; 
using System.IO;
using System.Windows;
using System.Windows.Annotations;
using System.Xml; 
using System.Xml.Schema;
using System.Xml.XPath; 
using System.Xml.Serialization; 
using MS.Internal;
using MS.Internal.Annotations; 
using MS.Internal.Annotations.Storage;
using MS.Utility;
using System.Windows.Markup;
using MS.Internal.Controls.StickyNote; 
using System.Windows.Controls;
 
namespace System.Windows.Annotations.Storage 
{
    ///  
    ///     An AnnotationStore subclass based on XML streams.  Processes XML
    ///     streams and returns CAF 2.0 OM objects.  Useful to other
    ///     AnnotationStore subclass who can get an XML stream of their
    ///     content. 
    /// 
    public sealed class XmlStreamStore : AnnotationStore 
    { 
        //-----------------------------------------------------
        // 
        //  Constructors
        //
        //-----------------------------------------------------
 
        #region Constructors
 
        ///  
        /// This ctor initializes the Dictionary with predefined namespases
        /// and their compatibility 
        /// 
        static XmlStreamStore()
        {
            _predefinedNamespaces = new Dictionary>(6); 
            _predefinedNamespaces.Add(new Uri(AnnotationXmlConstants.Namespaces.CoreSchemaNamespace), null);
            _predefinedNamespaces.Add(new Uri(AnnotationXmlConstants.Namespaces.BaseSchemaNamespace), null); 
            _predefinedNamespaces.Add(new Uri(XamlReaderHelper.DefaultNamespaceURI), null); 
        }
 
        /// 
        ///     Creates an instance using the XML stream passed in as the
        ///     content. The XML in the stream must be valid XML and conform
        ///     to the CAF 2.0 schema. 
        /// 
        /// stream containing annotation data in XML format 
        /// stream is null 
        /// stream contains invalid XML
        public XmlStreamStore(Stream stream) 
            : base()
        {
            if (stream == null)
                throw new ArgumentNullException("stream"); 

            if (!stream.CanSeek) 
                throw new ArgumentException(SR.Get(SRID.StreamDoesNotSupportSeek)); 

            SetStream(stream, null); 
        }

        /// 
        ///     Creates an instance using the XML stream passed in as the 
        ///     content. The XML in the stream must be valid XML and conform
        ///     to the Annotations V1 schema or a valid future version XML which 
        ///     compatibility rules are that when applied they will produce 
        ///     a valid Annotations V1 XML. This .ctor allows registration of
        ///     application specific known namespaces. 
        /// 
        /// stream containing annotation data in XML format
        /// A dictionary with known and compatible namespaces. The keys in
        /// this dictionary are known namespaces. The value of each key is a list of namespaces that are compatible with 
        /// the key one, i.e. each of the namespaces in the value list will be transformed to the
        /// key namespace while reading the input XML. 
        /// stream is null 
        /// stream contains invalid XML
        /// duplicate namespace in knownNamespaces dictionary 
        /// null key in knownNamespaces dictionary
        public XmlStreamStore(Stream stream, IDictionary> knownNamespaces)
            : base()
        { 
            if (stream == null)
                throw new ArgumentNullException("stream"); 
 
            SetStream(stream, knownNamespaces);
        } 

        #endregion Constructors

        //------------------------------------------------------ 
        //
        //  Public Methods 
        // 
        //-----------------------------------------------------
 
        #region Public Methods

        /// 
        ///     Add a new annotation to this store.  The new annotation's Id 
        ///     is set to a new value.
        ///  
        /// the annotation to be added to the store 
        /// newAnnotation is null
        /// newAnnotation already exists in this store, as determined by its Id 
        /// if no stream has been set on the store
        /// if object has been Disposed
        public override void AddAnnotation(Annotation newAnnotation)
        { 
            if (newAnnotation == null)
                throw new ArgumentNullException("newAnnotation"); 
 
            // We are going to modify internal data. Lock the object
            // to avoid modifications from other threads 
            lock (SyncRoot)
            {

                //fire trace event 
                EventTrace.NormalTraceEvent(EventTraceGuidId.ADDANNOTATIONGUID, EventType.StartEvent);
 
                CheckStatus(); 

                XPathNavigator editor = GetAnnotationNodeForId(newAnnotation.Id); 

                // we are making sure that the newAnnotation doesn't already exist in the store
                if (editor != null)
                    throw new ArgumentException(SR.Get(SRID.AnnotationAlreadyExists), "newAnnotation"); 

                // we are making sure that the newAnnotation doesn't already exist in the store map 
                if (_storeAnnotationsMap.FindAnnotation(newAnnotation.Id) != null) 
                    throw new ArgumentException(SR.Get(SRID.AnnotationAlreadyExists), "newAnnotation");
 
                // simply add the annotation to the map to save on performance
                // notice that we need to tell the map that this instance of the annotation is dirty
                _storeAnnotationsMap.AddAnnotation(newAnnotation, true);
 
                //fire trace event
                EventTrace.NormalTraceEvent(EventTraceGuidId.ADDANNOTATIONGUID, EventType.EndEvent); 
 
            }
 
            OnStoreContentChanged(new StoreContentChangedEventArgs(StoreContentAction.Added, newAnnotation));
        }

        ///  
        ///     Delete the specified annotation.
        ///  
        /// the Id of the annotation to be deleted 
        /// the annotation that was deleted, or null if no annotation
        /// with the specified Id was found 
        /// if no stream has been set on the store
        /// if object has been disposed
        public override Annotation DeleteAnnotation(Guid annotationId)
        { 
            Annotation annotation = null;
 
            // We are now going to modify internal data. Lock the object 
            // to avoid modifications from other threads
            lock (SyncRoot) 
            {

                //fire trace event
                EventTrace.NormalTraceEvent(EventTraceGuidId.DELETEANNOTATIONGUID, EventType.StartEvent); 

                CheckStatus(); 
 
                annotation = _storeAnnotationsMap.FindAnnotation(annotationId);
 
                XPathNavigator editor = GetAnnotationNodeForId(annotationId);
                if (editor != null)
                {
                    // Only deserialize the annotation if its not already in our map 
                    if (annotation == null)
                    { 
                        annotation = (Annotation)_serializer.Deserialize(editor.ReadSubtree()); 
                    }
                    editor.DeleteSelf(); 
                }

                // Remove the instance from the map
                _storeAnnotationsMap.RemoveAnnotation(annotationId); 

                // notice that in Add we add the annotation to the map only 
                // but in delete we delete it from both to the Xml and the map 

                //fire trace event 
                EventTrace.NormalTraceEvent(EventTraceGuidId.DELETEANNOTATIONGUID, EventType.EndEvent);

            }
 
            // Only fire notification if we actually removed an annotation
            if (annotation != null) 
            { 
                OnStoreContentChanged(new StoreContentChangedEventArgs(StoreContentAction.Deleted, annotation));
            } 

            return annotation;
        }
 
        /// 
        ///     Queries the Xml stream for annotations that have an anchor 
        ///     that contains a locator that begins with the locator parts 
        ///     in anchorLocator.
        ///  
        /// the locator we are looking for
        /// 
        ///    A list of annotations that have locators in their anchors
        ///    starting with the same locator parts list as of the input locator 
        ///    If no such annotations an empty list will be returned. The method
        ///    never returns null. 
        ///  
        /// if object has been Disposed
        /// the stream is null 
        public override IList GetAnnotations(ContentLocator anchorLocator)
        {
            // First we generate the XPath expression
            if (anchorLocator == null) 
                throw new ArgumentNullException("anchorLocator");
 
            if (anchorLocator.Parts == null) 
                throw new ArgumentNullException("anchorLocator.Parts");
 
            //fire trace event
            EventTrace.NormalTraceEvent(EventTraceGuidId.GETANNOTATIONBYLOCGUID, EventType.StartEvent);

            string query = @"//" + AnnotationXmlConstants.Prefixes.CoreSchemaPrefix + ":" + AnnotationXmlConstants.Elements.ContentLocator; 

            if (anchorLocator.Parts.Count > 0) 
            { 
                query += @"/child::*[1]/self::";
                for (int i = 0; i < anchorLocator.Parts.Count; i++) 
                {
                    if (anchorLocator.Parts[i] != null)
                    {
                        if (i > 0) 
                        {
                            query += @"/following-sibling::"; 
                        } 

                        string fragment = anchorLocator.Parts[i].GetQueryFragment(_namespaceManager); 

                        if (fragment != null)
                        {
                            query += fragment; 
                        }
                        else 
                        { 
                            query += "*";
                        } 
                    }
                }
            }
 
            query += @"/ancestor::" + AnnotationXmlConstants.Prefixes.CoreSchemaPrefix + ":Anchors/ancestor::" + AnnotationXmlConstants.Prefixes.CoreSchemaPrefix + ":Annotation";
 
 
            //fire trace event
            EventTrace.NormalTraceEvent(EventTraceGuidId.GETANNOTATIONBYLOCGUID, EventType.EndEvent); 

            return InternalGetAnnotations(query, anchorLocator);
        }
 
        /// 
        /// Returns a list of all annotations in the store 
        ///  
        /// annotations list. Can return an empty list, but never null.
        /// if object has been disposed 
        public override IList GetAnnotations()
        {

            //fire trace event 
            EventTrace.NormalTraceEvent(EventTraceGuidId.GETANNOTATIONSGUID, EventType.StartEvent);
 
            string query = "//" + AnnotationXmlConstants.Prefixes.CoreSchemaPrefix + ":Annotation"; 
            return InternalGetAnnotations(query, null);
 
            //fire trace event
            // unreachable - previous statement is 'return' (bug 1788240)
            #pragma warning disable 0162
            EventTrace.NormalTraceEvent(EventTraceGuidId.GETANNOTATIONSGUID, EventType.EndEvent); 
            #pragma warning restore 0162
 
        } 

        ///  
        /// Finds annotation by Id
        /// 
        /// annotation id
        /// The annotation. Null if the annotation does not exists 
        /// if object has been disposed
        public override Annotation GetAnnotation(Guid annotationId) 
        { 
            lock (SyncRoot)
            { 

                //fire trace event
                EventTrace.NormalTraceEvent(EventTraceGuidId.GETANNOTATIONBYIDGUID, EventType.StartEvent);
 
                CheckStatus();
 
                Annotation annotation = _storeAnnotationsMap.FindAnnotation(annotationId); 

                // If there is no pre-existing instance, we deserialize and create an instance. 
                if (annotation != null)
                {
                    return annotation;
                } 

                XPathNavigator editor = GetAnnotationNodeForId(annotationId); 
                if (editor != null) 
                {
                    annotation = (Annotation)_serializer.Deserialize(editor.ReadSubtree()); 

                    // Add the new instance to the map
                    _storeAnnotationsMap.AddAnnotation(annotation, false);
                } 

 
                //fire trace event 
                EventTrace.NormalTraceEvent(EventTraceGuidId.GETANNOTATIONBYIDGUID, EventType.EndEvent);
 
                return annotation;
            }
        }
 
        /// 
        ///     Causes any buffered data to be written to the underlying 
        ///     storage mechanism.  Gets called after each operation if 
        ///     AutoFlush is set to true.  The stream is truncated to
        ///     the length of the data written out by the store. 
        /// 
        /// stream cannot be written to
        /// if no stream has been set on the store
        /// if object has been disposed 
        /// 
        public override void Flush() 
        { 
            lock (SyncRoot)
            { 
                CheckStatus();
                if (!_stream.CanWrite)
                {
                    throw new UnauthorizedAccessException(SR.Get(SRID.StreamCannotBeWritten)); 
                }
 
                if (_dirty) 
                {
                    SerializeAnnotations(); 
                    _stream.Position = 0;
                    _stream.SetLength(0);
                    _document.PreserveWhitespace = true;
                    _document.Save(_stream); 
                    _stream.Flush();
                    _dirty = false; 
                } 
            }
        } 

        /// 
        /// Returns a list of namespaces that are compatible with an  input namespace
        ///  
        /// namespace
        /// a list of compatible namespaces. Can be null 
        /// This method works only with built-in AnnotationFramework namespaces. 
        /// For any other input namespace the return value will be null even if it is
        /// registered with the XmlStreamStore ctor 
        public static IList GetWellKnownCompatibleNamespaces(Uri name)
        {
            if (name == null)
            { 
                throw new ArgumentNullException("name");
            } 
            if (_predefinedNamespaces.ContainsKey(name)) 
                return _predefinedNamespaces[name];
            return null; 
        }

        #endregion Public Methods
 
        //------------------------------------------------------
        // 
        //  Public Operators 
        //
        //------------------------------------------------------ 
        //-----------------------------------------------------
        //
        //  Public Events
        // 
        //------------------------------------------------------
        //----------------------------------------------------- 
        // 
        //  Public Properties
        // 
        //-----------------------------------------------------

        #region Public Properties
 
        /// 
        ///     When set to true an implementation should call Flush() 
        ///     as a side-effect after each operation. 
        /// 
        ///  
        ///     true if the implementation is set to call Flush() after
        ///     each operation; false otherwise
        /// 
        public override bool AutoFlush 
        {
            get 
            { 
                lock (SyncRoot)
                { 
                    return _autoFlush;
                }
            }
            set 
            {
                lock (SyncRoot) 
                { 
                    _autoFlush = value;
 
                    // Commit anything that needs to be committed up to this point
                    if (_autoFlush)
                    {
                        Flush(); 
                    }
                } 
            } 
        }
 
        /// 
        /// Returns a list of the namespaces that are ignored while loading
        /// the Xml stream
        ///  
        /// The value is never null, but can be an empty list if nothing has been ignored
        public IList IgnoredNamespaces 
        { 
            get
            { 
                return _ignoredNamespaces;
            }
        }
 
        /// 
        /// Returns a list of all namespaces that are internaly used by the framework 
        ///  
        public static IList WellKnownNamespaces
        { 
            get
            {
                Uri[] res = new Uri[_predefinedNamespaces.Keys.Count];
                _predefinedNamespaces.Keys.CopyTo(res, 0); 
                return res;
            } 
        } 

 
        #endregion Public Properties

        //-----------------------------------------------------
        // 
        //  Protected Methods
        // 
        //------------------------------------------------------ 

        #region Protected Methods 

        /// 
        ///     Disposes of the resources (other than memory) used by the store.
        ///  
        /// true to release both managed and unmanaged
        /// resources; false to release only unmanaged resources 
        protected override void Dispose(bool disposing) 
        {
            //call the base class first to set _disposed to false 
            //in order to avoid working with the store when the resources
            //are released
            base.Dispose(disposing);
 
            if (disposing)
            { 
                Cleanup(); 
            }
        } 

        /// 
        ///     Called after every annotation action on the store.  We override it
        ///     to update the dirty state of the store. 
        /// 
        /// arguments for the event to fire 
        protected override void OnStoreContentChanged(StoreContentChangedEventArgs e) 
        {
            lock (SyncRoot) 
            {
                _dirty = true;
            }
 
            base.OnStoreContentChanged(e);
        } 
 
        #endregion Protected Methods
 
        //-----------------------------------------------------
        //
        //  Private Methods
        // 
        //------------------------------------------------------
 
        #region Private Methods 

        ///  
        ///     Applies the specified XPath expression to the store and returns
        ///     the results.
        /// 
        /// the XPath expression to be applied to the store 
        /// 
        ///     An IList containing zero or more annotations that match the 
        ///     criteria in the XPath expression; never will return null.  If 
        ///     no annotations meet the criteria, an empty list is returned.
        ///  
        /// if no stream has been set on the store
        /// if object has been Disposed
        /// queryExpression is null
        /// queryExpression is empty string 
        private List FindAnnotationIds(string queryExpression)
        { 
            Invariant.Assert(queryExpression != null && queryExpression.Length > 0, 
                          "Invalid query expression");
 
            Guid annId;
            List retObj = null;

            // Lock the object so nobody can change the document 
            // while the query is executed
            lock (SyncRoot) 
            { 
                CheckStatus();
 
                XPathNavigator navigator = _document.CreateNavigator();
                XPathNodeIterator iterator = navigator.Select(queryExpression, _namespaceManager);

                if (iterator != null && iterator.Count > 0) 
                {
                    retObj = new List(iterator.Count); 
                    foreach (XPathNavigator node in iterator) 
                    {
                        string nodeId = node.GetAttribute("Id", ""); 
                        if (String.IsNullOrEmpty(nodeId))
                        {
                            throw new XmlException(SR.Get(SRID.RequiredAttributeMissing, AnnotationXmlConstants.Attributes.Id, AnnotationXmlConstants.Elements.Annotation));
                        } 

                        try 
                        { 
                            annId = XmlConvert.ToGuid(nodeId);
                        } 
                        catch (FormatException fe)
                        {
                            throw new InvalidOperationException(SR.Get(SRID.CannotParseId), fe);
                        } 

                        retObj.Add(annId); 
                    } 
                }
                else 
                {
                    retObj = new List(0);
                }
            } 

            return retObj; 
        } 

        ///  
        ///     Used as AuthorChanged event handler for all annotations
        ///     handed out by the map.
        /// 
        /// annotation that sent the event 
        /// args for the event
        private void HandleAuthorChanged(object sender, AnnotationAuthorChangedEventArgs e) 
        { 
            lock (SyncRoot)
            { 
                _dirty = true;
            }

            OnAuthorChanged(e); 
        }
 
        ///  
        ///     Used as AnchorChanged event handler for all annotations
        ///     handed out by the map. 
        /// 
        /// annotation that sent the event
        /// args for the event
        private void HandleAnchorChanged(object sender, AnnotationResourceChangedEventArgs e) 
        {
            lock (SyncRoot) 
            { 
                _dirty = true;
            } 

            OnAnchorChanged(e);
        }
 
        /// 
        ///     Used as CargoChanged event handler for all annotations 
        ///     handed out by the map. 
        /// 
        /// annotation that sent the event 
        /// args for the event
        private void HandleCargoChanged(object sender, AnnotationResourceChangedEventArgs e)
        {
            lock (SyncRoot) 
            {
                _dirty = true; 
            } 

            OnCargoChanged(e); 
        }

        /// 
        /// 1- Merge Annotations queried from both the map and the Xml stream 
        /// 2- Add the annotations found in the Xml stream to the map
        ///  
        /// A dictionary of map annotations 
        /// A list of annotation ids from the Xml stream
        ///  
        private IList MergeAndCacheAnnotations(Dictionary mapAnnotations, List storeAnnotationsId)
        {
            // first put all annotations from the map in the return list
            List annotations = new List((IEnumerable)mapAnnotations.Values); 

            // there three possible conditions 
            // 1- An annotation exists in xml and in the store map results 
            // 2- An annotation exists in xml and not in the store map results
            //      2-1- The annotation is found in the map 
            //      2-2- The annotation is not found in the map

            // Now, we need to find annotations in the store that are not in the map results
            // and verify that they should be serialized 
            foreach (Guid annotationId in storeAnnotationsId)
            { 
                Annotation annot; 
                bool foundInMapResults = mapAnnotations.TryGetValue(annotationId, out annot);
                if (!foundInMapResults) 
                {
                    // it is not in the map - get it from the store
                    annot = GetAnnotation(annotationId);
                    annotations.Add(annot); 
                }
            } 
 
            return annotations;
        } 

        /// 
        /// Do the GetAnnotations work inside a lock statement for thread safety reasons
        ///  
        /// 
        ///  
        ///  
        private IList InternalGetAnnotations(string query, ContentLocator anchorLocator)
        { 
            // anchorLocator being null is handled appropriately below
            Invariant.Assert(query != null, "Parameter 'query' is null.");

            lock (SyncRoot) 
            {
                CheckStatus(); 
 
                List annotationIds = FindAnnotationIds(query);
                Dictionary annotations = null; 

                // Now, get the annotations in the map that satisfies the query criterion
                if (anchorLocator == null)
                { 
                    annotations = _storeAnnotationsMap.FindAnnotations();
                } 
                else 
                {
                    annotations = _storeAnnotationsMap.FindAnnotations(anchorLocator); 
                }

                // merge both query results
                return MergeAndCacheAnnotations(annotations, annotationIds); 
            }
        } 
 

        ///  
        ///     Loads the current stream into the XmlDocument used by this
        ///     store as a backing store.  If the stream doesn't contain any
        ///     data we load up a default XmlDocument for the Annotations schema.
        ///     The "http://schemas.microsoft.com/windows/annotations/2003/11/core" 
        ///     namespace is registered with "anc" prefix and the
        ///    "http://schemas.microsoft.com/windows/annotations/2003/11/base" namespace 
        ///     is registered with "anb" prefix as global namespaces. 
        ///     We also select from the newly loaded document the new top
        ///     level node.  This is used later for insertions, etc. 
        /// 
        /// if the stream contains invalid XML
        private void LoadStream(IDictionary> knownNamespaces)
        { 
            //check input data first
            CheckKnownNamespaces(knownNamespaces); 
 
            lock (SyncRoot)
            { 
                _document = new XmlDocument();
                _document.PreserveWhitespace = false;
                if (_stream.Length == 0)
                { 
                    _document.LoadXml(" <" +
                        AnnotationXmlConstants.Prefixes.CoreSchemaPrefix + ":Annotations xmlns:" + AnnotationXmlConstants.Prefixes.CoreSchemaPrefix + "=\"" + 
                        AnnotationXmlConstants.Namespaces.CoreSchemaNamespace + "\" xmlns:" + AnnotationXmlConstants.Prefixes.BaseSchemaPrefix + "=\"" + AnnotationXmlConstants.Namespaces.BaseSchemaNamespace + "\" />"); 
                }
                else 
                {
                    _xmlCompatibilityReader = SetupReader(knownNamespaces);
                    _document.Load(_xmlCompatibilityReader);
                } 

                _namespaceManager = new XmlNamespaceManager(_document.NameTable); 
                _namespaceManager.AddNamespace(AnnotationXmlConstants.Prefixes.CoreSchemaPrefix, AnnotationXmlConstants.Namespaces.CoreSchemaNamespace); 
                _namespaceManager.AddNamespace(AnnotationXmlConstants.Prefixes.BaseSchemaPrefix, AnnotationXmlConstants.Namespaces.BaseSchemaNamespace);
 
                // This use of an iterator is necessary because SelectSingleNode isn't available in the PD3
                // drop of the CLR.  Eventually we should be able to call SelectSingleNode and not have to
                // use an iterator to get a single node.
 
                XPathNavigator navigator = _document.CreateNavigator();
                XPathNodeIterator iterator = navigator.Select("//" + AnnotationXmlConstants.Prefixes.CoreSchemaPrefix + ":Annotations", _namespaceManager); 
                Invariant.Assert(iterator.Count == 1, "More than one annotation returned for the query"); 

                iterator.MoveNext(); 
                _rootNavigator = (XPathNavigator)iterator.Current;
            }
        }
 
        /// 
        /// Checks if passed external known namespaces are valid 
        ///  
        /// namespaces dictionary
        /// We do not allow internal namespases in this dictionary nor 
        /// duplicates
        private void CheckKnownNamespaces(IDictionary> knownNamespaces)
        {
            if (knownNamespaces == null) 
                return;
 
            IList allNamespaces = new List(); 

            //add AnnotationFramework namespaces 
            foreach (Uri name in _predefinedNamespaces.Keys)
            {
                allNamespaces.Add(name);
            } 

            //add external namespaces 
            foreach (Uri knownNamespace in knownNamespaces.Keys) 
            {
                if (knownNamespace == null) 
                {
                    throw new ArgumentException(SR.Get(SRID.NullUri), "knownNamespaces");
                }
                if (allNamespaces.Contains(knownNamespace)) 
                {
                    throw new ArgumentException(SR.Get(SRID.DuplicatedUri), "knownNamespaces"); 
                } 
                allNamespaces.Add(knownNamespace);
            } 

            // check compatible namespaces
            foreach (KeyValuePair> item in knownNamespaces)
            { 
                if (item.Value != null)
                { 
                    foreach (Uri name in item.Value) 
                    {
                        if (name == null) 
                        {
                            throw new ArgumentException(SR.Get(SRID.NullUri), "knownNamespaces");
                        }
 
                        if (allNamespaces.Contains(name))
                        { 
                            throw new ArgumentException(SR.Get(SRID.DuplicatedCompatibleUri), "knownNamespaces"); 
                        }
                        allNamespaces.Add(name); 
                    }//foreach
                }//if

            }//foreach 

        } 
 
        /// 
        /// Creates and initializes the XmlCompatibilityReader 
        /// 
        /// Dictionary of external known namespaces
        /// The XmlCompatibilityReader
        private XmlCompatibilityReader SetupReader(IDictionary> knownNamespaces) 
        {
            IList supportedNamespaces = new List(); 
 
            //add AnnotationFramework namespaces
            foreach (Uri name in _predefinedNamespaces.Keys) 
            {
                supportedNamespaces.Add(name.ToString());
            }
 
            //add external namespaces
            if (knownNamespaces != null) 
            { 
                foreach (Uri knownNamespace in knownNamespaces.Keys)
                { 
                    Debug.Assert(knownNamespace != null, "null knownNamespace");
                    supportedNamespaces.Add(knownNamespace.ToString());
                }
            } 

            //create XmlCompatibilityReader first 
            XmlCompatibilityReader reader = new XmlCompatibilityReader(new XmlTextReader(_stream), 
            new IsXmlNamespaceSupportedCallback(IsXmlNamespaceSupported), supportedNamespaces);
 
            // Declare compatibility.
            // Skip the Framework ones because they are all null in this version
            if (knownNamespaces != null)
            { 
                foreach (KeyValuePair> item in knownNamespaces)
                { 
                    if (item.Value != null) 
                    {
                        foreach (Uri name in item.Value) 
                        {
                            Debug.Assert(name != null, "null compatible namespace");
                            reader.DeclareNamespaceCompatibility(item.Key.ToString(), name.ToString());
                        }//foreach 
                    }//if
 
                }//foreach 

            }//if 

            //cleanup the _ignoredNamespaces
            _ignoredNamespaces.Clear();
            return reader; 

        } 
 
        /// 
        /// A callback for XmlCompatibilityReader 
        /// 
        /// inquired namespace
        /// the newer subsumming namespace
        /// true if the namespace is known, false if not 
        /// This API always returns false because all known namespaces are registered
        /// before loading the Xml. It stores the namespace as ignored. 
        private bool IsXmlNamespaceSupported(string xmlNamespace, out string newXmlNamespace) 
        {
 
            //store the namespace if needed
            if (!String.IsNullOrEmpty(xmlNamespace))
            {
                if (!Uri.IsWellFormedUriString(xmlNamespace, UriKind.RelativeOrAbsolute)) 
                {
                    throw new ArgumentException(SR.Get(SRID.InvalidNamespace, xmlNamespace), "xmlNamespace"); 
                } 
                Uri namespaceUri = new Uri(xmlNamespace, UriKind.RelativeOrAbsolute);
                if (!_ignoredNamespaces.Contains(namespaceUri)) 
                    _ignoredNamespaces.Add(namespaceUri);
            }

            newXmlNamespace = null; 
            return false;
        } 
 

 
        /// 
        ///     Selects the annotation XmlElement from the current document
        ///     with the given id.
        ///  
        /// the id to query for in the XmlDocument
        /// the XmlElement representing the annotation with the given id 
        private XPathNavigator GetAnnotationNodeForId(Guid id) 
        {
            XPathNavigator navigator = null; 

            lock (SyncRoot)
            {
                XPathNavigator tempNavigator = _document.CreateNavigator(); 

                // This use of an iterator is necessary because SelectSingleNode isn't available in the PD3 
                // drop of the CLR.  Eventually we should be able to call SelectSingleNode and not have to 
                // use an iterator to get a single node.
 
                // We use XmlConvert.ToString to turn the Guid into a string because
                // that's what is used by the Annotation's serialization methods.
                XPathNodeIterator iterator = tempNavigator.Select(@"//" + AnnotationXmlConstants.Prefixes.CoreSchemaPrefix + @":Annotation[@Id=""" + XmlConvert.ToString(id) + @"""]", _namespaceManager);
                if (iterator.MoveNext()) 
                {
                    navigator = (XPathNavigator)iterator.Current; 
                } 
            }
 
            return navigator;
        }

        ///  
        ///    Verifies the store is in a valid state.  Throws exceptions otherwise.
        ///  
        private void CheckStatus() 
        {
            lock (SyncRoot) 
            {
                if (IsDisposed)
                    throw new ObjectDisposedException(null, SR.Get(SRID.ObjectDisposed_StoreClosed));
 
                if (_stream == null)
                    throw new InvalidOperationException(SR.Get(SRID.StreamNotSet)); 
            } 
        }
 
        /// 
        /// Called from flush to serialize all annotations in the map
        /// notice that delete takes care of the delete action both in the map
        /// and in the store 
        /// 
        private void SerializeAnnotations() 
        { 
            List mapAnnotations = _storeAnnotationsMap.FindDirtyAnnotations();
            foreach (Annotation annotation in mapAnnotations) 
            {
                XPathNavigator editor = GetAnnotationNodeForId(annotation.Id);
                if (editor == null)
                { 
                    editor = (XPathNavigator)_rootNavigator.CreateNavigator();
                    XmlWriter writer = editor.AppendChild(); 
                    _serializer.Serialize(writer, annotation); 
                    writer.Close();
                } 
                else
                {
                    XmlWriter writer = editor.InsertBefore();
                    _serializer.Serialize(writer, annotation); 
                    writer.Close();
                    editor.DeleteSelf(); 
                } 
            }
            _storeAnnotationsMap.ValidateDirtyAnnotations(); 
        }

        /// 
        ///    Discards changes and cleans up references. 
        /// 
        private void Cleanup() 
        { 
            lock (SyncRoot)
            { 
                _xmlCompatibilityReader = null;
                _ignoredNamespaces = null;
                _stream = null;
                _document = null; 
                _rootNavigator = null;
                _storeAnnotationsMap = null; 
            } 
        }
 
        /// 
        ///     Sets the stream for this store.  Assumes Cleanup has
        ///     been previously called (or this is a new store).
        ///  
        /// new stream for this store
        /// List of known and compatible namespaces used to initialize 
        /// the XmlCompatibilityReader 
        private void SetStream(Stream stream, IDictionary> knownNamespaces)
        { 
            try
            {
                lock (SyncRoot)
                { 
                    _storeAnnotationsMap = new StoreAnnotationsMap(HandleAuthorChanged, HandleAnchorChanged, HandleCargoChanged);
                    _stream = stream; 
                    LoadStream(knownNamespaces); 
                }
            } 
            catch
            {
                Cleanup();
                throw; 
            }
        } 
 
        #endregion Private Methods
 
        //------------------------------------------------------
        //
        //  Private Fields
        // 
        //-----------------------------------------------------
 
        #region Private Fields 

        // Dirty bit for the store's in-memory cache 
        private bool _dirty;
        // Boolean flag represents whether the store should perform a flush
        // after every operation or not
        private bool _autoFlush; 
        // XmlDocument used as an in-memory cache of the stream's XML content
        private XmlDocument _document; 
        // Namespace manager used for all queries. 
        private XmlNamespaceManager _namespaceManager;
        // Stream passed in by the creator of this instance. 
        private Stream _stream;
        // The xpath navigator used to navigate the annotations Xml stream
        private XPathNavigator _rootNavigator;
        // map that holds AnnotationId->Annotation 
        StoreAnnotationsMap _storeAnnotationsMap;
        //list of ignored namespaces during XmlLoad 
        List _ignoredNamespaces = new List(); 

        //XmlCompatibilityReader - we need to hold that one open, so the underlying stream stays open too 
        // if the store is disposed the reader will be disposed and the stream will be closed too.
        XmlCompatibilityReader _xmlCompatibilityReader;

        /// 
        ///Static fields
        /// 
 
        //predefined namespaces
        private static readonly Dictionary> _predefinedNamespaces; 
        // Serializer for Annotations
        private static readonly Serializer _serializer = new Serializer(typeof(Annotation));

#endregion Private Fields 

    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------ 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//     An AnnotationStore subclass based on XML streams.  Processes XML 
//     streams and returns CAF 2.0 OM objects.  Useful to other
//     AnnotationStore subclass who can get an XML stream of their 
//     content.
//     Spec: http://team/sites/ag/Specifications/CAF%20Storage%20Spec.doc
//
// History: 
//  10/04/2002: rruiz:    Added header comment to XmlStreamStore.cs.
//  05/29/2003: LGolding: Ported to WCP tree. 
//  07/17/2003: rruiz:    Made this a real AnnotationStore subclass; updated with 
//                        new APIs; renamed class.
//  01/13/2004  ssimova:  Added revert events 
//  02/12/2004  ssimova:  Added locks for thread safety
//  08/18/2004: magedz:   Modify the add/delete/querying capabilities to use the map
//
//----------------------------------------------------------------------------- 
using System;
using System.Collections; 
using System.Collections.Generic; 
using System.Globalization;
using System.Diagnostics; 
using System.IO;
using System.Windows;
using System.Windows.Annotations;
using System.Xml; 
using System.Xml.Schema;
using System.Xml.XPath; 
using System.Xml.Serialization; 
using MS.Internal;
using MS.Internal.Annotations; 
using MS.Internal.Annotations.Storage;
using MS.Utility;
using System.Windows.Markup;
using MS.Internal.Controls.StickyNote; 
using System.Windows.Controls;
 
namespace System.Windows.Annotations.Storage 
{
    ///  
    ///     An AnnotationStore subclass based on XML streams.  Processes XML
    ///     streams and returns CAF 2.0 OM objects.  Useful to other
    ///     AnnotationStore subclass who can get an XML stream of their
    ///     content. 
    /// 
    public sealed class XmlStreamStore : AnnotationStore 
    { 
        //-----------------------------------------------------
        // 
        //  Constructors
        //
        //-----------------------------------------------------
 
        #region Constructors
 
        ///  
        /// This ctor initializes the Dictionary with predefined namespases
        /// and their compatibility 
        /// 
        static XmlStreamStore()
        {
            _predefinedNamespaces = new Dictionary>(6); 
            _predefinedNamespaces.Add(new Uri(AnnotationXmlConstants.Namespaces.CoreSchemaNamespace), null);
            _predefinedNamespaces.Add(new Uri(AnnotationXmlConstants.Namespaces.BaseSchemaNamespace), null); 
            _predefinedNamespaces.Add(new Uri(XamlReaderHelper.DefaultNamespaceURI), null); 
        }
 
        /// 
        ///     Creates an instance using the XML stream passed in as the
        ///     content. The XML in the stream must be valid XML and conform
        ///     to the CAF 2.0 schema. 
        /// 
        /// stream containing annotation data in XML format 
        /// stream is null 
        /// stream contains invalid XML
        public XmlStreamStore(Stream stream) 
            : base()
        {
            if (stream == null)
                throw new ArgumentNullException("stream"); 

            if (!stream.CanSeek) 
                throw new ArgumentException(SR.Get(SRID.StreamDoesNotSupportSeek)); 

            SetStream(stream, null); 
        }

        /// 
        ///     Creates an instance using the XML stream passed in as the 
        ///     content. The XML in the stream must be valid XML and conform
        ///     to the Annotations V1 schema or a valid future version XML which 
        ///     compatibility rules are that when applied they will produce 
        ///     a valid Annotations V1 XML. This .ctor allows registration of
        ///     application specific known namespaces. 
        /// 
        /// stream containing annotation data in XML format
        /// A dictionary with known and compatible namespaces. The keys in
        /// this dictionary are known namespaces. The value of each key is a list of namespaces that are compatible with 
        /// the key one, i.e. each of the namespaces in the value list will be transformed to the
        /// key namespace while reading the input XML. 
        /// stream is null 
        /// stream contains invalid XML
        /// duplicate namespace in knownNamespaces dictionary 
        /// null key in knownNamespaces dictionary
        public XmlStreamStore(Stream stream, IDictionary> knownNamespaces)
            : base()
        { 
            if (stream == null)
                throw new ArgumentNullException("stream"); 
 
            SetStream(stream, knownNamespaces);
        } 

        #endregion Constructors

        //------------------------------------------------------ 
        //
        //  Public Methods 
        // 
        //-----------------------------------------------------
 
        #region Public Methods

        /// 
        ///     Add a new annotation to this store.  The new annotation's Id 
        ///     is set to a new value.
        ///  
        /// the annotation to be added to the store 
        /// newAnnotation is null
        /// newAnnotation already exists in this store, as determined by its Id 
        /// if no stream has been set on the store
        /// if object has been Disposed
        public override void AddAnnotation(Annotation newAnnotation)
        { 
            if (newAnnotation == null)
                throw new ArgumentNullException("newAnnotation"); 
 
            // We are going to modify internal data. Lock the object
            // to avoid modifications from other threads 
            lock (SyncRoot)
            {

                //fire trace event 
                EventTrace.NormalTraceEvent(EventTraceGuidId.ADDANNOTATIONGUID, EventType.StartEvent);
 
                CheckStatus(); 

                XPathNavigator editor = GetAnnotationNodeForId(newAnnotation.Id); 

                // we are making sure that the newAnnotation doesn't already exist in the store
                if (editor != null)
                    throw new ArgumentException(SR.Get(SRID.AnnotationAlreadyExists), "newAnnotation"); 

                // we are making sure that the newAnnotation doesn't already exist in the store map 
                if (_storeAnnotationsMap.FindAnnotation(newAnnotation.Id) != null) 
                    throw new ArgumentException(SR.Get(SRID.AnnotationAlreadyExists), "newAnnotation");
 
                // simply add the annotation to the map to save on performance
                // notice that we need to tell the map that this instance of the annotation is dirty
                _storeAnnotationsMap.AddAnnotation(newAnnotation, true);
 
                //fire trace event
                EventTrace.NormalTraceEvent(EventTraceGuidId.ADDANNOTATIONGUID, EventType.EndEvent); 
 
            }
 
            OnStoreContentChanged(new StoreContentChangedEventArgs(StoreContentAction.Added, newAnnotation));
        }

        ///  
        ///     Delete the specified annotation.
        ///  
        /// the Id of the annotation to be deleted 
        /// the annotation that was deleted, or null if no annotation
        /// with the specified Id was found 
        /// if no stream has been set on the store
        /// if object has been disposed
        public override Annotation DeleteAnnotation(Guid annotationId)
        { 
            Annotation annotation = null;
 
            // We are now going to modify internal data. Lock the object 
            // to avoid modifications from other threads
            lock (SyncRoot) 
            {

                //fire trace event
                EventTrace.NormalTraceEvent(EventTraceGuidId.DELETEANNOTATIONGUID, EventType.StartEvent); 

                CheckStatus(); 
 
                annotation = _storeAnnotationsMap.FindAnnotation(annotationId);
 
                XPathNavigator editor = GetAnnotationNodeForId(annotationId);
                if (editor != null)
                {
                    // Only deserialize the annotation if its not already in our map 
                    if (annotation == null)
                    { 
                        annotation = (Annotation)_serializer.Deserialize(editor.ReadSubtree()); 
                    }
                    editor.DeleteSelf(); 
                }

                // Remove the instance from the map
                _storeAnnotationsMap.RemoveAnnotation(annotationId); 

                // notice that in Add we add the annotation to the map only 
                // but in delete we delete it from both to the Xml and the map 

                //fire trace event 
                EventTrace.NormalTraceEvent(EventTraceGuidId.DELETEANNOTATIONGUID, EventType.EndEvent);

            }
 
            // Only fire notification if we actually removed an annotation
            if (annotation != null) 
            { 
                OnStoreContentChanged(new StoreContentChangedEventArgs(StoreContentAction.Deleted, annotation));
            } 

            return annotation;
        }
 
        /// 
        ///     Queries the Xml stream for annotations that have an anchor 
        ///     that contains a locator that begins with the locator parts 
        ///     in anchorLocator.
        ///  
        /// the locator we are looking for
        /// 
        ///    A list of annotations that have locators in their anchors
        ///    starting with the same locator parts list as of the input locator 
        ///    If no such annotations an empty list will be returned. The method
        ///    never returns null. 
        ///  
        /// if object has been Disposed
        /// the stream is null 
        public override IList GetAnnotations(ContentLocator anchorLocator)
        {
            // First we generate the XPath expression
            if (anchorLocator == null) 
                throw new ArgumentNullException("anchorLocator");
 
            if (anchorLocator.Parts == null) 
                throw new ArgumentNullException("anchorLocator.Parts");
 
            //fire trace event
            EventTrace.NormalTraceEvent(EventTraceGuidId.GETANNOTATIONBYLOCGUID, EventType.StartEvent);

            string query = @"//" + AnnotationXmlConstants.Prefixes.CoreSchemaPrefix + ":" + AnnotationXmlConstants.Elements.ContentLocator; 

            if (anchorLocator.Parts.Count > 0) 
            { 
                query += @"/child::*[1]/self::";
                for (int i = 0; i < anchorLocator.Parts.Count; i++) 
                {
                    if (anchorLocator.Parts[i] != null)
                    {
                        if (i > 0) 
                        {
                            query += @"/following-sibling::"; 
                        } 

                        string fragment = anchorLocator.Parts[i].GetQueryFragment(_namespaceManager); 

                        if (fragment != null)
                        {
                            query += fragment; 
                        }
                        else 
                        { 
                            query += "*";
                        } 
                    }
                }
            }
 
            query += @"/ancestor::" + AnnotationXmlConstants.Prefixes.CoreSchemaPrefix + ":Anchors/ancestor::" + AnnotationXmlConstants.Prefixes.CoreSchemaPrefix + ":Annotation";
 
 
            //fire trace event
            EventTrace.NormalTraceEvent(EventTraceGuidId.GETANNOTATIONBYLOCGUID, EventType.EndEvent); 

            return InternalGetAnnotations(query, anchorLocator);
        }
 
        /// 
        /// Returns a list of all annotations in the store 
        ///  
        /// annotations list. Can return an empty list, but never null.
        /// if object has been disposed 
        public override IList GetAnnotations()
        {

            //fire trace event 
            EventTrace.NormalTraceEvent(EventTraceGuidId.GETANNOTATIONSGUID, EventType.StartEvent);
 
            string query = "//" + AnnotationXmlConstants.Prefixes.CoreSchemaPrefix + ":Annotation"; 
            return InternalGetAnnotations(query, null);
 
            //fire trace event
            // unreachable - previous statement is 'return' (bug 1788240)
            #pragma warning disable 0162
            EventTrace.NormalTraceEvent(EventTraceGuidId.GETANNOTATIONSGUID, EventType.EndEvent); 
            #pragma warning restore 0162
 
        } 

        ///  
        /// Finds annotation by Id
        /// 
        /// annotation id
        /// The annotation. Null if the annotation does not exists 
        /// if object has been disposed
        public override Annotation GetAnnotation(Guid annotationId) 
        { 
            lock (SyncRoot)
            { 

                //fire trace event
                EventTrace.NormalTraceEvent(EventTraceGuidId.GETANNOTATIONBYIDGUID, EventType.StartEvent);
 
                CheckStatus();
 
                Annotation annotation = _storeAnnotationsMap.FindAnnotation(annotationId); 

                // If there is no pre-existing instance, we deserialize and create an instance. 
                if (annotation != null)
                {
                    return annotation;
                } 

                XPathNavigator editor = GetAnnotationNodeForId(annotationId); 
                if (editor != null) 
                {
                    annotation = (Annotation)_serializer.Deserialize(editor.ReadSubtree()); 

                    // Add the new instance to the map
                    _storeAnnotationsMap.AddAnnotation(annotation, false);
                } 

 
                //fire trace event 
                EventTrace.NormalTraceEvent(EventTraceGuidId.GETANNOTATIONBYIDGUID, EventType.EndEvent);
 
                return annotation;
            }
        }
 
        /// 
        ///     Causes any buffered data to be written to the underlying 
        ///     storage mechanism.  Gets called after each operation if 
        ///     AutoFlush is set to true.  The stream is truncated to
        ///     the length of the data written out by the store. 
        /// 
        /// stream cannot be written to
        /// if no stream has been set on the store
        /// if object has been disposed 
        /// 
        public override void Flush() 
        { 
            lock (SyncRoot)
            { 
                CheckStatus();
                if (!_stream.CanWrite)
                {
                    throw new UnauthorizedAccessException(SR.Get(SRID.StreamCannotBeWritten)); 
                }
 
                if (_dirty) 
                {
                    SerializeAnnotations(); 
                    _stream.Position = 0;
                    _stream.SetLength(0);
                    _document.PreserveWhitespace = true;
                    _document.Save(_stream); 
                    _stream.Flush();
                    _dirty = false; 
                } 
            }
        } 

        /// 
        /// Returns a list of namespaces that are compatible with an  input namespace
        ///  
        /// namespace
        /// a list of compatible namespaces. Can be null 
        /// This method works only with built-in AnnotationFramework namespaces. 
        /// For any other input namespace the return value will be null even if it is
        /// registered with the XmlStreamStore ctor 
        public static IList GetWellKnownCompatibleNamespaces(Uri name)
        {
            if (name == null)
            { 
                throw new ArgumentNullException("name");
            } 
            if (_predefinedNamespaces.ContainsKey(name)) 
                return _predefinedNamespaces[name];
            return null; 
        }

        #endregion Public Methods
 
        //------------------------------------------------------
        // 
        //  Public Operators 
        //
        //------------------------------------------------------ 
        //-----------------------------------------------------
        //
        //  Public Events
        // 
        //------------------------------------------------------
        //----------------------------------------------------- 
        // 
        //  Public Properties
        // 
        //-----------------------------------------------------

        #region Public Properties
 
        /// 
        ///     When set to true an implementation should call Flush() 
        ///     as a side-effect after each operation. 
        /// 
        ///  
        ///     true if the implementation is set to call Flush() after
        ///     each operation; false otherwise
        /// 
        public override bool AutoFlush 
        {
            get 
            { 
                lock (SyncRoot)
                { 
                    return _autoFlush;
                }
            }
            set 
            {
                lock (SyncRoot) 
                { 
                    _autoFlush = value;
 
                    // Commit anything that needs to be committed up to this point
                    if (_autoFlush)
                    {
                        Flush(); 
                    }
                } 
            } 
        }
 
        /// 
        /// Returns a list of the namespaces that are ignored while loading
        /// the Xml stream
        ///  
        /// The value is never null, but can be an empty list if nothing has been ignored
        public IList IgnoredNamespaces 
        { 
            get
            { 
                return _ignoredNamespaces;
            }
        }
 
        /// 
        /// Returns a list of all namespaces that are internaly used by the framework 
        ///  
        public static IList WellKnownNamespaces
        { 
            get
            {
                Uri[] res = new Uri[_predefinedNamespaces.Keys.Count];
                _predefinedNamespaces.Keys.CopyTo(res, 0); 
                return res;
            } 
        } 

 
        #endregion Public Properties

        //-----------------------------------------------------
        // 
        //  Protected Methods
        // 
        //------------------------------------------------------ 

        #region Protected Methods 

        /// 
        ///     Disposes of the resources (other than memory) used by the store.
        ///  
        /// true to release both managed and unmanaged
        /// resources; false to release only unmanaged resources 
        protected override void Dispose(bool disposing) 
        {
            //call the base class first to set _disposed to false 
            //in order to avoid working with the store when the resources
            //are released
            base.Dispose(disposing);
 
            if (disposing)
            { 
                Cleanup(); 
            }
        } 

        /// 
        ///     Called after every annotation action on the store.  We override it
        ///     to update the dirty state of the store. 
        /// 
        /// arguments for the event to fire 
        protected override void OnStoreContentChanged(StoreContentChangedEventArgs e) 
        {
            lock (SyncRoot) 
            {
                _dirty = true;
            }
 
            base.OnStoreContentChanged(e);
        } 
 
        #endregion Protected Methods
 
        //-----------------------------------------------------
        //
        //  Private Methods
        // 
        //------------------------------------------------------
 
        #region Private Methods 

        ///  
        ///     Applies the specified XPath expression to the store and returns
        ///     the results.
        /// 
        /// the XPath expression to be applied to the store 
        /// 
        ///     An IList containing zero or more annotations that match the 
        ///     criteria in the XPath expression; never will return null.  If 
        ///     no annotations meet the criteria, an empty list is returned.
        ///  
        /// if no stream has been set on the store
        /// if object has been Disposed
        /// queryExpression is null
        /// queryExpression is empty string 
        private List FindAnnotationIds(string queryExpression)
        { 
            Invariant.Assert(queryExpression != null && queryExpression.Length > 0, 
                          "Invalid query expression");
 
            Guid annId;
            List retObj = null;

            // Lock the object so nobody can change the document 
            // while the query is executed
            lock (SyncRoot) 
            { 
                CheckStatus();
 
                XPathNavigator navigator = _document.CreateNavigator();
                XPathNodeIterator iterator = navigator.Select(queryExpression, _namespaceManager);

                if (iterator != null && iterator.Count > 0) 
                {
                    retObj = new List(iterator.Count); 
                    foreach (XPathNavigator node in iterator) 
                    {
                        string nodeId = node.GetAttribute("Id", ""); 
                        if (String.IsNullOrEmpty(nodeId))
                        {
                            throw new XmlException(SR.Get(SRID.RequiredAttributeMissing, AnnotationXmlConstants.Attributes.Id, AnnotationXmlConstants.Elements.Annotation));
                        } 

                        try 
                        { 
                            annId = XmlConvert.ToGuid(nodeId);
                        } 
                        catch (FormatException fe)
                        {
                            throw new InvalidOperationException(SR.Get(SRID.CannotParseId), fe);
                        } 

                        retObj.Add(annId); 
                    } 
                }
                else 
                {
                    retObj = new List(0);
                }
            } 

            return retObj; 
        } 

        ///  
        ///     Used as AuthorChanged event handler for all annotations
        ///     handed out by the map.
        /// 
        /// annotation that sent the event 
        /// args for the event
        private void HandleAuthorChanged(object sender, AnnotationAuthorChangedEventArgs e) 
        { 
            lock (SyncRoot)
            { 
                _dirty = true;
            }

            OnAuthorChanged(e); 
        }
 
        ///  
        ///     Used as AnchorChanged event handler for all annotations
        ///     handed out by the map. 
        /// 
        /// annotation that sent the event
        /// args for the event
        private void HandleAnchorChanged(object sender, AnnotationResourceChangedEventArgs e) 
        {
            lock (SyncRoot) 
            { 
                _dirty = true;
            } 

            OnAnchorChanged(e);
        }
 
        /// 
        ///     Used as CargoChanged event handler for all annotations 
        ///     handed out by the map. 
        /// 
        /// annotation that sent the event 
        /// args for the event
        private void HandleCargoChanged(object sender, AnnotationResourceChangedEventArgs e)
        {
            lock (SyncRoot) 
            {
                _dirty = true; 
            } 

            OnCargoChanged(e); 
        }

        /// 
        /// 1- Merge Annotations queried from both the map and the Xml stream 
        /// 2- Add the annotations found in the Xml stream to the map
        ///  
        /// A dictionary of map annotations 
        /// A list of annotation ids from the Xml stream
        ///  
        private IList MergeAndCacheAnnotations(Dictionary mapAnnotations, List storeAnnotationsId)
        {
            // first put all annotations from the map in the return list
            List annotations = new List((IEnumerable)mapAnnotations.Values); 

            // there three possible conditions 
            // 1- An annotation exists in xml and in the store map results 
            // 2- An annotation exists in xml and not in the store map results
            //      2-1- The annotation is found in the map 
            //      2-2- The annotation is not found in the map

            // Now, we need to find annotations in the store that are not in the map results
            // and verify that they should be serialized 
            foreach (Guid annotationId in storeAnnotationsId)
            { 
                Annotation annot; 
                bool foundInMapResults = mapAnnotations.TryGetValue(annotationId, out annot);
                if (!foundInMapResults) 
                {
                    // it is not in the map - get it from the store
                    annot = GetAnnotation(annotationId);
                    annotations.Add(annot); 
                }
            } 
 
            return annotations;
        } 

        /// 
        /// Do the GetAnnotations work inside a lock statement for thread safety reasons
        ///  
        /// 
        ///  
        ///  
        private IList InternalGetAnnotations(string query, ContentLocator anchorLocator)
        { 
            // anchorLocator being null is handled appropriately below
            Invariant.Assert(query != null, "Parameter 'query' is null.");

            lock (SyncRoot) 
            {
                CheckStatus(); 
 
                List annotationIds = FindAnnotationIds(query);
                Dictionary annotations = null; 

                // Now, get the annotations in the map that satisfies the query criterion
                if (anchorLocator == null)
                { 
                    annotations = _storeAnnotationsMap.FindAnnotations();
                } 
                else 
                {
                    annotations = _storeAnnotationsMap.FindAnnotations(anchorLocator); 
                }

                // merge both query results
                return MergeAndCacheAnnotations(annotations, annotationIds); 
            }
        } 
 

        ///  
        ///     Loads the current stream into the XmlDocument used by this
        ///     store as a backing store.  If the stream doesn't contain any
        ///     data we load up a default XmlDocument for the Annotations schema.
        ///     The "http://schemas.microsoft.com/windows/annotations/2003/11/core" 
        ///     namespace is registered with "anc" prefix and the
        ///    "http://schemas.microsoft.com/windows/annotations/2003/11/base" namespace 
        ///     is registered with "anb" prefix as global namespaces. 
        ///     We also select from the newly loaded document the new top
        ///     level node.  This is used later for insertions, etc. 
        /// 
        /// if the stream contains invalid XML
        private void LoadStream(IDictionary> knownNamespaces)
        { 
            //check input data first
            CheckKnownNamespaces(knownNamespaces); 
 
            lock (SyncRoot)
            { 
                _document = new XmlDocument();
                _document.PreserveWhitespace = false;
                if (_stream.Length == 0)
                { 
                    _document.LoadXml(" <" +
                        AnnotationXmlConstants.Prefixes.CoreSchemaPrefix + ":Annotations xmlns:" + AnnotationXmlConstants.Prefixes.CoreSchemaPrefix + "=\"" + 
                        AnnotationXmlConstants.Namespaces.CoreSchemaNamespace + "\" xmlns:" + AnnotationXmlConstants.Prefixes.BaseSchemaPrefix + "=\"" + AnnotationXmlConstants.Namespaces.BaseSchemaNamespace + "\" />"); 
                }
                else 
                {
                    _xmlCompatibilityReader = SetupReader(knownNamespaces);
                    _document.Load(_xmlCompatibilityReader);
                } 

                _namespaceManager = new XmlNamespaceManager(_document.NameTable); 
                _namespaceManager.AddNamespace(AnnotationXmlConstants.Prefixes.CoreSchemaPrefix, AnnotationXmlConstants.Namespaces.CoreSchemaNamespace); 
                _namespaceManager.AddNamespace(AnnotationXmlConstants.Prefixes.BaseSchemaPrefix, AnnotationXmlConstants.Namespaces.BaseSchemaNamespace);
 
                // This use of an iterator is necessary because SelectSingleNode isn't available in the PD3
                // drop of the CLR.  Eventually we should be able to call SelectSingleNode and not have to
                // use an iterator to get a single node.
 
                XPathNavigator navigator = _document.CreateNavigator();
                XPathNodeIterator iterator = navigator.Select("//" + AnnotationXmlConstants.Prefixes.CoreSchemaPrefix + ":Annotations", _namespaceManager); 
                Invariant.Assert(iterator.Count == 1, "More than one annotation returned for the query"); 

                iterator.MoveNext(); 
                _rootNavigator = (XPathNavigator)iterator.Current;
            }
        }
 
        /// 
        /// Checks if passed external known namespaces are valid 
        ///  
        /// namespaces dictionary
        /// We do not allow internal namespases in this dictionary nor 
        /// duplicates
        private void CheckKnownNamespaces(IDictionary> knownNamespaces)
        {
            if (knownNamespaces == null) 
                return;
 
            IList allNamespaces = new List(); 

            //add AnnotationFramework namespaces 
            foreach (Uri name in _predefinedNamespaces.Keys)
            {
                allNamespaces.Add(name);
            } 

            //add external namespaces 
            foreach (Uri knownNamespace in knownNamespaces.Keys) 
            {
                if (knownNamespace == null) 
                {
                    throw new ArgumentException(SR.Get(SRID.NullUri), "knownNamespaces");
                }
                if (allNamespaces.Contains(knownNamespace)) 
                {
                    throw new ArgumentException(SR.Get(SRID.DuplicatedUri), "knownNamespaces"); 
                } 
                allNamespaces.Add(knownNamespace);
            } 

            // check compatible namespaces
            foreach (KeyValuePair> item in knownNamespaces)
            { 
                if (item.Value != null)
                { 
                    foreach (Uri name in item.Value) 
                    {
                        if (name == null) 
                        {
                            throw new ArgumentException(SR.Get(SRID.NullUri), "knownNamespaces");
                        }
 
                        if (allNamespaces.Contains(name))
                        { 
                            throw new ArgumentException(SR.Get(SRID.DuplicatedCompatibleUri), "knownNamespaces"); 
                        }
                        allNamespaces.Add(name); 
                    }//foreach
                }//if

            }//foreach 

        } 
 
        /// 
        /// Creates and initializes the XmlCompatibilityReader 
        /// 
        /// Dictionary of external known namespaces
        /// The XmlCompatibilityReader
        private XmlCompatibilityReader SetupReader(IDictionary> knownNamespaces) 
        {
            IList supportedNamespaces = new List(); 
 
            //add AnnotationFramework namespaces
            foreach (Uri name in _predefinedNamespaces.Keys) 
            {
                supportedNamespaces.Add(name.ToString());
            }
 
            //add external namespaces
            if (knownNamespaces != null) 
            { 
                foreach (Uri knownNamespace in knownNamespaces.Keys)
                { 
                    Debug.Assert(knownNamespace != null, "null knownNamespace");
                    supportedNamespaces.Add(knownNamespace.ToString());
                }
            } 

            //create XmlCompatibilityReader first 
            XmlCompatibilityReader reader = new XmlCompatibilityReader(new XmlTextReader(_stream), 
            new IsXmlNamespaceSupportedCallback(IsXmlNamespaceSupported), supportedNamespaces);
 
            // Declare compatibility.
            // Skip the Framework ones because they are all null in this version
            if (knownNamespaces != null)
            { 
                foreach (KeyValuePair> item in knownNamespaces)
                { 
                    if (item.Value != null) 
                    {
                        foreach (Uri name in item.Value) 
                        {
                            Debug.Assert(name != null, "null compatible namespace");
                            reader.DeclareNamespaceCompatibility(item.Key.ToString(), name.ToString());
                        }//foreach 
                    }//if
 
                }//foreach 

            }//if 

            //cleanup the _ignoredNamespaces
            _ignoredNamespaces.Clear();
            return reader; 

        } 
 
        /// 
        /// A callback for XmlCompatibilityReader 
        /// 
        /// inquired namespace
        /// the newer subsumming namespace
        /// true if the namespace is known, false if not 
        /// This API always returns false because all known namespaces are registered
        /// before loading the Xml. It stores the namespace as ignored. 
        private bool IsXmlNamespaceSupported(string xmlNamespace, out string newXmlNamespace) 
        {
 
            //store the namespace if needed
            if (!String.IsNullOrEmpty(xmlNamespace))
            {
                if (!Uri.IsWellFormedUriString(xmlNamespace, UriKind.RelativeOrAbsolute)) 
                {
                    throw new ArgumentException(SR.Get(SRID.InvalidNamespace, xmlNamespace), "xmlNamespace"); 
                } 
                Uri namespaceUri = new Uri(xmlNamespace, UriKind.RelativeOrAbsolute);
                if (!_ignoredNamespaces.Contains(namespaceUri)) 
                    _ignoredNamespaces.Add(namespaceUri);
            }

            newXmlNamespace = null; 
            return false;
        } 
 

 
        /// 
        ///     Selects the annotation XmlElement from the current document
        ///     with the given id.
        ///  
        /// the id to query for in the XmlDocument
        /// the XmlElement representing the annotation with the given id 
        private XPathNavigator GetAnnotationNodeForId(Guid id) 
        {
            XPathNavigator navigator = null; 

            lock (SyncRoot)
            {
                XPathNavigator tempNavigator = _document.CreateNavigator(); 

                // This use of an iterator is necessary because SelectSingleNode isn't available in the PD3 
                // drop of the CLR.  Eventually we should be able to call SelectSingleNode and not have to 
                // use an iterator to get a single node.
 
                // We use XmlConvert.ToString to turn the Guid into a string because
                // that's what is used by the Annotation's serialization methods.
                XPathNodeIterator iterator = tempNavigator.Select(@"//" + AnnotationXmlConstants.Prefixes.CoreSchemaPrefix + @":Annotation[@Id=""" + XmlConvert.ToString(id) + @"""]", _namespaceManager);
                if (iterator.MoveNext()) 
                {
                    navigator = (XPathNavigator)iterator.Current; 
                } 
            }
 
            return navigator;
        }

        ///  
        ///    Verifies the store is in a valid state.  Throws exceptions otherwise.
        ///  
        private void CheckStatus() 
        {
            lock (SyncRoot) 
            {
                if (IsDisposed)
                    throw new ObjectDisposedException(null, SR.Get(SRID.ObjectDisposed_StoreClosed));
 
                if (_stream == null)
                    throw new InvalidOperationException(SR.Get(SRID.StreamNotSet)); 
            } 
        }
 
        /// 
        /// Called from flush to serialize all annotations in the map
        /// notice that delete takes care of the delete action both in the map
        /// and in the store 
        /// 
        private void SerializeAnnotations() 
        { 
            List mapAnnotations = _storeAnnotationsMap.FindDirtyAnnotations();
            foreach (Annotation annotation in mapAnnotations) 
            {
                XPathNavigator editor = GetAnnotationNodeForId(annotation.Id);
                if (editor == null)
                { 
                    editor = (XPathNavigator)_rootNavigator.CreateNavigator();
                    XmlWriter writer = editor.AppendChild(); 
                    _serializer.Serialize(writer, annotation); 
                    writer.Close();
                } 
                else
                {
                    XmlWriter writer = editor.InsertBefore();
                    _serializer.Serialize(writer, annotation); 
                    writer.Close();
                    editor.DeleteSelf(); 
                } 
            }
            _storeAnnotationsMap.ValidateDirtyAnnotations(); 
        }

        /// 
        ///    Discards changes and cleans up references. 
        /// 
        private void Cleanup() 
        { 
            lock (SyncRoot)
            { 
                _xmlCompatibilityReader = null;
                _ignoredNamespaces = null;
                _stream = null;
                _document = null; 
                _rootNavigator = null;
                _storeAnnotationsMap = null; 
            } 
        }
 
        /// 
        ///     Sets the stream for this store.  Assumes Cleanup has
        ///     been previously called (or this is a new store).
        ///  
        /// new stream for this store
        /// List of known and compatible namespaces used to initialize 
        /// the XmlCompatibilityReader 
        private void SetStream(Stream stream, IDictionary> knownNamespaces)
        { 
            try
            {
                lock (SyncRoot)
                { 
                    _storeAnnotationsMap = new StoreAnnotationsMap(HandleAuthorChanged, HandleAnchorChanged, HandleCargoChanged);
                    _stream = stream; 
                    LoadStream(knownNamespaces); 
                }
            } 
            catch
            {
                Cleanup();
                throw; 
            }
        } 
 
        #endregion Private Methods
 
        //------------------------------------------------------
        //
        //  Private Fields
        // 
        //-----------------------------------------------------
 
        #region Private Fields 

        // Dirty bit for the store's in-memory cache 
        private bool _dirty;
        // Boolean flag represents whether the store should perform a flush
        // after every operation or not
        private bool _autoFlush; 
        // XmlDocument used as an in-memory cache of the stream's XML content
        private XmlDocument _document; 
        // Namespace manager used for all queries. 
        private XmlNamespaceManager _namespaceManager;
        // Stream passed in by the creator of this instance. 
        private Stream _stream;
        // The xpath navigator used to navigate the annotations Xml stream
        private XPathNavigator _rootNavigator;
        // map that holds AnnotationId->Annotation 
        StoreAnnotationsMap _storeAnnotationsMap;
        //list of ignored namespaces during XmlLoad 
        List _ignoredNamespaces = new List(); 

        //XmlCompatibilityReader - we need to hold that one open, so the underlying stream stays open too 
        // if the store is disposed the reader will be disposed and the stream will be closed too.
        XmlCompatibilityReader _xmlCompatibilityReader;

        /// 
        ///Static fields
        /// 
 
        //predefined namespaces
        private static readonly Dictionary> _predefinedNamespaces; 
        // Serializer for Annotations
        private static readonly Serializer _serializer = new Serializer(typeof(Annotation));

#endregion Private Fields 

    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK