XmlStreamStore.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / System / Windows / Annotations / Storage / XmlStreamStore.cs / 1305600 / 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.EasyTraceEvent(EventTrace.Keyword.KeywordAnnotation, EventTrace.Event.AddAnnotationBegin);
                try 
                { 
                    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); 
                }
                finally 
                { 
                    //fire trace event
                    EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordAnnotation, EventTrace.Event.AddAnnotationEnd); 
                }

            }
 
            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.EasyTraceEvent(EventTrace.Keyword.KeywordAnnotation, EventTrace.Event.DeleteAnnotationBegin);
 
                try
                {
                    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 

                } 
                finally 
                {
                    //fire trace event 
                    EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordAnnotation, EventTrace.Event.DeleteAnnotationEnd);
                }
            }
 
            // 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.EasyTraceEvent(EventTrace.Keyword.KeywordAnnotation, EventTrace.Event.GetAnnotationByLocBegin);
            IList annotations = null;
            try 
            {
                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"; 

                annotations = InternalGetAnnotations(query, anchorLocator); 
            }
            finally
            {
                //fire trace event 
                EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordAnnotation, EventTrace.Event.GetAnnotationByLocEnd);
            } 
 
            return annotations;
        } 

        /// 
        /// 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() 
        {
            IList annotations = null; 
            //fire trace event
            EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordAnnotation, EventTrace.Event.GetAnnotationsBegin);
            try
            { 

                string query = "//" + AnnotationXmlConstants.Prefixes.CoreSchemaPrefix + ":Annotation"; 
 
                annotations = InternalGetAnnotations(query, null);
            } 
            finally
            {
                EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordAnnotation, EventTrace.Event.GetAnnotationsEnd);
            } 
            return annotations;
 
        } 

        ///  
        /// 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)
            { 
                Annotation annotation = null;
                //fire trace event
                EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordAnnotation, EventTrace.Event.GetAnnotationByIdBegin);
                try 
                {
                    CheckStatus(); 
 
                    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); 
                    }
                } 
                finally 
                {
                    //fire trace event 
                    EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordAnnotation, EventTrace.Event.GetAnnotationByIdEnd);
                }
                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.EasyTraceEvent(EventTrace.Keyword.KeywordAnnotation, EventTrace.Event.AddAnnotationBegin);
                try 
                { 
                    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); 
                }
                finally 
                { 
                    //fire trace event
                    EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordAnnotation, EventTrace.Event.AddAnnotationEnd); 
                }

            }
 
            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.EasyTraceEvent(EventTrace.Keyword.KeywordAnnotation, EventTrace.Event.DeleteAnnotationBegin);
 
                try
                {
                    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 

                } 
                finally 
                {
                    //fire trace event 
                    EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordAnnotation, EventTrace.Event.DeleteAnnotationEnd);
                }
            }
 
            // 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.EasyTraceEvent(EventTrace.Keyword.KeywordAnnotation, EventTrace.Event.GetAnnotationByLocBegin);
            IList annotations = null;
            try 
            {
                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"; 

                annotations = InternalGetAnnotations(query, anchorLocator); 
            }
            finally
            {
                //fire trace event 
                EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordAnnotation, EventTrace.Event.GetAnnotationByLocEnd);
            } 
 
            return annotations;
        } 

        /// 
        /// 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() 
        {
            IList annotations = null; 
            //fire trace event
            EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordAnnotation, EventTrace.Event.GetAnnotationsBegin);
            try
            { 

                string query = "//" + AnnotationXmlConstants.Prefixes.CoreSchemaPrefix + ":Annotation"; 
 
                annotations = InternalGetAnnotations(query, null);
            } 
            finally
            {
                EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordAnnotation, EventTrace.Event.GetAnnotationsEnd);
            } 
            return annotations;
 
        } 

        ///  
        /// 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)
            { 
                Annotation annotation = null;
                //fire trace event
                EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordAnnotation, EventTrace.Event.GetAnnotationByIdBegin);
                try 
                {
                    CheckStatus(); 
 
                    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); 
                    }
                } 
                finally 
                {
                    //fire trace event 
                    EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordAnnotation, EventTrace.Event.GetAnnotationByIdEnd);
                }
                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