File: System\Data\Metadata\Edm\Provider\ClrProviderManifest.cs
Project: ndp\fx\src\DataEntity\System.Data.Entity.csproj (System.Data.Entity)
//---------------------------------------------------------------------
// <copyright file="ClrProviderManifest.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner       Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
 
using System.Collections.Generic;
using System.Data.Common;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Data.Spatial;
using System.Threading;
using System.Linq;
 
namespace System.Data.Metadata.Edm
{
    internal class ClrProviderManifest : DbProviderManifest
    {
        private const int s_PrimitiveTypeCount = 17;
        private System.Collections.ObjectModel.ReadOnlyCollection<PrimitiveType> _primitiveTypes;
        private static ClrProviderManifest _instance = new ClrProviderManifest();
 
        /// <summary>
        /// A private constructor to prevent other places from instantiating this class
        /// </summary>
        private ClrProviderManifest()
        {
        }
 
        /// <summary>
        /// Gets the EDM provider manifest singleton instance
        /// </summary>
        internal static ClrProviderManifest Instance
        {
            get
            {
                return _instance;
            }
        }
 
        /// <summary>
        /// Returns the namespace used by this provider manifest
        /// </summary>
        public override string NamespaceName
        {
            get { return EdmConstants.ClrPrimitiveTypeNamespace; }
        }
                
        /// <summary>
        /// Returns the primitive type corresponding to the given CLR type
        /// </summary>
        /// <param name="clrType">The CLR type for which the PrimitiveType object is retrieved</param>
        /// <param name="primitiveType">The retrieved primitive type</param>
        /// <returns>True if a primitive type is returned</returns>
        internal bool TryGetPrimitiveType(Type clrType, out PrimitiveType primitiveType)
        {
            primitiveType = null;
            PrimitiveTypeKind resolvedTypeKind;
            if (TryGetPrimitiveTypeKind(clrType, out resolvedTypeKind))
            {
                InitializePrimitiveTypes();
                primitiveType = _primitiveTypes[(int)resolvedTypeKind];
                return true;
            }
            
            return false;
        }
 
        /// <summary>
        /// Returns the <see cref="PrimitiveTypeKind"/> corresponding to the given CLR type
        /// </summary>
        /// <param name="clrType">The CLR type for which the PrimitiveTypeKind value should be resolved</param>
        /// <param name="primitiveType">The PrimitiveTypeKind value to which the CLR type resolves, if any.</param>
        /// <returns>True if the CLR type represents a primitive (EDM) type; otherwise false.</returns>
        internal bool TryGetPrimitiveTypeKind(Type clrType, out PrimitiveTypeKind resolvedPrimitiveTypeKind)
        {
            PrimitiveTypeKind? primitiveTypeKind = null;
            if (!clrType.IsEnum) // Enums return the TypeCode of their underlying type
            {
                // As an optimization, short-circuit when the provided type has a known type code.
                switch (Type.GetTypeCode(clrType))
                {
                    // PrimitiveTypeKind.Binary = byte[] = TypeCode.Object
                    case TypeCode.Boolean:
                        primitiveTypeKind = PrimitiveTypeKind.Boolean;
                        break;
                    case TypeCode.Byte:
                        primitiveTypeKind = PrimitiveTypeKind.Byte;
                        break;
                    case TypeCode.DateTime:
                        primitiveTypeKind = PrimitiveTypeKind.DateTime;
                        break;
                    // PrimitiveTypeKind.DateTimeOffset = System.DateTimeOffset = TypeCode.Object
                    case TypeCode.Decimal:
                        primitiveTypeKind = PrimitiveTypeKind.Decimal;
                        break;
                    case TypeCode.Double:
                        primitiveTypeKind = PrimitiveTypeKind.Double;
                        break;
                    // PrimitiveTypeKind.Geography = System.Data.Spatial.DbGeometry (or subtype) = TypeCode.Object
                    // PrimitiveTypeKind.Geometry = System.Data.Spatial.DbGeometry (or subtype) = TypeCode.Object
                    // PrimitiveTypeKind.Guid = System.Guid = TypeCode.Object
                    case TypeCode.Int16:
                        primitiveTypeKind = PrimitiveTypeKind.Int16;
                        break;
                    case TypeCode.Int32:
                        primitiveTypeKind = PrimitiveTypeKind.Int32;
                        break;
                    case TypeCode.Int64:
                        primitiveTypeKind = PrimitiveTypeKind.Int64;
                        break;
                    case TypeCode.SByte:
                        primitiveTypeKind = PrimitiveTypeKind.SByte;
                        break;
                    case TypeCode.Single:
                        primitiveTypeKind = PrimitiveTypeKind.Single;
                        break;
                    case TypeCode.String:
                        primitiveTypeKind = PrimitiveTypeKind.String;
                        break;
                    // PrimitiveTypeKind.Time = System.TimeSpan = TypeCode.Object
                    case TypeCode.Object:
                        {
                            if (typeof(byte[]) == clrType)
                            {
                                primitiveTypeKind = PrimitiveTypeKind.Binary;
                            }
                            else if (typeof(DateTimeOffset) == clrType)
                            {
                                primitiveTypeKind = PrimitiveTypeKind.DateTimeOffset;
                            }
                            // DbGeography/Geometry are abstract so subtypes must be allowed
                            else if (typeof(System.Data.Spatial.DbGeography).IsAssignableFrom(clrType))
                            {
                                primitiveTypeKind = PrimitiveTypeKind.Geography;
                            }
                            else if (typeof(System.Data.Spatial.DbGeometry).IsAssignableFrom(clrType))
                            {
                                primitiveTypeKind = PrimitiveTypeKind.Geometry;
                            }
                            else if (typeof(Guid) == clrType)
                            {
                                primitiveTypeKind = PrimitiveTypeKind.Guid;
                            }
                            else if (typeof(TimeSpan) == clrType)
                            {
                                primitiveTypeKind = PrimitiveTypeKind.Time;
                            }
                            break;
                        }
                }
            }
 
            if (primitiveTypeKind.HasValue)
            {
                resolvedPrimitiveTypeKind = primitiveTypeKind.Value;
                return true;
            }
            else
            {
                resolvedPrimitiveTypeKind = default(PrimitiveTypeKind);
                return false;
            }
        }
 
