admin
2020-06-10 a610f2ab6e543d2cb78c1ef212ac6a74ddc067d9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
// Copyright © 2016 The CefSharp Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
 
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
 
namespace CefSharp.ModelBinding
{
    /// <summary>
    /// Default binder - used as a fallback when a specific modelbinder
    /// is not available.
    /// </summary>
    public class DefaultBinder : IBinder
    {
        private static readonly MethodInfo ToArrayMethodInfo = typeof(Enumerable).GetMethod("ToArray", BindingFlags.Public | BindingFlags.Static);
 
        /// <summary>
        /// Bind to the given model type
        /// </summary>
        /// <param name="obj">object to be converted into a model</param>
        /// <param name="targetType">the target param type</param>
        /// <returns>Bound model</returns>
        public virtual object Bind(object obj, Type targetType)
        {
            if (obj == null)
            {
                if (targetType.IsValueType)
                {
                    //For value types (int, double, etc) we cannot return null,
                    //we need to return the default value for that type.
                    return Activator.CreateInstance(targetType);
                }
                return null;
            }
 
            var objType = obj.GetType();
 
            // If the object can be directly assigned to the modelType then return immediately. 
            if (targetType.IsAssignableFrom(objType))
            {
                return obj;
            }
 
            if (targetType.IsEnum && targetType.IsEnumDefined(obj))
            {
                return Enum.ToObject(targetType, obj);
            }
 
            var typeConverter = TypeDescriptor.GetConverter(objType);
 
            // If the object can be converted to the modelType (eg: double to int)
            if (typeConverter.CanConvertTo(targetType))
            {
                return typeConverter.ConvertTo(obj, targetType);
            }
 
            if (targetType.IsCollection() || targetType.IsArray() || targetType.IsEnumerable())
            {
                return BindCollection(targetType, objType, obj);
            }
 
            return BindObject(targetType, objType, obj);
        }
 
        /// <summary>
        /// Bind collection.
        /// </summary>
        /// <param name="targetType">the target param type.</param>
        /// <param name="objType">Type of the object.</param>
        /// <param name="obj">object to be converted into a model.</param>
        /// <returns>
        /// An object.
        /// </returns>
        protected virtual object BindCollection(Type targetType, Type objType, object obj)
        {
            var collection = obj as ICollection;
 
            if (collection == null)
            {
                return null;
            }
 
            Type genericType = null;
 
            // Make sure it has a generic type
            if (targetType.GetTypeInfo().IsGenericType)
            {
                genericType = targetType.GetGenericArguments().FirstOrDefault();
            }
            else
            {
                var ienumerable = targetType.GetInterfaces().Where(i => i.GetTypeInfo().IsGenericType).FirstOrDefault(i => i.GetGenericTypeDefinition() == typeof(IEnumerable<>));
                genericType = ienumerable == null ? null : ienumerable.GetGenericArguments().FirstOrDefault();
            }
 
            if (genericType == null)
            {
                // If we don't have a generic type then just use object
                genericType = typeof(object);
            }
 
            var modelType = typeof(List<>).MakeGenericType(genericType);
            var model = (IList)Activator.CreateInstance(modelType);
            var list = (IList<object>)obj;
 
            for (var i = 0; i < collection.Count; i++)
            {
                var val = list.ElementAtOrDefault(i);
 
                //Previously we only called bind for IDictionary<string, object>
                // and IList<object>, we now bind for all values to allow for
                // type conversion like int -> double where javascript gives
                // us a mixed array of types, some int, some double (Issue #3129)
                var result = Bind(val, genericType);
                model.Add(result);
            }
 
            if (targetType.IsArray())
            {
                var genericToArrayMethod = ToArrayMethodInfo.MakeGenericMethod(new[] { genericType });
                return genericToArrayMethod.Invoke(null, new[] { model });
            }
 
            return model;
        }
 
        /// <summary>
        /// Bind object.
        /// </summary>
        /// <param name="targetType">the target param type.</param>
        /// <param name="objType">Type of the object.</param>
        /// <param name="obj">object to be converted into a model.</param>
        /// <returns>
        /// An object.
        /// </returns>
        protected virtual object BindObject(Type targetType, Type objType, object obj)
        {
            var model = Activator.CreateInstance(targetType, true);
 
            // If the object type is a dictionary (we're using ExpandoObject instead of Dictionary now)
            // Then attempt to bind all the members
            if (typeof(IDictionary<string, object>).IsAssignableFrom(objType))
            {
                var dictionary = (IDictionary<string, object>)obj;
                var members = BindingMemberInfo.Collect(targetType).ToList();
 
                foreach (var modelProperty in members)
                {
                    object val;
 
                    if (dictionary.TryGetValue(modelProperty.Name, out val))
                    {
                        var propertyVal = Bind(val, modelProperty.Type);
 
                        modelProperty.SetValue(model, propertyVal);
                    }
                }
            }
 
            return model;
        }
    }
}