fa6ff618f6b38c3da6b66863fff6ff3a97c90fb5
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / internal / Xaml / TypeConversionExtensions.cs
1 /*
2  * Copyright(c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 //
18 // TypeConversionExtensions.cs
19 //
20 // Author:
21 //       Stephane Delcroix <stephane@mi8.be>
22 //
23 // Copyright (c) 2013 Mobile Inception
24 // Copyright (c) 2014 Xamarin, Inc.
25 //
26 // Permission is hereby granted, free of charge, to any person obtaining a copy
27 // of this software and associated documentation files (the "Software"), to deal
28 // in the Software without restriction, including without limitation the rights
29 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
30 // copies of the Software, and to permit persons to whom the Software is
31 // furnished to do so, subject to the following conditions:
32 //
33 // The above copyright notice and this permission notice shall be included in
34 // all copies or substantial portions of the Software.
35 //
36 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
37 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
38 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
39 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
40 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
41 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
42 // THE SOFTWARE.
43
44 using System;
45 using System.Collections.Generic;
46 using System.Globalization;
47 using System.Linq;
48 using System.Reflection;
49 // using Tizen.NUI.Binding.Internals;
50 using Tizen.NUI.Xaml.Internals;
51 using Tizen.NUI.Binding;
52
53 namespace Tizen.NUI.Xaml
54 {
55     internal static class TypeConversionExtensions
56     {
57         internal static object ConvertTo(this object value, Type toType, Func<ParameterInfo> pinfoRetriever,
58             IServiceProvider serviceProvider)
59         {
60             Func<TypeConverter> getConverter = () =>
61             {
62                 ParameterInfo pInfo;
63                 if (pinfoRetriever == null || (pInfo = pinfoRetriever()) == null)
64                     return null;
65
66                 var converterTypeName = pInfo.CustomAttributes.GetTypeConverterTypeName();
67                 if (converterTypeName == null)
68                     return null;
69                 var convertertype = Type.GetType(converterTypeName);
70                 return (TypeConverter)Activator.CreateInstance(convertertype);
71             };
72
73             return ConvertTo(value, toType, getConverter, serviceProvider);
74         }
75
76         internal static object ConvertTo(this object value, Type toType, Func<MemberInfo> minfoRetriever,
77             IServiceProvider serviceProvider)
78         {
79             Func<object> getConverter = () =>
80             {
81                 MemberInfo memberInfo;
82                 string converterTypeName = null;
83                 Type realType = toType;
84
85                 if (true == realType.IsGenericType && typeof(Nullable<>) == realType.GetGenericTypeDefinition())
86                 {
87                     realType = realType.GetGenericArguments()[0];
88                 }
89
90                 converterTypeName = realType.CustomAttributes.GetTypeConverterTypeName();
91                 if (minfoRetriever != null && (memberInfo = minfoRetriever()) != null)
92                     converterTypeName = memberInfo.CustomAttributes.GetTypeConverterTypeName() ?? converterTypeName;
93
94                 if (converterTypeName == null)
95                 {
96                     converterTypeName = toType.FullName + "TypeConverter";
97                 }
98
99                 var convertertype = Type.GetType(converterTypeName);
100
101                 if (null == convertertype)
102                 {
103                     return null;
104                 }
105                 else
106                 {
107                     return Activator.CreateInstance(convertertype);
108                 }
109             };
110
111             return ConvertTo(value, toType, getConverter, serviceProvider);
112         }
113
114         static string GetTypeConverterTypeName(this IEnumerable<CustomAttributeData> attributes)
115         {
116             var converterAttribute =
117                 attributes.FirstOrDefault(cad => TypeConverterAttribute.TypeConvertersType.Contains(cad.AttributeType.FullName));
118             if (converterAttribute == null)
119                 return null;
120             if (converterAttribute.ConstructorArguments[0].ArgumentType == typeof(string))
121                 return (string)converterAttribute.ConstructorArguments[0].Value;
122             if (converterAttribute.ConstructorArguments[0].ArgumentType == typeof(Type))
123                 return ((Type)converterAttribute.ConstructorArguments[0].Value).AssemblyQualifiedName;
124             return null;
125         }
126
127         //Don't change the name or the signature of this, it's used by XamlC
128         public static object ConvertTo(this object value, Type toType, Type convertertype, IServiceProvider serviceProvider)
129         {
130             if (convertertype == null)
131                 return value.ConvertTo(toType, (Func<object>)null, serviceProvider);
132             Func<object> getConverter = () => Activator.CreateInstance(convertertype);
133             ;
134             return value.ConvertTo(toType, getConverter, serviceProvider);
135         }
136
137         private delegate void ParseValueFunc(string s, IFormatProvider provider);
138
139         static private Dictionary<Type, ParseValueFunc> typeToParseValueFunc = null;
140
141         static private void BuildParseValueFunc()
142         {
143             if (null == typeToParseValueFunc)
144             {
145                 typeToParseValueFunc = new Dictionary<Type, ParseValueFunc>();
146
147             }
148         }
149
150         internal static object ConvertTo(this object value, Type toType, Func<object> getConverter,
151             IServiceProvider serviceProvider)
152         {
153             if (value == null)
154                 return null;
155
156             var str = value as string;
157             if (str != null)
158             {
159                 //If there's a [TypeConverter], use it
160                 object converter = getConverter?.Invoke();
161                 if (null != converter)
162                 {
163                     var xfTypeConverter = converter as TypeConverter;
164                     var xfExtendedTypeConverter = xfTypeConverter as IExtendedTypeConverter;
165                     if (xfExtendedTypeConverter != null)
166                         return value = xfExtendedTypeConverter.ConvertFromInvariantString(str, serviceProvider);
167                     if (xfTypeConverter != null)
168                         return value = xfTypeConverter.ConvertFromInvariantString(str);
169                     var converterType = converter?.GetType();
170                     if (converterType != null)
171                     {
172                         var convertFromStringInvariant = converterType.GetRuntimeMethod("ConvertFromInvariantString",
173                             new[] { typeof(string) });
174                         if (convertFromStringInvariant != null)
175                             return value = convertFromStringInvariant.Invoke(converter, new object[] { str });
176                     }
177                 }
178
179                 var ignoreCase = (serviceProvider?.GetService(typeof(IConverterOptions)) as IConverterOptions)?.IgnoreCase ?? false;
180
181                 //If the type is nullable, as the value is not null, it's safe to assume we want the built-in conversion
182                 if (toType.GetTypeInfo().IsGenericType && toType.GetGenericTypeDefinition() == typeof(Nullable<>))
183                     toType = Nullable.GetUnderlyingType(toType);
184
185                 //Obvious Built-in conversions
186                 if (toType.GetTypeInfo().IsEnum)
187                     return Enum.Parse(toType, str, ignoreCase);
188
189                 if (toType == typeof(SByte))
190                     return SByte.Parse(str, CultureInfo.InvariantCulture);
191                 if (toType == typeof(Int16))
192                     return Int16.Parse(str, CultureInfo.InvariantCulture);
193                 if (toType == typeof(Int32))
194                     return Int32.Parse(str, CultureInfo.InvariantCulture);
195                 if (toType == typeof(Int64))
196                     return Int64.Parse(str, CultureInfo.InvariantCulture);
197                 if (toType == typeof(Byte))
198                     return Byte.Parse(str, CultureInfo.InvariantCulture);
199                 if (toType == typeof(UInt16))
200                     return UInt16.Parse(str, CultureInfo.InvariantCulture);
201                 if (toType == typeof(UInt32))
202                     return UInt32.Parse(str, CultureInfo.InvariantCulture);
203                 if (toType == typeof(UInt64))
204                     return UInt64.Parse(str, CultureInfo.InvariantCulture);
205                 if (toType == typeof(Single))
206                     return Single.Parse(str, CultureInfo.InvariantCulture);
207                 if (toType == typeof(Double))
208                     return Double.Parse(str, CultureInfo.InvariantCulture);
209                 if (toType == typeof(Boolean))
210                     return Boolean.Parse(str);
211                 if (toType == typeof(TimeSpan))
212                     return TimeSpan.Parse(str, CultureInfo.InvariantCulture);
213                 if (toType == typeof(DateTime))
214                     return DateTime.Parse(str, CultureInfo.InvariantCulture);
215                 if (toType == typeof(Char))
216                 {
217                     char c = '\0';
218                     _ = Char.TryParse(str, out c);
219                     return c;
220                 }
221                 if (toType == typeof(String) && str.StartsWith("{}", StringComparison.Ordinal))
222                     return str.Substring(2);
223                 if (toType == typeof(String))
224                     return value;
225                 if (toType == typeof(Decimal))
226                     return Decimal.Parse(str, CultureInfo.InvariantCulture);
227             }
228
229             //if the value is not assignable and there's an implicit conversion, convert
230             if (value != null && !toType.IsAssignableFrom(value.GetType()))
231             {
232                 var opImplicit = value.GetType().GetImplicitConversionOperator(fromType: value.GetType(), toType: toType)
233                                 ?? toType.GetImplicitConversionOperator(fromType: value.GetType(), toType: toType);
234
235                 if (opImplicit != null)
236                 {
237                     value = opImplicit.Invoke(null, new[] { value });
238                     return value;
239                 }
240             }
241
242             return value;
243         }
244
245         internal static MethodInfo GetImplicitConversionOperator(this Type onType, Type fromType, Type toType)
246         {
247 #if NETSTANDARD1_0
248             var mi = onType.GetRuntimeMethod("op_Implicit", new[] { fromType });
249 #else
250             var bindingFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy;
251             var mi = onType.GetMethod("op_Implicit", bindingFlags, null, new[] { fromType }, null);
252 #endif
253             if (mi == null) return null;
254             if (!mi.IsSpecialName) return null;
255             if (!mi.IsPublic) return null;
256             if (!mi.IsStatic) return null;
257             if (!toType.IsAssignableFrom(mi.ReturnType)) return null;
258
259             return mi;
260         }
261     }
262 }