        /// <summary>
        /// Returns all the functions in this provider manifest
        /// </summary>
        /// <returns>A collection of functions</returns>
        public override System.Collections.ObjectModel.ReadOnlyCollection<EdmFunction> GetStoreFunctions()
        {
            return Helper.EmptyEdmFunctionReadOnlyCollection;
        }
 
        /// <summary>
        /// Returns all the FacetDescriptions for a particular type
        /// </summary>
        /// <param name="type">the type to return FacetDescriptions for.</param>
        /// <returns>The FacetDescriptions for the type given.</returns>
        public override System.Collections.ObjectModel.ReadOnlyCollection<FacetDescription> GetFacetDescriptions(EdmType type)
        {
            if (Helper.IsPrimitiveType(type) && ((PrimitiveType)type).DataSpace == DataSpace.OSpace)
            {
                // we don't have our own facets, just defer to the edm primitive type facets
                PrimitiveType basePrimitive = (PrimitiveType)type.BaseType;
                return basePrimitive.ProviderManifest.GetFacetDescriptions(basePrimitive);
            }
 
            return Helper.EmptyFacetDescriptionEnumerable;
        }
 
        /// <summary>
        /// Initializes all the primitive types
        /// </summary>
        private void InitializePrimitiveTypes()
        {
            if (_primitiveTypes != null)
            {
                return;
            }
 
            PrimitiveType[] primitiveTypes = new PrimitiveType[s_PrimitiveTypeCount];
            primitiveTypes[(int)PrimitiveTypeKind.Binary] = CreatePrimitiveType(typeof(Byte[]), PrimitiveTypeKind.Binary);
            primitiveTypes[(int)PrimitiveTypeKind.Boolean] = CreatePrimitiveType(typeof(Boolean), PrimitiveTypeKind.Boolean);
            primitiveTypes[(int)PrimitiveTypeKind.Byte] = CreatePrimitiveType(typeof(Byte), PrimitiveTypeKind.Byte);
            primitiveTypes[(int)PrimitiveTypeKind.DateTime] = CreatePrimitiveType(typeof(DateTime), PrimitiveTypeKind.DateTime);
            primitiveTypes[(int)PrimitiveTypeKind.Time] = CreatePrimitiveType(typeof(TimeSpan), PrimitiveTypeKind.Time);
            primitiveTypes[(int)PrimitiveTypeKind.DateTimeOffset] = CreatePrimitiveType(typeof(DateTimeOffset), PrimitiveTypeKind.DateTimeOffset);
            primitiveTypes[(int)PrimitiveTypeKind.Decimal] = CreatePrimitiveType(typeof(Decimal), PrimitiveTypeKind.Decimal);
            primitiveTypes[(int)PrimitiveTypeKind.Double] = CreatePrimitiveType(typeof(Double), PrimitiveTypeKind.Double);
            primitiveTypes[(int)PrimitiveTypeKind.Geography] = CreatePrimitiveType(typeof(DbGeography), PrimitiveTypeKind.Geography);
            primitiveTypes[(int)PrimitiveTypeKind.Geometry] = CreatePrimitiveType(typeof(DbGeometry), PrimitiveTypeKind.Geometry);
            primitiveTypes[(int)PrimitiveTypeKind.Guid] = CreatePrimitiveType(typeof(Guid), PrimitiveTypeKind.Guid);
            primitiveTypes[(int)PrimitiveTypeKind.Int16] = CreatePrimitiveType(typeof(Int16), PrimitiveTypeKind.Int16);
            primitiveTypes[(int)PrimitiveTypeKind.Int32] = CreatePrimitiveType(typeof(Int32), PrimitiveTypeKind.Int32);
            primitiveTypes[(int)PrimitiveTypeKind.Int64] = CreatePrimitiveType(typeof(Int64), PrimitiveTypeKind.Int64);
            primitiveTypes[(int)PrimitiveTypeKind.SByte] = CreatePrimitiveType(typeof(SByte), PrimitiveTypeKind.SByte);
            primitiveTypes[(int)PrimitiveTypeKind.Single] = CreatePrimitiveType(typeof(Single), PrimitiveTypeKind.Single);
            primitiveTypes[(int)PrimitiveTypeKind.String] = CreatePrimitiveType(typeof(String), PrimitiveTypeKind.String);
 
            System.Collections.ObjectModel.ReadOnlyCollection<PrimitiveType> readOnlyTypes = new System.Collections.ObjectModel.ReadOnlyCollection<PrimitiveType>(primitiveTypes);
 
            // Set the result to _primitiveTypes at the end
            Interlocked.CompareExchange<System.Collections.ObjectModel.ReadOnlyCollection<PrimitiveType>>(ref _primitiveTypes, readOnlyTypes, null);
        }
 
