PageRenderTime 78ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/CompositeC1/Composite/Core/Types/ValueTypeConverter.cs

#
C# | 355 lines | 264 code | 63 blank | 28 comment | 75 complexity | 8ad68bfc70fb2a32f030fa6d0a75a422 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /*
  2. * The contents of this web application are subject to the Mozilla Public License Version
  3. * 1.1 (the "License"); you may not use this web application except in compliance with
  4. * the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/.
  5. *
  6. * Software distributed under the License is distributed on an "AS IS" basis,
  7. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  8. * for the specific language governing rights and limitations under the License.
  9. *
  10. * The Original Code is owned by and the Initial Developer of the Original Code is
  11. * Composite A/S (Danish business reg.no. 21744409). All Rights Reserved
  12. *
  13. * Section 11 of the License is EXPRESSLY amended to include a provision stating
  14. * that any dispute, including but not limited to disputes related to the enforcement
  15. * of the License, to which Composite A/S as owner of the Original Code, as Initial
  16. * Developer or in any other role, becomes a part to shall be governed by Danish law
  17. * and be initiated before the Copenhagen City Court ("K�benhavns Byret")
  18. */
  19. using System;
  20. using System.Collections;
  21. using System.Collections.Generic;
  22. using System.ComponentModel;
  23. using System.Globalization;
  24. using System.Linq;
  25. using System.Reflection;
  26. using System.Xml;
  27. using System.Xml.Linq;
  28. using Composite.C1Console.Users;
  29. using Composite.Core.Extensions;
  30. using Composite.Core.ResourceSystem;
  31. using Composite.Core.Xml;
  32. using Composite.Data;
  33. namespace Composite.Core.Types
  34. {
  35. /// <summary>
  36. /// </summary>
  37. /// <exclude />
  38. [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
  39. public static class ValueTypeConverter
  40. {
  41. private static readonly MethodInfo NewLazyObjectMethodInfo = StaticReflection.GetGenericMethodInfo(() => NewLazyObject<object>(null));
  42. /// <exclude />
  43. public static object Convert(object value, Type targetType)
  44. {
  45. Exception conversionError;
  46. return TryConvert(value, targetType, out conversionError);
  47. }
  48. /// <exclude />
  49. internal static object TryConvert(object value, Type targetType, out Exception conversionError)
  50. {
  51. conversionError = null;
  52. if (value == null)
  53. {
  54. if (!targetType.IsPrimitive)
  55. {
  56. return null;
  57. }
  58. throw new InvalidOperationException(string.Format("Can not convert null to type '{0}'", targetType));
  59. }
  60. if (targetType.IsInstanceOfType(value))
  61. {
  62. return value;
  63. }
  64. if (targetType.IsLazyGenericType() && !value.GetType().IsLazyGenericType())
  65. {
  66. Type genericArgument = targetType.GetGenericArguments()[0];
  67. object convertedValue = TryConvert(value, genericArgument, out conversionError);
  68. return CreateLazyObject(() => convertedValue, genericArgument);
  69. }
  70. var helper = targetType.GetCustomAttributesRecursively<ValueTypeConverterHelperAttribute>().FirstOrDefault();
  71. if (helper != null)
  72. {
  73. object ret;
  74. if (helper.TryConvert(value, targetType, out ret))
  75. {
  76. return ret;
  77. }
  78. }
  79. var helper2 = value.GetType().GetCustomAttributesRecursively<ValueTypeConverterHelperAttribute>().FirstOrDefault();
  80. if (helper2 != null)
  81. {
  82. object ret;
  83. if (helper2.TryConvert(value, targetType, out ret))
  84. {
  85. return ret;
  86. }
  87. }
  88. if (targetType == typeof(object))
  89. {
  90. return value;
  91. }
  92. if ((IsGenericEnumerable(value.GetType())) &&
  93. (IsGenericEnumerable(targetType)))
  94. {
  95. Type targetItemType = targetType.GetGenericArguments()[0];
  96. IList targetValue = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(new [] { targetItemType }));
  97. foreach (object valueItem in ((IEnumerable)value))
  98. {
  99. object convertedValueItem = Convert(valueItem, targetItemType);
  100. targetValue.Add(convertedValueItem);
  101. }
  102. return targetValue;
  103. }
  104. // Haz item, wantz list of it.
  105. if (!IsGenericEnumerable(value.GetType())
  106. && IsGenericEnumerable(targetType))
  107. {
  108. Type targetItemType = targetType.GetGenericArguments()[0];
  109. if (targetItemType.IsInstanceOfType(value))
  110. {
  111. IList targetValue = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(new [] { targetItemType }));
  112. targetValue.Add(value);
  113. return targetValue;
  114. }
  115. }
  116. var stringValue = value as string;
  117. if (stringValue != null)
  118. {
  119. return TryConvertStringValue(stringValue, targetType, ref conversionError);
  120. }
  121. if (targetType == typeof(string))
  122. {
  123. if (value is Type) return TypeManager.SerializeType((Type)value);
  124. if (value is DateTime) return XmlConvert.ToString((DateTime)value, XmlDateTimeSerializationMode.Local);
  125. }
  126. TypeConverter targetConverter = TypeDescriptor.GetConverter(targetType);
  127. if (targetConverter.CanConvertFrom(value.GetType()))
  128. {
  129. return targetConverter.ConvertFrom(null, UserSettings.CultureInfo, value);
  130. }
  131. TypeConverter valueConverter = TypeDescriptor.GetConverter(value.GetType());
  132. if (valueConverter.CanConvertTo(targetType))
  133. {
  134. return valueConverter.ConvertTo(null, UserSettings.CultureInfo, value, targetType);
  135. }
  136. throw new InvalidOperationException(string.Format("No conversion from {0} to {1} could be found", value.GetType(), targetType));
  137. }
  138. private static object TryConvertStringValue(string stringValue, Type targetType, ref Exception conversionError)
  139. {
  140. if (targetType == typeof (Type))
  141. {
  142. return stringValue != string.Empty ? TypeManager.GetType(stringValue) : null;
  143. }
  144. if (targetType == typeof (XhtmlDocument))
  145. {
  146. if (stringValue == string.Empty)
  147. {
  148. return new XhtmlDocument();
  149. }
  150. return XhtmlDocument.Parse(stringValue);
  151. }
  152. if (targetType == typeof (XDocument))
  153. {
  154. return XDocument.Parse(stringValue);
  155. }
  156. if (targetType == typeof (XElement))
  157. {
  158. return XElement.Parse(stringValue);
  159. }
  160. if (targetType == typeof (IEnumerable<XNode>))
  161. {
  162. try
  163. {
  164. XElement wrapper = XElement.Parse(string.Format("<wrapper>{0}</wrapper>", stringValue));
  165. return wrapper.Nodes();
  166. }
  167. catch
  168. {
  169. throw new InvalidCastException(string.Format("Unable to convert string '{0}' to a list of XNodes.", stringValue));
  170. }
  171. }
  172. if (targetType == typeof (IEnumerable<XElement>))
  173. {
  174. try
  175. {
  176. XElement wrapper = XElement.Parse(string.Format("<wrapper>{0}</wrapper>", stringValue));
  177. return wrapper.Elements();
  178. }
  179. catch
  180. {
  181. throw new InvalidCastException(string.Format("Unable to convert string '{0}' to a list of XElements.", stringValue));
  182. }
  183. }
  184. if (targetType == typeof (XNamespace))
  185. {
  186. return XNamespace.Get(stringValue);
  187. }
  188. if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof (Nullable<>))
  189. {
  190. Type valueType = targetType.GetGenericArguments()[0];
  191. if (IsOneOfTheHandledValueTypes(valueType))
  192. {
  193. if (stringValue.Trim().Length == 0)
  194. {
  195. return null;
  196. }
  197. return TryConvertValueType(stringValue, valueType, out conversionError);
  198. }
  199. }
  200. if (IsOneOfTheHandledValueTypes(targetType))
  201. {
  202. return TryConvertValueType(stringValue, targetType, out conversionError);
  203. }
  204. TypeConverter tc = TypeDescriptor.GetConverter(targetType);
  205. CultureInfo culture = LocalizationScopeManager.CurrentLocalizationScope;
  206. object convertedResult = tc.ConvertFromString(null, culture, stringValue);
  207. if (convertedResult == null && !string.IsNullOrEmpty(stringValue))
  208. {
  209. throw new InvalidOperationException(string.Format("Unable to convert string value '{0}' to type '{1}'", stringValue, targetType.FullName));
  210. }
  211. return convertedResult;
  212. }
  213. private static object CreateLazyObject(Func<object> func, Type type)
  214. {
  215. return NewLazyObjectMethodInfo.MakeGenericMethod(type).Invoke(null, new object[] { func });
  216. }
  217. private static Lazy<T> NewLazyObject<T>(Func<object> func)
  218. {
  219. return new Lazy<T>(() => (T)func(), true);
  220. }
  221. private static bool IsOneOfTheHandledValueTypes(Type type)
  222. {
  223. return type == typeof (bool) || type == typeof (Guid) || type == typeof (int) || type == typeof (Decimal);
  224. }
  225. private static object TryConvertValueType(string stringValue, Type targetType, out Exception conversionError)
  226. {
  227. conversionError = null;
  228. if (targetType == typeof(bool))
  229. {
  230. bool boolResult;
  231. if (!bool.TryParse(stringValue, out boolResult))
  232. {
  233. boolResult = false;
  234. // TODO: localize
  235. conversionError = new InvalidOperationException("Failed to convert value '{0}' into a Boolean".FormatWith(stringValue));
  236. }
  237. return boolResult;
  238. }
  239. if (targetType == typeof(int))
  240. {
  241. int intResult = 0;
  242. try
  243. {
  244. intResult = Int32.Parse(stringValue);
  245. }
  246. catch(OverflowException)
  247. {
  248. conversionError = new InvalidOperationException(LocalizationFiles.Composite_Management.Validation_Int32_Overflow);
  249. }
  250. catch (Exception ex)
  251. {
  252. conversionError = ex;
  253. }
  254. return intResult;
  255. }
  256. if (targetType == typeof(decimal))
  257. {
  258. // TODO: localize
  259. decimal decimalResult;
  260. if (!decimal.TryParse(stringValue, out decimalResult))
  261. {
  262. conversionError = new InvalidOperationException("Failed to convert value '{0}' into a Decimal".FormatWith(stringValue));
  263. }
  264. return decimalResult;
  265. }
  266. if (targetType == typeof(Guid))
  267. {
  268. Guid guidResult = Guid.Empty;
  269. if (string.IsNullOrEmpty(stringValue) || !Guid.TryParse(stringValue, out guidResult))
  270. {
  271. // TODO: localize
  272. conversionError = new InvalidOperationException("Failed to convert value '{0}' into a Guid".FormatWith(stringValue));
  273. }
  274. return guidResult;
  275. }
  276. throw new NotImplementedException("Supported types should be defined in IsOneOfTheHandledValueTypes() method");
  277. }
  278. /// <exclude />
  279. public static T Convert<T>(object value)
  280. {
  281. return (T)Convert(value, typeof(T));
  282. }
  283. private static bool IsGenericEnumerable(Type type)
  284. {
  285. if (!type.IsGenericType) return false;
  286. type = type.GetGenericTypeDefinition();
  287. return typeof (IEnumerable<>).IsAssignableFrom(type)
  288. || typeof (List<>).IsAssignableFrom(type)
  289. || typeof (IList<>).IsAssignableFrom(type);
  290. }
  291. }
  292. }