File: system\runtime\interopservices\windowsruntime\icustompropertyprovider.cs
Project: ndp\clr\src\bcl\mscorlib.csproj (mscorlib)
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
//
// <OWNER>Microsoft</OWNER>
 
using System;
using System.StubHelpers;
using System.Reflection;
using System.Diagnostics.Contracts;
using System.Runtime.InteropServices;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Security;
 
namespace System.Runtime.InteropServices.WindowsRuntime
{
    [ComImport]
    [Guid("7C925755-3E48-42B4-8677-76372267033F")]
    [WindowsRuntimeImport]
    internal interface ICustomPropertyProvider 
    {    
        [Pure]
        ICustomProperty GetCustomProperty(string name);
 
        [Pure]
        ICustomProperty GetIndexedProperty(string name, Type indexParameterType);
 
        [Pure]
        string GetStringRepresentation();
 
        Type Type 
        { 
            [Pure]        
            get; 
        }
    }
 
    //
    // Implementation helpers
    //
    internal static class ICustomPropertyProviderImpl
    {
        //
        // Creates a ICustomProperty implementation for Jupiter
        // Called from ICustomPropertyProvider_GetProperty from within runtime
        //
        static internal ICustomProperty CreateProperty(object target, string propertyName)
        {
            Contract.Requires(target != null);
            Contract.Requires(propertyName != null);
 
            // Only return public instance/static properties
            PropertyInfo propertyInfo = target.GetType().GetProperty(
                propertyName,
                BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
 
            if (propertyInfo == null)
                return null;
            else
                return new CustomPropertyImpl(propertyInfo);
        }
 
        //
        // Creates a ICustomProperty implementation for Jupiter
        // Called from ICustomPropertyProvider_GetIndexedProperty from within runtime
        //               
        [System.Security.SecurityCritical]
        static internal unsafe ICustomProperty CreateIndexedProperty(object target, string propertyName, TypeNameNative *pIndexedParamType)
        {
            Contract.Requires(target != null);
            Contract.Requires(propertyName != null);
 
            Type indexedParamType = null;
            SystemTypeMarshaler.ConvertToManaged(pIndexedParamType, ref indexedParamType);
 
            return CreateIndexedProperty(target, propertyName, indexedParamType);        
        }
 
        static internal ICustomProperty CreateIndexedProperty(object target, string propertyName, Type indexedParamType)
        {
            Contract.Requires(target != null);
            Contract.Requires(propertyName != null);
 
            // Only return public instance/static properties
            PropertyInfo propertyInfo = target.GetType().GetProperty(
                propertyName,
                BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public,
                null,                                                                   // default binder
                null,                                                                   // ignore return type
                new Type[] { indexedParamType },                                        // indexed parameter type
                null                                                                    // ignore type modifier
                );
 
            if (propertyInfo == null)
                return null;
            else
                return new CustomPropertyImpl(propertyInfo);
        }
 
        [System.Security.SecurityCritical]
        static internal unsafe void GetType(object target, TypeNameNative *pIndexedParamType)
        {            
            SystemTypeMarshaler.ConvertToNative(target.GetType(), pIndexedParamType);
        }        
    }
 
    [Flags]
    enum InterfaceForwardingSupport
    {
        None                        = 0,
        IBindableVector             = 0x1,              // IBindableVector -> IBindableVector
        IVector                     = 0x2,              // IBindableVector -> IVector<T>
        IBindableVectorView         = 0x4,              // IBindableVectorView -> IBindableVectorView
        IVectorView                 = 0x8,              // IBindableVectorView -> IVectorView<T>
        IBindableIterableOrIIterable= 0x10              // IBindableIterable -> IBindableIterable/IIterable<T>
    }
 
    //
    // Interface for data binding code (CustomPropertyImpl) to retreive the target object
    // See CustomPropertyImpl.InvokeInternal for details
    //
    internal interface IGetProxyTarget
    {
        object GetTarget();            
    }
    
