File: UI\WebControls\DataSourceHelper.cs
Project: ndp\fx\src\xsp\system\Extensions\System.Web.Extensions.csproj (System.Web.Extensions)
namespace System.Web.UI.WebControls {
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.Web;
    using System.Linq;
    using System.Web.Compilation;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.Resources;
    using System.Globalization;
 
    internal static class DataSourceHelper {
        public static object SaveViewState(ParameterCollection parameters) {
            if (parameters != null) {
                return ((IStateManager)parameters).SaveViewState();
            }
            return null;
        }
 
        public static void TrackViewState(ParameterCollection parameters) {
            if (parameters != null) {
                ((IStateManager)parameters).TrackViewState();
            }
        }        
 
        public static IDictionary<string, object> ToDictionary(this ParameterCollection parameters, HttpContext context, Control control) {
            return ToDictionary(parameters.GetValues(context, control));
        }
 
        internal static IDictionary<string, object> ToDictionary(this IOrderedDictionary parameterValues) {
            Dictionary<string, object> values = new Dictionary<string, object>(parameterValues.Count, StringComparer.OrdinalIgnoreCase);
 
            foreach (DictionaryEntry entry in parameterValues) {
                values[(string)entry.Key] = entry.Value;
            }
 
            return values;
        }     
 
        public static IOrderedDictionary ToCaseInsensitiveDictionary(this IDictionary dictionary) {
            if (dictionary != null) {
                IOrderedDictionary destination = new OrderedDictionary(dictionary.Count, StringComparer.OrdinalIgnoreCase);
 
                foreach (DictionaryEntry de in dictionary) {
                    destination[de.Key] = de.Value;
                }
 
                return destination;
            }
            return null;
        }
 
        internal static object CreateObjectInstance(Type type) {
            // FastCreatePublicInstance is faster than Activator.CreateInstance since it caches the type factories.
            return HttpRuntime.FastCreatePublicInstance(type);
        }
 
        public static bool MergeDictionaries(object dataObjectType, ParameterCollection referenceValues, IDictionary source,
                               IDictionary destination, IDictionary<string, Exception> validationErrors) {
            return MergeDictionaries(dataObjectType, referenceValues, source, destination, null, validationErrors);
        }
 
        public static bool MergeDictionaries(object dataObjectType, ParameterCollection reference, IDictionary source,
                                              IDictionary destination, IDictionary destinationCopy, IDictionary<string, Exception> validationErrors) {            
            if (source != null) {
                foreach (DictionaryEntry de in source) {
                    object value = de.Value;
                    // search for a parameter that corresponds to this dictionary entry.
                    Parameter referenceParameter = null;
                    string parameterName = (string)de.Key;
                    foreach (Parameter p in reference) {
                        if (String.Equals(p.Name, parameterName, StringComparison.OrdinalIgnoreCase)) {
                            referenceParameter = p;
                            break;
                        }
                    }
                    // use the parameter for type conversion, default value and/or converting empty string to null.
                    if (referenceParameter != null) {
                        try {
                            value = referenceParameter.GetValue(value, true);
                        }
                        catch (Exception e) {
                            // catch conversion exceptions so they can be handled. Note that conversion throws various
                            // types of exceptions like InvalidCastException, FormatException, OverflowException, etc.
                            validationErrors[referenceParameter.Name] = e;
                        }
                    }
                    // save the value to the merged dictionaries.
                    destination[parameterName] = value;
                    if (destinationCopy != null) {
                        destinationCopy[parameterName] = value;
                    }
                }
            }
            return validationErrors.Count == 0;
        }
 
        public static Type GetType(string typeName) {
            return BuildManager.GetType(typeName, true /* throwOnError */, true /* ignoreCase */);
        }
 
        private static object ConvertType(object value, Type type, string paramName) {
            // NOTE: This method came from ObjectDataSource with no changes made.
            string s = value as string;
            if (s != null) {
                // Get the type converter for the destination type
                TypeConverter converter = TypeDescriptor.GetConverter(type);
                if (converter != null) {
                    // Perform the conversion
                    try {
                        value = converter.ConvertFromString(s);
                    }
                    catch (NotSupportedException) {
                        throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture,
                            AtlasWeb.LinqDataSourceView_CannotConvertType, paramName, typeof(string).FullName,
                            type.FullName));
                    }
                    catch (FormatException) {
                        throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture,
                            AtlasWeb.LinqDataSourceView_CannotConvertType, paramName, typeof(string).FullName,
                            type.FullName));
                    }
                }
            }
            return value;
        }
 
        public static object BuildDataObject(Type dataObjectType, IDictionary inputParameters, IDictionary<string, Exception> validationErrors) {
            object dataObject = CreateObjectInstance(dataObjectType);
 
            PropertyDescriptorCollection props = TypeDescriptor.GetProperties(dataObject);
            foreach (DictionaryEntry de in inputParameters) {
                string propName = (de.Key == null ? String.Empty : de.Key.ToString());
                PropertyDescriptor property = props.Find(propName, /*ignoreCase*/true);
                // NOTE: No longer throws when a property is not found or is read only.  This makes
                // Delete, Insert and Update operations more optimistic, allowing scenarios such as:
                // 1) Deletes and Updates after projecting data in the Selecting event.
                // 2) Deletes and Updates after selecting children of the data object type in the
                //    Selecting event.
                if ((property != null) && (!property.IsReadOnly)) {
                    try {
                        object value = BuildObjectValue(de.Value, property.PropertyType, propName);
                        property.SetValue(dataObject, value);
                    }
                    catch (Exception e) {
                        validationErrors[property.Name] = e;
                    }
                }
            }
 
            if (validationErrors.Any()) {
                return null;
            }
 
            return dataObject;
        }
 
        internal static object BuildObjectValue(object value, Type destinationType, string paramName) {
            // NOTE: This method came from ObjectDataSource with no changes made.
            // Only consider converting the type if the value is non-null and the types don't match
            if ((value != null) && (!destinationType.IsInstanceOfType(value))) {
                Type innerDestinationType = destinationType;
                bool isNullable = false;
                if (destinationType.IsGenericType &&
                    (destinationType.GetGenericTypeDefinition() == typeof(Nullable<>))) {
                    innerDestinationType = destinationType.GetGenericArguments()[0];
                    isNullable = true;
                }
                else {
                    if (destinationType.IsByRef) {
                        innerDestinationType = destinationType.GetElementType();
                    }
                }
 
                // Try to convert from for example string to DateTime, so that
                // afterwards we can convert DateTime to Nullable<DateTime>
 
                // If the value is a string, we attempt to use a TypeConverter to convert it
                value = ConvertType(value, innerDestinationType, paramName);
 
                // Special-case the value when the destination is Nullable<T>
                if (isNullable) {
                    Type paramValueType = value.GetType();
                    if (innerDestinationType != paramValueType) {
                        // Throw if for example, we are trying to convert from int to Nullable<bool>
                        throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture,
                            AtlasWeb.LinqDataSourceView_CannotConvertType, paramName, paramValueType.FullName,
                            String.Format(CultureInfo.InvariantCulture, "Nullable<{0}>",
                            destinationType.GetGenericArguments()[0].FullName)));
                    }
                }
            }
            return value;
        }
    }
}