ValueTable.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Framework / MS / Internal / Data / ValueTable.cs / 1 / ValueTable.cs

                            //---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: Mapping of (item, property accessor) to value 
// 
//---------------------------------------------------------------------------
 
/***************************************************************************\
    Some properties in the world behave poorly, in the sense that two
    successive GetValue calls return different values (i.e. the property
    "changes" every time you call it).  To work around this problem, we 
    cache the value the first time we retrieve it, and interpose a cache
    lookup in front of every GetValue.  The ValueTable class implements this 
    cache;  there is one such table per DataBindEngine (hence one per Dispatcher). 

    The malfeasant properties seem to be isolated to ADO-related classes in 
    System.Data.  Lacking an explicit list of all such properties, or a rule
    for identifying them, we use a heuristic that covers all the known cases.
    See the ShouldCache method.
\***************************************************************************/ 

using System; 
using System.Collections; 
using System.Collections.Generic;           // IEnumerable
using System.Collections.Specialized;       // HybridDictionary 
using System.ComponentModel;                // IBindingList
using System.Reflection;                    // TypeDescriptor
using System.Windows;                       // SR
using MS.Internal;                          // Invariant.Assert 

namespace MS.Internal.Data 
{ 
    internal sealed class ValueTable : IWeakEventListener
    { 
        // should we cache the value of the given property from the given item?
        internal static bool ShouldCache(object item, PropertyDescriptor pd)
        {
            // custom property descriptors returning IBindingList (bug 1190076) 
            if (item is ICustomTypeDescriptor && pd.PropertyType == typeof(IBindingList))
            { 
                // according to SQL dev, the problem only arises for certain 
                // subclasses of PropertyDescriptor (bug 1285603)
                string name = pd.GetType().Name; 
                if (name == "DataTablePropertyDescriptor" ||
                    name == "DataRelationPropertyDescriptor")
                {
                    return true; 
                }
            } 
 
            // XLinq's property descriptors for the Elements and Descendants properties
            if (IsXLinqCollectionProperty(pd)) 
            {
                return true;
            }
 
            return false;       // everything else is treated normally
        } 
 
        // XLinq exposes two synthetic properties - Elements and Descendants -
        // on XElement that return IEnumerable.  We handle these specially 
        // to work around problems involving identity and change notifications
        internal static bool IsXLinqCollectionProperty(PropertyDescriptor pd)
        {
            // Both properties return IEnumerable (possibly changing 
            // to IList).  Use this to eliminate other properties cheaply.
            Type propertyType = pd.PropertyType; 
            if (!propertyType.IsGenericType) 
                return false;
 
            Type propertyTypeDefinition = propertyType.GetGenericTypeDefinition();
            if (propertyTypeDefinition != typeof(IEnumerable<>) && propertyTypeDefinition != typeof(IList<>))
                return false;
 
            Type descriptorType = pd.GetType();
            if (descriptorType.Namespace != "System.Xml.Linq.ComponentModel") 
                return false; 

            string name = descriptorType.Name; 
            return (name == "XElementElementsPropertyDescriptor" ||
                    name == "XElementDescendantsPropertyDescriptor");
        }
 
