/// <summary> /// Processes the value. /// </summary> /// <returns> /// The <see cref="object" /> representing the processed value. /// </returns> public override object ProcessValue() { object result = this.Value; var propertyType = this.Context.PropertyDescriptor.PropertyType; var propertyIsEnumerableType = Direction == EnumerableConvertionDirection.Automatic ? propertyType.IsEnumerableType() && !(propertyType == typeof(string)) : Direction == EnumerableConvertionDirection.ToEnumerable; if (this.Value != null) { var valueType = this.Value.GetType(); var valueIsEnumerableType = valueType.IsEnumerableType() && !(this.Value is string); if (propertyIsEnumerableType) { if (!valueIsEnumerableType) { // Property is enumerable, but value isn't, so make enumerable var arr = Array.CreateInstance(valueType, 1); arr.SetValue(this.Value, 0); result = arr; } } else { if (valueIsEnumerableType) { // Property is not enumerable, but value is, so grab first item var enumerator = ((IEnumerable)this.Value).GetEnumerator(); result = enumerator.MoveNext() ? enumerator.Current : null; } } } else { if (propertyIsEnumerableType) { if (propertyType.IsInterface && !propertyType.IsEnumerableOfKeyValueType()) { // Value is null, but property is enumerable interface, so return empty enumerable result = EnumerableInvocations.Empty(propertyType.GenericTypeArguments.First()); } else { // Concrete enumerables cannot be cast from Array so we create an instance and return it // if we know it has an empty constructor. var constructorParams = propertyType.GetConstructorParameters(); if (constructorParams != null && constructorParams.Length == 0) { // Internally this uses Activator.CreateInstance which is heavily optimized. result = propertyType.GetInstance(); } } } } return(result); }
/// <summary> /// Processes the value. /// </summary> /// <returns> /// The <see cref="object" /> representing the processed value. /// </returns> /// <exception cref="System.NotImplementedException"></exception> public override object ProcessValue() { object result = Value; var propertyIsEnumerableType = Context.PropertyDescriptor.PropertyType.IsEnumerableType() && !Context.PropertyDescriptor.PropertyType.IsEnumerableOfKeyValueType() && !(Context.PropertyDescriptor.PropertyType == typeof(string)); if (Value != null) { var valueIsEnumerableType = Value.GetType().IsEnumerableType() && !Value.GetType().IsEnumerableOfKeyValueType() && !(Value is string); if (propertyIsEnumerableType) { if (!valueIsEnumerableType) { // Property is enumerable, but value isn't, so make enumerable var arr = Array.CreateInstance(Value.GetType(), 1); arr.SetValue(Value, 0); result = arr; } } else { if (valueIsEnumerableType) { // Property is not enumerable, but value is, so grab first item var enumerator = ((IEnumerable)Value).GetEnumerator(); result = enumerator.MoveNext() ? enumerator.Current : null; } } } else { if (propertyIsEnumerableType) { // Value is null, but property is enumerable, so return empty enumerable result = EnumerableInvocations.Empty(Context.PropertyDescriptor.PropertyType.GenericTypeArguments.First()); } } return(result); }
/// <summary> /// Converts the given object to the type of this converter, using the specified context and culture information. /// </summary> /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext" /> that provides a format context.</param> /// <param name="culture">The <see cref="T:System.Globalization.CultureInfo" /> to use as the current culture.</param> /// <param name="value">The <see cref="T:System.Object" /> to convert.</param> /// <returns> /// An <see cref="T:System.Object" /> that represents the converted value. /// </returns> public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { // ReSharper disable once ConditionIsAlwaysTrueOrFalse if (context == null || context.PropertyDescriptor == null) { return(Enumerable.Empty <object>()); } var propertyType = context.PropertyDescriptor.PropertyType; var isGenericType = propertyType.IsGenericType; var targetType = isGenericType ? propertyType.GenericTypeArguments.First() : propertyType; if (value.IsNullOrEmptyString()) { return(EnumerableInvocations.Empty(targetType)); } // DictionaryPublishedContent IPublishedContent content = value as IPublishedContent; if (content != null) { // Use the id so we get folder sanitation. return(this.ConvertMediaFromInt(content.Id, targetType, culture)); } // If a single item is selected, this is passed as an int, not a string. if (value is int) { var id = (int)value; return(this.ConvertMediaFromInt(id, targetType, culture).YieldSingleItem()); } var s = value as string; if (!string.IsNullOrWhiteSpace(s)) { var multiMediaPicker = EnumerableInvocations.Empty(targetType); int n; var nodeIds = XmlHelper.CouldItBeXml(s) ? s.GetXmlIds() : s .ToDelimitedList() .Select(x => int.TryParse(x, NumberStyles.Any, culture, out n) ? n : -1) .Where(x => x > 0) .ToArray(); if (nodeIds.Any()) { multiMediaPicker = nodeIds.ForEach(i => this.ConvertMediaFromInt(i, targetType, culture)); } return(multiMediaPicker); } return(base.ConvertFrom(context, culture, value)); }
/// <summary> /// Set the typed value to the given instance. /// </summary> /// <param name="content">The <see cref="IPublishedContent"/> to convert.</param> /// <param name="culture">The <see cref="CultureInfo"/></param> /// <param name="propertyInfo">The <see cref="PropertyInfo"/> property info associated with the type.</param> /// <param name="propertyValue">The property value.</param> /// <param name="instance">The instance to assign the value to.</param> /// <returns>The strong typed converted value for the given property.</returns> private static object GetConvertedValue( IPublishedContent content, CultureInfo culture, PropertyInfo propertyInfo, object propertyValue, object instance) { // Process the value. object result = null; var propertyType = propertyInfo.PropertyType; var typeInfo = propertyType.GetTypeInfo(); // This should return false against typeof(string) etc also. var propertyIsEnumerableType = propertyType.IsCastableEnumerableType(); // Try any custom type converters first. // 1: Check the property. // 2: Check any type arguments in generic enumerable types. // 3: Check the type itself. var converterAttribute = propertyInfo.GetCustomAttribute <TypeConverterAttribute>() ?? (propertyIsEnumerableType ? typeInfo.GenericTypeArguments.First().GetCustomAttribute <TypeConverterAttribute>(true) : propertyType.GetCustomAttribute <TypeConverterAttribute>(true)); if (converterAttribute != null && converterAttribute.ConverterTypeName != null) { // Time custom conversions. using (DittoDisposableTimer.DebugDuration <object>(string.Format("Custom TypeConverter ({0}, {1})", content.Id, propertyInfo.Name))) { // Get the custom converter from the attribute and attempt to convert. var converterType = Type.GetType(converterAttribute.ConverterTypeName); if (converterType != null) { var converter = converterType.GetDependencyResolvedInstance() as TypeConverter; if (converter != null) { // Create context to pass to converter implementations. // This contains the IPublishedContent and the currently converting property descriptor. var descriptor = TypeDescriptor.GetProperties(instance)[propertyInfo.Name]; var context = new DittoTypeConverterContext { ConversionType = instance.GetType(), Instance = content, PropertyDescriptor = descriptor }; Type propertyValueType = null; if (propertyValue != null) { propertyValueType = propertyValue.GetType(); } // We're deliberately passing null. // ReSharper disable once AssignNullToNotNullAttribute if (converter.CanConvertFrom(context, propertyValueType)) { object converted = converter.ConvertFrom(context, culture, propertyValue); if (converted != null) { // Handle Typeconverters returning single objects when we want an IEnumerable. // Use case: Someone selects a folder of images rather than a single image with the media picker. var convertedType = converted.GetType(); if (propertyIsEnumerableType) { var parameterType = typeInfo.GenericTypeArguments.First(); // Some converters return an IEnumerable so we check again. if (!convertedType.IsEnumerableType()) { // Using 'Cast' to convert the type back to IEnumerable<T>. object enumerablePropertyValue = EnumerableInvocations.Cast( parameterType, converted.YieldSingleItem()); result = enumerablePropertyValue; } else { // Nothing is strong typed anymore. result = EnumerableInvocations.Cast(parameterType, (IEnumerable)converted); } } else { // Return single expected items from converters returning an IEnumerable. // Check for key/value pairs and strings. if (convertedType.IsEnumerableType() && !convertedType.IsEnumerableOfKeyValueType() && !(convertedType == typeof(string) && propertyType == typeof(string))) { // Use 'FirstOrDefault' to convert the type back to T. result = EnumerableInvocations.FirstOrDefault( propertyType, (IEnumerable)converted); } else { result = converted; } } } else { // Ensure we pass back an empty enumerable if the expected output is an enumerable. // and null has been returned by the type converter. if (propertyIsEnumerableType) { result = EnumerableInvocations.Empty(typeInfo.GenericTypeArguments.First()); } } } else if (propertyType.IsInstanceOfType(propertyValue)) { // If the TypeConverter's `CanConvertFrom` has returned false, // then we can check if the value is the same type as the target type. result = propertyValue; } } } } } else if (propertyType == typeof(HtmlString)) { // Handle Html strings so we don't have to set the attribute. var converterType = typeof(DittoHtmlStringConverter); var converter = converterType.GetDependencyResolvedInstance() as TypeConverter; if (converter != null) { // This contains the IPublishedContent and the currently converting property descriptor. var descriptor = TypeDescriptor.GetProperties(instance)[propertyInfo.Name]; var context = new DittoTypeConverterContext { ConversionType = instance.GetType(), Instance = content, PropertyDescriptor = descriptor }; Type propertyValueType = null; if (propertyValue != null) { propertyValueType = propertyValue.GetType(); } // We're deliberately passing null. // ReSharper disable once AssignNullToNotNullAttribute if (converter.CanConvertFrom(context, propertyValueType)) { result = converter.ConvertFrom(context, culture, propertyValue); } } } else if (propertyType.IsInstanceOfType(propertyValue)) { // Simple types result = propertyValue; } else if (propertyValue is IPublishedContent && propertyType.IsClass) { // If the property value is an IPublishedContent, then we can use Ditto to map to the target type. result = ((IPublishedContent)propertyValue).As(propertyType); } else if (propertyValue != null && propertyValue.GetType().IsEnumerableOfType(typeof(IPublishedContent)) && propertyType.IsEnumerable() && propertyType.GetEnumerableType() != null && propertyType.GetEnumerableType().IsClass) { // If the property value is IEnumerable<IPublishedContent>, then we can use Ditto to map to the target type. result = ((IEnumerable <IPublishedContent>)propertyValue).As(propertyType.GetEnumerableType()); } else { using (DittoDisposableTimer.DebugDuration <object>(string.Format("TypeConverter ({0}, {1})", content.Id, propertyInfo.Name))) { var convert = propertyValue.TryConvertTo(propertyType); if (convert.Success) { result = convert.Result; } } } return(result); }
/// <summary> /// Converts the given object to the type of this converter, using the specified context and culture information. /// </summary> /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext" /> that provides a format context.</param> /// <param name="culture">The <see cref="T:System.Globalization.CultureInfo" /> to use as the current culture.</param> /// <param name="value">The <see cref="T:System.Object" /> to convert.</param> /// <returns> /// An <see cref="T:System.Object" /> that represents the converted value. /// </returns> public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { // ReSharper disable once ConditionIsAlwaysTrueOrFalse if (context == null || context.PropertyDescriptor == null) { // There's no way to determine the type here. return(null); } var propertyType = context.PropertyDescriptor.PropertyType; var isGenericType = propertyType.IsGenericType; var targetType = isGenericType ? propertyType.GenericTypeArguments.First() : propertyType; if (value.IsNullOrEmptyString()) { if (isGenericType) { return(EnumerableInvocations.Empty(targetType)); } return(null); } // If a single item is selected, this comes back as an int, not a string. if (value is int) { var id = (int)value; // CheckBoxList, ListBox if (targetType != null) { return(this.ConvertContentFromInt(id, targetType, culture).YieldSingleItem()); } // AutoComplete, DropDownList, RadioButton return(this.ConvertContentFromInt(id, propertyType, culture)); } if (value != null) { string s = value as string ?? value.ToString(); if (!string.IsNullOrWhiteSpace(s)) { int n; var nodeIds = s .ToDelimitedList() .Select(x => int.TryParse(x, NumberStyles.Any, culture, out n) ? n : -1) .Where(x => x > 0) .ToArray(); if (nodeIds.Any()) { var ultimatePicker = new List <IPublishedContent>(); // ReSharper disable once LoopCanBeConvertedToQuery foreach (var nodeId in nodeIds) { var item = UmbracoContext.Current.ContentCache.GetById(nodeId); if (item != null) { ultimatePicker.Add(item); } } // CheckBoxList, ListBox if (isGenericType) { return(ultimatePicker.As(targetType, culture)); } // AutoComplete, DropDownList, RadioButton return(ultimatePicker.As(targetType, culture).FirstOrDefault()); } } } return(base.ConvertFrom(context, culture, value)); }
/// <summary> /// Converts the given object to the type of this converter, using the specified context and culture information. /// </summary> /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext" /> that provides a format context.</param> /// <param name="culture">The <see cref="T:System.Globalization.CultureInfo" /> to use as the current culture.</param> /// <param name="value">The <see cref="T:System.Object" /> to convert.</param> /// <returns> /// An <see cref="T:System.Object" /> that represents the converted value. /// </returns> public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { // ReSharper disable once ConditionIsAlwaysTrueOrFalse if (context == null || context.PropertyDescriptor == null) { return(Enumerable.Empty <object>()); } var propertyType = context.PropertyDescriptor.PropertyType; var isGenericType = propertyType.IsGenericType; var targetType = isGenericType ? propertyType.GenericTypeArguments.First() : propertyType; if (value.IsNullOrEmptyString()) { return(EnumerableInvocations.Empty(targetType)); } // Single IPublishedContent IPublishedContent content = value as IPublishedContent; if (content != null) { return(content.As(targetType, null, null, culture)); } // ReSharper disable once PossibleNullReferenceException var type = value.GetType(); // Multiple IPublishedContent if (type.IsEnumerableOfType(typeof(IPublishedContent))) { return(((IEnumerable <IPublishedContent>)value).As(targetType, null, null, null, culture)); } int[] nodeIds = { }; // First try enumerable strings, ints. if (type.IsGenericType || type.IsArray) { if (type.IsEnumerableOfType(typeof(string))) { int n; nodeIds = ((IEnumerable <string>)value) .Select(x => int.TryParse(x, NumberStyles.Any, culture, out n) ? n : -1) .ToArray(); } if (type.IsEnumerableOfType(typeof(int))) { nodeIds = ((IEnumerable <int>)value).ToArray(); } } // Now csv strings. if (!nodeIds.Any()) { var s = value as string ?? value.ToString(); if (!string.IsNullOrWhiteSpace(s)) { int n; nodeIds = XmlHelper.CouldItBeXml(s) ? s.GetXmlIds() : s .ToDelimitedList() .Select(x => int.TryParse(x, NumberStyles.Any, culture, out n) ? n : -1) .Where(x => x > 0) .ToArray(); } } if (nodeIds.Any()) { var umbracoContext = UmbracoContext.Current; var membershipHelper = new MembershipHelper(umbracoContext); var objectType = UmbracoObjectTypes.Unknown; var multiPicker = new List <IPublishedContent>(); // Oh so ugly if you let Resharper do this. // ReSharper disable once LoopCanBeConvertedToQuery foreach (var nodeId in nodeIds) { var item = this.GetPublishedContent(nodeId, ref objectType, UmbracoObjectTypes.Document, umbracoContext.ContentCache.GetById) ?? this.GetPublishedContent(nodeId, ref objectType, UmbracoObjectTypes.Media, umbracoContext.MediaCache.GetById) ?? this.GetPublishedContent(nodeId, ref objectType, UmbracoObjectTypes.Member, membershipHelper.GetById); if (item != null) { multiPicker.Add(item); } } return(multiPicker.As(targetType, null, null, null, culture)); } return(base.ConvertFrom(context, culture, value)); }