    //
    // Proxy that supports data binding on another object
    //
    // This serves two purposes:
    //
    // 1. Delegate data binding interfaces to another object
    // Note that this proxy implements the native interfaces directly to avoid unnecessary overhead 
    // (such as the adapter code that addresses behavior differences between IBindableVector & List
    // as well as simplify forwarding code (except for IEnumerable)
    //
    // 2. ICLRServices.CreateManagedReference will hand out ICCW* of a new instance of this object
    // and will hold the other object alive
    //
    //
    internal class ICustomPropertyProviderProxy<T1, T2> : IGetProxyTarget,
                                                          ICustomPropertyProvider, 
                                                          ICustomQueryInterface,
                                                          IEnumerable,          // IBindableIterable -> IBindableIterable/IIterable<T>
                                                          IBindableVector,      // IBindableVector -> IBindableVector/IVector<T>
                                                          IBindableVectorView   // IBindableVectorView -> IBindableVectorView/IVectorView<T>
    {
        private object _target;
        private InterfaceForwardingSupport _flags;
 
        internal ICustomPropertyProviderProxy(object target, InterfaceForwardingSupport flags)
        {
            _target = target;
            _flags = flags;
        }
 
        //
        // Creates a new instance of ICustomPropertyProviderProxy<T1, T2> and assign appropriate
        // flags
        //
        internal static object CreateInstance(object target)
        {
            InterfaceForwardingSupport supportFlags = InterfaceForwardingSupport.None;
 
            //
            // QI and figure out the right flags
            //
            if (target as IList != null)
                supportFlags |= InterfaceForwardingSupport.IBindableVector;
 
            // NOTE: We need to use the directed type here
            // If we use IVector_Raw<T1> here, it derives from a different IIterable<T> which the runtime
            // doesn't recognize, and therefore IEnumerable cast won't be able to take advantage of this QI
            if (target as IList<T1> != null)
                supportFlags |= InterfaceForwardingSupport.IVector;
            
            if (target as IBindableVectorView != null)
                supportFlags |= InterfaceForwardingSupport.IBindableVectorView;
 
            // NOTE: We need to use the redirected type here
            // If we use IVector_Raw<T1> here, it derives from a different IIterable<T> which the runtime
            // doesn't recognize, and therefore IEnumerable cast won't be able to take advantage of this QI
            if (target as IReadOnlyList<T2> != null)
                supportFlags |= InterfaceForwardingSupport.IVectorView;
 
            // Verify IEnumerable last because the first few QIs might succeed and we need
            // IEnumerable cast to use that cache (instead of having ICustomPropertyProvider to
            // forward it manually)
            // For example, if we try to shoot in the dark by trying IVector<IInspectable> and it 
            // succeeded, IEnumerable needs to know that
            if (target as IEnumerable != null)
                supportFlags |= InterfaceForwardingSupport.IBindableIterableOrIIterable;
            
            return new ICustomPropertyProviderProxy<T1, T2>(target, supportFlags);                
        }
 
        //
        // ICustomPropertyProvider implementation
        //
        ICustomProperty ICustomPropertyProvider.GetCustomProperty(string name)
        {
            return ICustomPropertyProviderImpl.CreateProperty(_target, name);
        }
 
        ICustomProperty ICustomPropertyProvider.GetIndexedProperty(string name, Type indexParameterType)
        {
            return ICustomPropertyProviderImpl.CreateIndexedProperty(_target, name, indexParameterType);
        }
 
        string ICustomPropertyProvider.GetStringRepresentation()
        {
            return WindowsRuntime.IStringableHelper.ToString(_target);
        }
 
        Type ICustomPropertyProvider.Type 
        { 
            get
            {
                return _target.GetType();
            }
        }
 
        //
        // override ToString() to make sure callers get correct IStringable.ToString() behavior in native code
        //
        public override string ToString()
        {
            return WindowsRuntime.IStringableHelper.ToString(_target);
        }
 
        //
        // IGetProxyTarget - unwraps the target object and use it for data binding
        // 
        object IGetProxyTarget.GetTarget()
        {
            return _target;
        }
 
        // 
        // ICustomQueryInterface methods
        //    
        [System.Security.SecurityCritical]
        public CustomQueryInterfaceResult GetInterface([In]ref Guid iid, out IntPtr ppv)
        {
            ppv = IntPtr.Zero;
 
            if (iid == typeof(IBindableIterable).GUID)
            {
                // Reject the QI if target doesn't implement IEnumerable
                if ((_flags & (InterfaceForwardingSupport.IBindableIterableOrIIterable)) == 0)
                    return CustomQueryInterfaceResult.Failed;                    
            }
 
            if (iid == typeof(IBindableVector).GUID)
            {
                // Reject the QI if target doesn't implement IBindableVector/IVector
                if ((_flags & (InterfaceForwardingSupport.IBindableVector | InterfaceForwardingSupport.IVector)) == 0)
                    return CustomQueryInterfaceResult.Failed;                    
            }
 
            if (iid == typeof(IBindableVectorView).GUID)
            {
                // Reject the QI if target doesn't implement IBindableVectorView/IVectorView
                if ((_flags & (InterfaceForwardingSupport.IBindableVectorView | InterfaceForwardingSupport.IVectorView)) == 0)
                    return CustomQueryInterfaceResult.Failed;                
            }
            
            return CustomQueryInterfaceResult.NotHandled;
        }
    
        //
        // IEnumerable methods
        //
        public IEnumerator GetEnumerator()
        {
            return ((IEnumerable)_target).GetEnumerator();
        }
 
        //
        // IBindableVector implementation (forwards to IBindableVector / IVector<T>)
        //        
        [Pure]
        object IBindableVector.GetAt(uint index)
        {
            IBindableVector bindableVector = GetIBindableVectorNoThrow();
            if (bindableVector != null)
            {
                // IBindableVector -> IBindableVector
                return bindableVector.GetAt(index);
            }
            else
            {
                // IBindableVector -> IVector<T>
                return GetVectorOfT().GetAt(index);
            }            
        }
        
        [Pure]
        uint IBindableVector.Size 
        { 
            get
            {            
                IBindableVector bindableVector = GetIBindableVectorNoThrow();
                if (bindableVector != null)
                {
                    // IBindableVector -> IBindableVector
                    return bindableVector.Size;
                }
                else
                {
                    // IBindableVector -> IVector<T>
                    return GetVectorOfT().Size;
                }            
            }
        }
        
        [Pure]
        IBindableVectorView IBindableVector.GetView()
        {
            IBindableVector bindableVector = GetIBindableVectorNoThrow();
            if (bindableVector != null)
            {
                // IBindableVector -> IBindableVector
                return bindableVector.GetView();
            }
            else
            {
                // IBindableVector -> IVector<T>
                return new IVectorViewToIBindableVectorViewAdapter<T1>(GetVectorOfT().GetView());
            }        
        }
 
        private sealed class IVectorViewToIBindableVectorViewAdapter<T> : IBindableVectorView
        {
            private IVectorView<T> _vectorView;
 
            public IVectorViewToIBindableVectorViewAdapter(IVectorView<T> vectorView)
            { 
                this._vectorView = vectorView; 
            }
 
            [Pure]
            object IBindableVectorView.GetAt(uint index)
            {
                return _vectorView.GetAt(index);
            }
            
            [Pure]
            uint IBindableVectorView.Size
            { 
                get
                {
                    return _vectorView.Size;
                }
            }
            
            [Pure]
            bool IBindableVectorView.IndexOf(object value, out uint index)
            {
                return _vectorView.IndexOf(ConvertTo<T>(value), out index);
            }
 
            IBindableIterator IBindableIterable.First()
            {
                return new IteratorOfTToIteratorAdapter<T>(_vectorView.First());
            }
 
        }         
        
        [Pure]
        bool IBindableVector.IndexOf(object value, out uint index)
        {
            IBindableVector bindableVector = GetIBindableVectorNoThrow();
            if (bindableVector != null)
            {
                // IBindableVector -> IBindableVector
                return bindableVector.IndexOf(value, out index);
            }
            else
            {
                // IBindableVector -> IVector<T>
                return GetVectorOfT().IndexOf(ConvertTo<T1>(value), out index);
            }            
        }
        
        void IBindableVector.SetAt(uint index, object value)
        {
            IBindableVector bindableVector = GetIBindableVectorNoThrow();
            if (bindableVector != null)
            {
                // IBindableVector -> IBindableVector
                bindableVector.SetAt(index, value);
            }
            else
            {
                // IBindableVector -> IVector<T>
                GetVectorOfT().SetAt(index, ConvertTo<T1>(value));
            }            
        }
        
        void IBindableVector.InsertAt(uint index, object value)
        {
            IBindableVector bindableVector = GetIBindableVectorNoThrow();
            if (bindableVector != null)
            {
                // IBindableVector -> IBindableVector
                bindableVector.InsertAt(index, value);
            }
            else
            {
                // IBindableVector -> IVector<T>
                GetVectorOfT().InsertAt(index, ConvertTo<T1>(value));
            }            
        }
        
        void IBindableVector.RemoveAt(uint index)
        {
            IBindableVector bindableVector = GetIBindableVectorNoThrow();
            if (bindableVector != null)
            {
                // IBindableVector -> IBindableVector
                bindableVector.RemoveAt(index);
            }
            else
            {
                // IBindableVector -> IVector<T>
                GetVectorOfT().RemoveAt(index);
            }            
        }
        
        void IBindableVector.Append(object value)
        {
            IBindableVector bindableVector = GetIBindableVectorNoThrow();
            if (bindableVector != null)
            {
                // IBindableVector -> IBindableVector
                bindableVector.Append(value);
            }
            else
            {
                // IBindableVector -> IVector<T>
                GetVectorOfT().Append(ConvertTo<T1>(value));
            }            
        }        
        
        void IBindableVector.RemoveAtEnd()
        {
            IBindableVector bindableVector = GetIBindableVectorNoThrow();
            if (bindableVector != null)
            {
                // IBindableVector -> IBindableVector
                bindableVector.RemoveAtEnd();
            }
            else
            {
                // IBindableVector -> IVector<T>
                GetVectorOfT().RemoveAtEnd();
            }            
        }
        
        void IBindableVector.Clear()
        {
            IBindableVector bindableVector = GetIBindableVectorNoThrow();
            if (bindableVector != null)
            {
                // IBindableVector -> IBindableVector
                bindableVector.Clear();
            }
            else
            {
                // IBindableVector -> IVector<T>
                GetVectorOfT().Clear();
            }            
        }
 
        [SecuritySafeCritical]
        private IBindableVector GetIBindableVectorNoThrow()
        {
            if ((_flags & InterfaceForwardingSupport.IBindableVector) != 0)
                return JitHelpers.UnsafeCast<IBindableVector>(_target);
            else
                return null;
        }
 
        [SecuritySafeCritical]
        private IVector_Raw<T1> GetVectorOfT()
        {
            if ((_flags & InterfaceForwardingSupport.IVector) != 0)
                return JitHelpers.UnsafeCast<IVector_Raw<T1>>(_target);
            else
                throw new InvalidOperationException();  // We should not go down this path, unless Jupiter pass this out to managed code
                                                        // and managed code use reflection to do the cast
        }
        
        //
        // IBindableVectorView implementation (forwarding to IBindableVectorView or IVectorView<T>)
        //
        [Pure]
        object IBindableVectorView.GetAt(uint index)
        {
            IBindableVectorView bindableVectorView = GetIBindableVectorViewNoThrow();
            if (bindableVectorView != null)
                return bindableVectorView.GetAt(index);
            else
                return GetVectorViewOfT().GetAt(index);
        }
        
        [Pure]
        uint IBindableVectorView.Size
        { 
            get
            {
                IBindableVectorView bindableVectorView = GetIBindableVectorViewNoThrow();
                if (bindableVectorView != null)
                    return bindableVectorView.Size;
                else
                    return GetVectorViewOfT().Size;
            }
        }
        
        [Pure]
        bool IBindableVectorView.IndexOf(object value, out uint index)
        {
            IBindableVectorView bindableVectorView = GetIBindableVectorViewNoThrow();
            if (bindableVectorView != null)
                return bindableVectorView.IndexOf(value, out index);
            else
                return GetVectorViewOfT().IndexOf(ConvertTo<T2>(value), out index);
        }
 
        IBindableIterator IBindableIterable.First()
        {
            IBindableVectorView bindableVectorView = GetIBindableVectorViewNoThrow();
            if (bindableVectorView != null)
                return bindableVectorView.First();
            else
                return new IteratorOfTToIteratorAdapter<T2>(GetVectorViewOfT().First());
        }
 
        private sealed class IteratorOfTToIteratorAdapter<T> : IBindableIterator
        {
            private IIterator<T> _iterator;
 
            public IteratorOfTToIteratorAdapter(IIterator<T> iterator)
            { this._iterator = iterator; }
 
            public bool HasCurrent { get { return _iterator.HasCurrent; } }
            public object Current  { get { return (object)_iterator.Current; } }
            public bool MoveNext() { return _iterator.MoveNext(); }
        }         
 
        [SecuritySafeCritical]
        private IBindableVectorView GetIBindableVectorViewNoThrow()
        {
            if ((_flags & InterfaceForwardingSupport.IBindableVectorView) != 0)
                return JitHelpers.UnsafeCast<IBindableVectorView>(_target);
            else
                return null;
        }
 
        [SecuritySafeCritical]
        private IVectorView<T2> GetVectorViewOfT()
        {
            if ((_flags & InterfaceForwardingSupport.IVectorView) != 0)
                return JitHelpers.UnsafeCast<IVectorView<T2>>(_target);
            else
                throw new InvalidOperationException();  // We should not go down this path, unless Jupiter pass this out to managed code
                                                        // and managed code use reflection to do the cast
        }
 
        //
        // Convert to type T
        //
        private static T ConvertTo<T>(object value)
        {
            // Throw ArgumentNullException if value is null (otherwise we'll throw NullReferenceException
            // when casting value to T)
            ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(value, ExceptionArgument.value);
        
            // No coersion support needed. If we need coersion later, this is the place
            return (T) value;
        }
    }
}