        // retrieve the value, using the cache if necessary
        internal object GetValue(object item, PropertyDescriptor pd, bool indexerIsNext) 
        { 
            if (!ShouldCache(item, pd))
            { 
                // normal case - just get the value the old-fashioned way
                return pd.GetValue(item);
            }
            else 
            {
                // lazy creation of the cache 
                if (_table == null) 
                {
                    _table = new HybridDictionary(); 
                }

                // look up the value in the cache
                bool isXLinqCollectionProperty = IsXLinqCollectionProperty(pd); 
                ValueTableKey key = new ValueTableKey(item, pd);
                object value = _table[key]; 
 
                // if there's no entry, fetch the value and cache it
                if (value == null) 
                {
                    if (isXLinqCollectionProperty)
                    {
                        // interpose our own value for special XLinq properties 
                        value = new XDeferredAxisSource(item, pd);
                    } 
                    else 
                    {
                        value = pd.GetValue(item); 
                    }

                    if (value == null)
                    { 
                        value = CachedNull;     // distinguish a null value from no entry
                    } 
 
                    _table[key] = value;
                } 

                // decode null, if necessary
                if (value == CachedNull)
                { 
                    value = null;
                } 
                else if (isXLinqCollectionProperty && !indexerIsNext) 
                {
                    // The XLinq properties need special help.  When the path 
                    // contains "Elements[Foo]", we should return the interposed
                    // XDeferredAxisSource;  the path worker will then call the XDAS's
                    // indexer with argument "Foo", and obtain the desired
                    // ObservableCollection.  But when the path contains "Elements" 
                    // with no indexer, we should return an ObservableCollection
                    // corresponding to the full set of children. 
                    // [All this applies to "Descendants" as well.] 
                    XDeferredAxisSource xdas = (XDeferredAxisSource)value;
                    value = xdas.FullCollection; 
                }

                return value;
            } 
        }
 
        // listen for changes to a property 
        internal void RegisterForChanges(object item, PropertyDescriptor pd, DataBindEngine engine)
        { 
            // lazy creation of the cache
            if (_table == null)
            {
                _table = new HybridDictionary(); 
            }
 
            ValueTableKey key = new ValueTableKey(item, pd); 
            object value = _table[key];
 
            if (value == null)
            {
                // new entry needed - add a listener
                INotifyPropertyChanged inpc = item as INotifyPropertyChanged; 
                if (inpc != null)
                { 
                    PropertyChangedEventManager.AddListener(inpc, this, pd.Name); 
                }
                else 
                {
                    ValueChangedEventManager.AddListener(item, this, pd);
                }
            } 
        }
 
        ///  
        /// Handle events from the centralized event table
        ///  
        bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
        {
            if (managerType == typeof(PropertyChangedEventManager))
            { 
                PropertyChangedEventArgs pce = (PropertyChangedEventArgs)e;
                InvalidateCache(sender, null, pce.PropertyName); 
            } 
            else if (managerType == typeof(ValueChangedEventManager))
            { 
                ValueChangedEventArgs vce = (ValueChangedEventArgs)e;
                InvalidateCache(sender, vce.PropertyDescriptor, null);
            }
            else 
            {
                return false;       // unrecognized event 
            } 

            return true; 
        }

        // invalidate (remove) a cache entry.  Called when the source raises a change event.
        void InvalidateCache(object item, PropertyDescriptor pd, string name) 
        {
            if (pd == null) 
            { 
                Invariant.Assert(!String.IsNullOrEmpty(name), "caller must supply either pd or name");
                // regenerate the descriptor from the name 
                // (this code matches PropertyPathWorker.GetInfo)
                if (item is ICustomTypeDescriptor)
                {
                    pd = TypeDescriptor.GetProperties(item)[name]; 
                }
                else 
                { 
                    pd = TypeDescriptor.GetProperties(item.GetType())[name];
                } 
            }

            // ignore changes to special XLinq PD's - leave our interposed object in the cache
            if (IsXLinqCollectionProperty(pd)) 
                return;
 
            ValueTableKey key = new ValueTableKey(item, pd); 
            _table.Remove(key);
        } 

        // remove stale entries from the table
        internal bool Purge()
        { 
            if (_table == null)
                return false; 
 
            // first see if there are any stale entries.  No sense allocating
            // storage if there's nothing to do. 
            bool isPurgeNeeded = false;
            ICollection keys = _table.Keys;
            foreach (ValueTableKey key in keys)
            { 
                if (key.IsStale)
                { 
                    isPurgeNeeded = true; 
                    break;
                } 
            }

            // if the purge is needed, copy the keys and purge the
            // stale entries.  The copy avoids deletion out from under the 
            // key collection.
            if (isPurgeNeeded) 
            { 
                ValueTableKey[] localKeys = new ValueTableKey[keys.Count];
                keys.CopyTo(localKeys, 0); 

                for (int i=localKeys.Length-1;  i >= 0;  --i)
                {
                    if (localKeys[i].IsStale) 
                    {
                        _table.Remove(localKeys[i]); 
                    } 
                }
            } 

            return isPurgeNeeded;   // return true if something happened
        }
 