        /// <summary>
        /// Initialize the primitive type with the given 
        /// </summary>
        /// <param name="clrType">The CLR type of this type</param>
        /// <param name="primitiveTypeKind">The primitive type kind of the primitive type</param>
        private PrimitiveType CreatePrimitiveType(Type clrType, PrimitiveTypeKind primitiveTypeKind)
        {
            // Figures out the base type
            PrimitiveType baseType = MetadataItem.EdmProviderManifest.GetPrimitiveType(primitiveTypeKind);
            PrimitiveType primitiveType = new PrimitiveType(clrType, baseType, this);
            primitiveType.SetReadOnly();
            return primitiveType;
        }
 
 
        public override System.Collections.ObjectModel.ReadOnlyCollection<PrimitiveType> GetStoreTypes()
        {
            InitializePrimitiveTypes();
            return this._primitiveTypes;
        }
 
        public override TypeUsage GetEdmType(TypeUsage storeType)
        {
            throw new NotImplementedException();
        }
 
        public override TypeUsage GetStoreType(TypeUsage edmType)
        {
            throw new NotImplementedException();
        }
 
        /// <summary>
        /// Providers should override this to return information specific to their provider.  
        /// 
        /// This method should never return null.
        /// </summary>
        /// <param name="informationType">The name of the information to be retrieved.</param>
        /// <returns>An XmlReader at the begining of the information requested.</returns>
        protected override System.Xml.XmlReader GetDbInformation(string informationType)
        {
            throw new NotImplementedException();
        }
    }
}