/// <summary>
        /// If <paramref name="type"/> has been registered with a <see cref="DataViewType"/>, this function returns <see langword="true"/>.
        /// Otherwise, this function returns <see langword="false"/>.
        /// </summary>
        internal static bool Knows(Type type, IEnumerable <Attribute> typeAttributes = null)
        {
            //Filter attributes as we only care about DataViewTypeAttribute
            DataViewTypeAttribute typeAttr = null;

            if (typeAttributes != null)
            {
                typeAttributes = typeAttributes.Where(attr => attr.GetType().IsSubclassOf(typeof(DataViewTypeAttribute)));
                if (typeAttributes.Count() > 1)
                {
                    throw Contracts.ExceptParam(nameof(type), "Type {0} cannot be marked with multiple attributes, {1}, derived from {2}.",
                                                type.Name, typeAttributes, typeof(DataViewTypeAttribute));
                }
                else if (typeAttributes.Count() == 1)
                {
                    typeAttr = typeAttributes.First() as DataViewTypeAttribute;
                }
            }
            lock (_lock)
            {
                // Compute the ID of type with extra attributes.
                var rawType = new TypeWithAttributes(type, typeAttr);

                // Check if this ID has been associated with a DataViewType.
                // Note that the dictionary below contains (rawType, dataViewType) pairs (key type is TypeWithAttributes, and value type is DataViewType).
                if (_rawTypeToDataViewTypeMap.ContainsKey(rawType))
                {
                    return(true);
                }
                else
                {
                    return(false);
                }
            }
        }
        /// <summary>
        /// Returns the <see cref="DataViewType"/> registered for <paramref name="type"/> and its <paramref name="typeAttributes"/>.
        /// </summary>
        internal static DataViewType GetDataViewType(Type type, IEnumerable <Attribute> typeAttributes = null)
        {
            //Filter attributes as we only care about DataViewTypeAttribute
            DataViewTypeAttribute typeAttr = null;

            if (typeAttributes != null)
            {
                typeAttributes = typeAttributes.Where(attr => attr.GetType().IsSubclassOf(typeof(DataViewTypeAttribute)));
                if (typeAttributes.Count() > 1)
                {
                    throw Contracts.ExceptParam(nameof(type), "Type {0} cannot be marked with multiple attributes, {1}, derived from {2}.",
                                                type.Name, typeAttributes, typeof(DataViewTypeAttribute));
                }
                else if (typeAttributes.Count() == 1)
                {
                    typeAttr = typeAttributes.First() as DataViewTypeAttribute;
                }
            }
            lock (_lock)
            {
                // Compute the ID of type with extra attributes.
                var rawType = new TypeWithAttributes(type, typeAttr);

                // Get the DataViewType's ID which typeID is mapped into.
                if (!_rawTypeToDataViewTypeMap.TryGetValue(rawType, out DataViewType dataViewType))
                {
                    throw Contracts.ExceptParam(nameof(type), $"The raw type {type} with attributes {typeAttributes} is not registered with a DataView type.");
                }

                // Retrieve the actual DataViewType identified by dataViewType.
                return(dataViewType);
            }
        }
        public static void Register(DataViewType dataViewType, Type type, IEnumerable <Attribute> typeAttributes)
        {
            DataViewTypeAttribute typeAttr = null;

            if (typeAttributes != null)
            {
                if (typeAttributes.Count() > 1)
                {
                    throw Contracts.ExceptParam(nameof(type), $"Type {type} has too many attributes.");
                }
                else if (typeAttributes.Count() == 1)
                {
                    var attr = typeAttributes.First();
                    if (!attr.GetType().IsSubclassOf(typeof(DataViewTypeAttribute)))
                    {
                        throw Contracts.ExceptParam(nameof(type), $"Type {type} has an attribute that is not of DataViewTypeAttribute.");
                    }
                    else
                    {
                        typeAttr = attr as DataViewTypeAttribute;
                    }
                }
            }
            Register(dataViewType, type, typeAttr);
        }
        /// <summary>
        /// This function tells that <paramref name="dataViewType"/> should be representation of data in <paramref name="type"/> in
        /// ML.NET's type system. The registered <paramref name="type"/> must be a standard C# object's type.
        /// </summary>
        /// <param name="type">Native type in C#.</param>
        /// <param name="dataViewType">The corresponding type of <paramref name="type"/> in ML.NET's type system.</param>
        /// <param name="typeAttribute">The <see cref="DataViewTypeAttribute"/> attached to <paramref name="type"/>.</param>
        public static void Register(DataViewType dataViewType, Type type, DataViewTypeAttribute typeAttribute = null)
        {
            lock (_lock)
            {
                if (_bannedRawTypes.Contains(type))
                {
                    throw Contracts.ExceptParam(nameof(type), $"Type {type} has been registered as ML.NET's default supported type, " +
                                                $"so it can't not be registered again.");
                }

                var rawType = new TypeWithAttributes(type, typeAttribute);

                if (_rawTypeToDataViewTypeMap.ContainsKey(rawType) && _rawTypeToDataViewTypeMap[rawType].Equals(dataViewType) &&
                    _dataViewTypeToRawTypeMap.ContainsKey(dataViewType) && _dataViewTypeToRawTypeMap[dataViewType].Equals(rawType))
                {
                    // This type pair has been registered. Note that registering one data type pair multiple times is allowed.
                    return;
                }

                if (_rawTypeToDataViewTypeMap.ContainsKey(rawType) && !_rawTypeToDataViewTypeMap[rawType].Equals(dataViewType))
                {
                    // There is a pair of (rawType, anotherDataViewType) in _typeToDataViewType so we cannot register
                    // (rawType, dataViewType) again. The assumption here is that one rawType can only be associated
                    // with one dataViewType.
                    var associatedDataViewType = _rawTypeToDataViewTypeMap[rawType];
                    throw Contracts.ExceptParam(nameof(type), $"Repeated type register. The raw type {type} " +
                                                $"has been associated with {associatedDataViewType} so it cannot be associated with {dataViewType}.");
                }

                if (_dataViewTypeToRawTypeMap.ContainsKey(dataViewType) && !_dataViewTypeToRawTypeMap[dataViewType].Equals(rawType))
                {
                    // There is a pair of (dataViewType, anotherRawType) in _dataViewTypeToType so we cannot register
                    // (dataViewType, rawType) again. The assumption here is that one dataViewType can only be associated
                    // with one rawType.
                    var associatedRawType = _dataViewTypeToRawTypeMap[dataViewType].TargetType;
                    throw Contracts.ExceptParam(nameof(dataViewType), $"Repeated type register. The DataView type {dataViewType} " +
                                                $"has been associated with {associatedRawType} so it cannot be associated with {type}.");
                }

                _rawTypeToDataViewTypeMap.Add(rawType, dataViewType);
                _dataViewTypeToRawTypeMap.Add(dataViewType, rawType);
            }
        }
 public TypeWithAttributes(Type type, DataViewTypeAttribute attribute)
 {
     TargetType           = type;
     _associatedAttribute = attribute;
 }