        private HybridDictionary _table;
        private static object CachedNull = new Object(); 
 
        private class ValueTableKey
        { 
            public ValueTableKey(object item, PropertyDescriptor pd)
            {
                Invariant.Assert(item != null && pd != null);
 
                // store weak references to item and pd, so as not to affect their
                // GC behavior.  But remember their hashcode. 
                _item = new WeakReference(item); 
                _descriptor = new WeakReference(pd);
                _hashCode = unchecked(item.GetHashCode() + pd.GetHashCode()); 
            }

            public object Item
            { 
                get { return _item.Target; }
            } 
 
            public PropertyDescriptor PropertyDescriptor
            { 
                get { return (PropertyDescriptor)_descriptor.Target; }
            }

            public bool IsStale 
            {
                get { return Item == null || PropertyDescriptor == null; } 
            } 

            public override bool Equals(object o) 
            {
                if (o == this)
                    return true;    // this allows deletion of stale keys
 
                ValueTableKey that = o as ValueTableKey;
                if (that != null) 
                { 
                    object item = this.Item;
                    PropertyDescriptor descriptor = this.PropertyDescriptor; 
                    if (item == null || descriptor == null)
                        return false;   // a stale key matches nothing (except itself)

                    return this._hashCode == that._hashCode && 
                            Object.Equals(item, that.Item) &&
                            Object.Equals(descriptor, that.PropertyDescriptor); 
                } 

                return false;   // this doesn't match a non-ValueTableKey 
            }

            public override int GetHashCode()
            { 
                return _hashCode;
            } 
 
            WeakReference _item;
            WeakReference _descriptor; 
            int _hashCode;
        }
    }
} 

// 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: Mapping of (item, property accessor) to value 
// 
//---------------------------------------------------------------------------
 
/***************************************************************************\
    Some properties in the world behave poorly, in the sense that two
    successive GetValue calls return different values (i.e. the property
    "changes" every time you call it).  To work around this problem, we 
    cache the value the first time we retrieve it, and interpose a cache
    lookup in front of every GetValue.  The ValueTable class implements this 
    cache;  there is one such table per DataBindEngine (hence one per Dispatcher). 

    The malfeasant properties seem to be isolated to ADO-related classes in 
    System.Data.  Lacking an explicit list of all such properties, or a rule
    for identifying them, we use a heuristic that covers all the known cases.
    See the ShouldCache method.
\***************************************************************************/ 

using System; 
using System.Collections; 
using System.Collections.Generic;           // IEnumerable
using System.Collections.Specialized;       // HybridDictionary 
using System.ComponentModel;                // IBindingList
using System.Reflection;                    // TypeDescriptor
using System.Windows;                       // SR
using MS.Internal;                          // Invariant.Assert 

namespace MS.Internal.Data 
{ 
    internal sealed class ValueTable : IWeakEventListener
    { 
        // should we cache the value of the given property from the given item?
        internal static bool ShouldCache(object item, PropertyDescriptor pd)
        {
            // custom property descriptors returning IBindingList (bug 1190076) 
            if (item is ICustomTypeDescriptor && pd.PropertyType == typeof(IBindingList))
            { 
                // according to SQL dev, the problem only arises for certain 
                // subclasses of PropertyDescriptor (bug 1285603)
                string name = pd.GetType().Name; 
                if (name == "DataTablePropertyDescriptor" ||
                    name == "DataRelationPropertyDescriptor")
                {
                    return true; 
                }
            } 
 
            // XLinq's property descriptors for the Elements and Descendants properties
            if (IsXLinqCollectionProperty(pd)) 
            {
                return true;
            }
 
            return false;       // everything else is treated normally
        } 
 
        // XLinq exposes two synthetic properties - Elements and Descendants -
        // on XElement that return IEnumerable.  We handle these specially 
        // to work around problems involving identity and change notifications
        internal static bool IsXLinqCollectionProperty(PropertyDescriptor pd)
        {
            // Both properties return IEnumerable (possibly changing 
            // to IList).  Use this to eliminate other properties cheaply.
            Type propertyType = pd.PropertyType; 
            if (!propertyType.IsGenericType) 
                return false;
 
            Type propertyTypeDefinition = propertyType.GetGenericTypeDefinition();
            if (propertyTypeDefinition != typeof(IEnumerable<>) && propertyTypeDefinition != typeof(IList<>))
                return false;
 
            Type descriptorType = pd.GetType();
            if (descriptorType.Namespace != "System.Xml.Linq.ComponentModel") 
                return false; 

            string name = descriptorType.Name; 
            return (name == "XElementElementsPropertyDescriptor" ||
                    name == "XElementDescendantsPropertyDescriptor");
        }
 
        // retrieve the value, using the cache if necessary
        internal object GetValue(object item, PropertyDescriptor pd, bool indexerIsNext) 
        { 
            if (!ShouldCache(item, pd))
            { 
                // normal case - just get the value the old-fashioned way
                return pd.GetValue(item);
            }
            else 
            {
                // lazy creation of the cache 
                if (_table == null) 
                {
                    _table = new HybridDictionary(); 
                }

                // look up the value in the cache
                bool isXLinqCollectionProperty = IsXLinqCollectionProperty(pd); 
                ValueTableKey key = new ValueTableKey(item, pd);
                object value = _table[key]; 
 
                // if there's no entry, fetch the value and cache it
                if (value == null) 
                {
                    if (isXLinqCollectionProperty)
                    {
                        // interpose our own value for special XLinq properties 
                        value = new XDeferredAxisSource(item, pd);
                    } 
                    else 
                    {
                        value = pd.GetValue(item); 
                    }

                    if (value == null)
                    { 
                        value = CachedNull;     // distinguish a null value from no entry
                    } 
 
                    _table[key] = value;
                } 

                // decode null, if necessary
                if (value == CachedNull)
                { 
                    value = null;
                } 
                else if (isXLinqCollectionProperty && !indexerIsNext) 
                {
                    // The XLinq properties need special help.  When the path 
                    // contains "Elements[Foo]", we should return the interposed
                    // XDeferredAxisSource;  the path worker will then call the XDAS's
                    // indexer with argument "Foo", and obtain the desired
                    // ObservableCollection.  But when the path contains "Elements" 
                    // with no indexer, we should return an ObservableCollection
                    // corresponding to the full set of children. 
                    // [All this applies to "Descendants" as well.] 
                    XDeferredAxisSource xdas = (XDeferredAxisSource)value;
                    value = xdas.FullCollection; 
                }

                return value;
            } 
        }
 
        // listen for changes to a property 
        internal void RegisterForChanges(object item, PropertyDescriptor pd, DataBindEngine engine)
        { 
            // lazy creation of the cache
            if (_table == null)
            {
                _table = new HybridDictionary(); 
            }
 
            ValueTableKey key = new ValueTableKey(item, pd); 
            object value = _table[key];
 
            if (value == null)
            {
                // new entry needed - add a listener
                INotifyPropertyChanged inpc = item as INotifyPropertyChanged; 
                if (inpc != null)
                { 
                    PropertyChangedEventManager.AddListener(inpc, this, pd.Name); 
                }
                else 
                {
                    ValueChangedEventManager.AddListener(item, this, pd);
                }
            } 
        }
 
        ///  
        /// Handle events from the centralized event table
        ///  
        bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
        {
            if (managerType == typeof(PropertyChangedEventManager))
            { 
                PropertyChangedEventArgs pce = (PropertyChangedEventArgs)e;
                InvalidateCache(sender, null, pce.PropertyName); 
            } 
            else if (managerType == typeof(ValueChangedEventManager))
            { 
                ValueChangedEventArgs vce = (ValueChangedEventArgs)e;
                InvalidateCache(sender, vce.PropertyDescriptor, null);
            }
            else 
            {
                return false;       // unrecognized event 
            } 

            return true; 
        }

        // invalidate (remove) a cache entry.  Called when the source raises a change event.
        void InvalidateCache(object item, PropertyDescriptor pd, string name) 
        {
            if (pd == null) 
            { 
                Invariant.Assert(!String.IsNullOrEmpty(name), "caller must supply either pd or name");
                // regenerate the descriptor from the name 
                // (this code matches PropertyPathWorker.GetInfo)
                if (item is ICustomTypeDescriptor)
                {
                    pd = TypeDescriptor.GetProperties(item)[name]; 
                }
                else 
                { 
                    pd = TypeDescriptor.GetProperties(item.GetType())[name];
                } 
            }

            // ignore changes to special XLinq PD's - leave our interposed object in the cache
            if (IsXLinqCollectionProperty(pd)) 
                return;
 
            ValueTableKey key = new ValueTableKey(item, pd); 
            _table.Remove(key);
        } 

        // remove stale entries from the table
        internal bool Purge()
        { 
            if (_table == null)
                return false; 
 
            // first see if there are any stale entries.  No sense allocating
            // storage if there's nothing to do. 
            bool isPurgeNeeded = false;
            ICollection keys = _table.Keys;
            foreach (ValueTableKey key in keys)
            { 
                if (key.IsStale)
                { 
                    isPurgeNeeded = true; 
                    break;
                } 
            }

            // if the purge is needed, copy the keys and purge the
            // stale entries.  The copy avoids deletion out from under the 
            // key collection.
            if (isPurgeNeeded) 
            { 
                ValueTableKey[] localKeys = new ValueTableKey[keys.Count];
                keys.CopyTo(localKeys, 0); 

                for (int i=localKeys.Length-1;  i >= 0;  --i)
                {
                    if (localKeys[i].IsStale) 
                    {
                        _table.Remove(localKeys[i]); 
                    } 
                }
            } 

            return isPurgeNeeded;   // return true if something happened
        }
 
        private HybridDictionary _table;
        private static object CachedNull = new Object(); 
 
        private class ValueTableKey
        { 
            public ValueTableKey(object item, PropertyDescriptor pd)
            {
                Invariant.Assert(item != null && pd != null);
 
                // store weak references to item and pd, so as not to affect their
                // GC behavior.  But remember their hashcode. 
                _item = new WeakReference(item); 
                _descriptor = new WeakReference(pd);
                _hashCode = unchecked(item.GetHashCode() + pd.GetHashCode()); 
            }

            public object Item
            { 
                get { return _item.Target; }
            } 
 
            public PropertyDescriptor PropertyDescriptor
            { 
                get { return (PropertyDescriptor)_descriptor.Target; }
            }

            public bool IsStale 
            {
                get { return Item == null || PropertyDescriptor == null; } 
            } 

            public override bool Equals(object o) 
            {
                if (o == this)
                    return true;    // this allows deletion of stale keys
 
                ValueTableKey that = o as ValueTableKey;
                if (that != null) 
                { 
                    object item = this.Item;
                    PropertyDescriptor descriptor = this.PropertyDescriptor; 
                    if (item == null || descriptor == null)
                        return false;   // a stale key matches nothing (except itself)

                    return this._hashCode == that._hashCode && 
                            Object.Equals(item, that.Item) &&
                            Object.Equals(descriptor, that.PropertyDescriptor); 
                } 

                return false;   // this doesn't match a non-ValueTableKey 
            }

            public override int GetHashCode()
            { 
                return _hashCode;
            } 
 
            WeakReference _item;
            WeakReference _descriptor; 
            int _hashCode;
        }
    }
} 

// 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