/// <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>
        /// Gets a collection of the given type from the given <see cref="IEnumerable{IPublishedContent}"/>.
        /// </summary>
        /// <param name="items">
        /// The <see cref="IEnumerable{IPublishedContent}"/> to convert.
        /// </param>
        /// <param name="type">
        /// The <see cref="Type"/> of items to return.
        /// </param>
        /// <param name="documentTypeAlias">
        /// The document type alias.
        /// </param>
        /// <param name="convertingType">
        /// The <see cref="Action{ConvertingTypeEventArgs}"/> to fire when converting.
        /// </param>
        /// <param name="convertedType">
        /// The <see cref="Action{ConvertedTypeEventArgs}"/> to fire when converted.
        /// </param>
        /// <param name="culture">
        /// The <see cref="CultureInfo"/>.
        /// </param>
        /// <returns>
        /// The resolved <see cref="IEnumerable{T}"/>.
        /// </returns>
        public static IEnumerable <object> As(
            this IEnumerable <IPublishedContent> items,
            Type type,
            string documentTypeAlias = null,
            Action <ConvertingTypeEventArgs> convertingType = null,
            Action <ConvertedTypeEventArgs> convertedType   = null,
            CultureInfo culture = null)
        {
            using (DisposableTimer.DebugDuration <IEnumerable <object> >(string.Format("IEnumerable As ({0})", documentTypeAlias)))
            {
                IEnumerable <object> typedItems;
                if (string.IsNullOrWhiteSpace(documentTypeAlias))
                {
                    typedItems = items.Select(x => x.As(type, convertingType, convertedType, culture));
                }
                else
                {
                    typedItems = items.Where(x => documentTypeAlias.InvariantEquals(x.DocumentTypeAlias))
                                 .Select(x => x.As(type, convertingType, convertedType, culture));
                }

                // We need to cast back here as nothing is strong typed anymore.
                return((IEnumerable <object>)EnumerableInvocations.Cast(type, typedItems));
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Gets a collection of the given type from the given <see cref="IEnumerable{IPublishedContent}"/>.
        /// </summary>
        /// <param name="items">
        /// The <see cref="IEnumerable{IPublishedContent}"/> to convert.
        /// </param>
        /// <param name="type">
        /// The <see cref="Type"/> of items to return.
        /// </param>
        /// <param name="culture">
        /// The <see cref="CultureInfo"/>.
        /// </param>
        /// <param name="valueResolverContexts">
        /// A collection of <see cref="DittoValueResolverContext"/> entities to use whilst resolving values.
        /// </param>
        /// <param name="onConverting">
        /// The <see cref="Action{ConversionHandlerContext}"/> to fire when converting.
        /// </param>
        /// <param name="onConverted">
        /// The <see cref="Action{ConversionHandlerContext}"/> to fire when converted.
        /// </param>
        /// <returns>
        /// The resolved <see cref="IEnumerable{T}"/>.
        /// </returns>
        public static IEnumerable <object> As(
            this IEnumerable <IPublishedContent> items,
            Type type,
            CultureInfo culture = null,
            IEnumerable <DittoValueResolverContext> valueResolverContexts = null,
            Action <DittoConversionHandlerContext> onConverting           = null,
            Action <DittoConversionHandlerContext> onConverted            = null)
        {
            using (DittoDisposableTimer.DebugDuration <IEnumerable <object> >("IEnumerable As"))
            {
                var typedItems = items.Select(x => x.As(type, culture, null, valueResolverContexts, onConverting, onConverted));

                // We need to cast back here as nothing is strong typed anymore.
                return((IEnumerable <object>)EnumerableInvocations.Cast(type, typedItems));
            }
        }
        /// <summary>
        /// Gets a collection of the given type from the given <see cref="IEnumerable{IPublishedContent}"/>.
        /// </summary>
        /// <param name="items">
        /// The <see cref="IEnumerable{IPublishedContent}"/> to convert.
        /// </param>
        /// <param name="type">
        /// The <see cref="Type"/> of items to return.
        /// </param>
        /// <param name="culture">
        /// The <see cref="CultureInfo"/>.
        /// </param>
        /// <param name="processorContexts">
        /// A collection of <see cref="DittoProcessorContext"/> entities to use whilst processing values.
        /// </param>
        /// <param name="onConverting">
        /// The <see cref="Action{ConversionHandlerContext}"/> to fire when converting.
        /// </param>
        /// <param name="onConverted">
        /// The <see cref="Action{ConversionHandlerContext}"/> to fire when converted.
        /// </param>
        /// <param name="chainContext">
        /// The <see cref="DittoChainContext"/> for the current processor chain.
        /// </param>
        /// <returns>
        /// The converted <see cref="IEnumerable{T}"/>.
        /// </returns>
        public static IEnumerable <object> As(
            this IEnumerable <IPublishedContent> items,
            Type type,
            CultureInfo culture = null,
            IEnumerable <DittoProcessorContext> processorContexts = null,
            Action <DittoConversionHandlerContext> onConverting   = null,
            Action <DittoConversionHandlerContext> onConverted    = null,
            DittoChainContext chainContext = null)
        {
            using (DittoDisposableTimer.DebugDuration <Ditto>($"IEnumerable As<{type.Name}>"))
            {
                var typedItems = items.Select(x => x.As(type, culture, null, processorContexts, onConverting, onConverted, chainContext));

                // We need to cast back here as nothing is strong typed anymore.
                return((IEnumerable <object>)EnumerableInvocations.Cast(type, typedItems));
            }
        }
        /// <summary>Returns the processed value for the given type and property.</summary>
        /// <param name="content">The content.</param>
        /// <param name="mappableProperty">Information about the mappable property.</param>
        /// <param name="baseProcessorContext">The base processor context.</param>
        /// <param name="chainContext">The <see cref="DittoChainContext"/> for the current processor chain.</param>
        /// <returns>Returns the processed value.</returns>
        private static object DoGetProcessedValue(
            IPublishedContent content,
            DittoTypeInfo.DittoTypePropertyInfo mappableProperty,
            DittoProcessorContext baseProcessorContext,
            DittoChainContext chainContext)
        {
            // Create holder for value as it's processed
            object currentValue = content;

            // Process attributes
            foreach (var processorAttr in mappableProperty.Processors)
            {
                using (DittoDisposableTimer.DebugDuration(typeof(Ditto), $"Processor '{processorAttr}' ({content.Id})"))
                {
                    // Get the right context type
                    var ctx = chainContext.ProcessorContexts.GetOrCreate(baseProcessorContext, processorAttr.ContextType);

                    // Populate UmbracoContext & ApplicationContext
                    processorAttr.UmbracoContext     = ContextAccessor.UmbracoContext;
                    processorAttr.ApplicationContext = ContextAccessor.ApplicationContext;

                    // Process value
                    currentValue = processorAttr.ProcessValue(currentValue, ctx, chainContext);
                }
            }

            // The following has to happen after all the processors.
            if (mappableProperty.IsEnumerable && currentValue != null && currentValue.Equals(Enumerable.Empty <object>()))
            {
                if (mappableProperty.PropertyInfo.PropertyType.IsInterface)
                {
                    // You cannot set an enumerable of type from an empty object array.
                    currentValue = EnumerableInvocations.Cast(mappableProperty.EnumerableType, (IEnumerable)currentValue);
                }
                else
                {
                    // This should allow the casting back of IEnumerable<T> to an empty List<T> Collection<T> etc.
                    // I cant think of any that don't have an empty constructor
                    currentValue = mappableProperty.PropertyInfo.PropertyType.GetInstance();
                }
            }

            return(currentValue == null && mappableProperty.PropertyInfo.PropertyType.IsValueType
                ? mappableProperty.PropertyInfo.PropertyType.GetInstance() // Set to default instance of value type
                : currentValue);
        }
        /// <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);
        }
Esempio n. 7
0
        /// <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));
        }
Esempio n. 8
0
        /// <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>
        /// Processes the value.
        /// </summary>
        /// <returns>
        /// The <see cref="object" /> representing the processed value.
        /// </returns>
        public override object ProcessValue()
        {
            var propType             = this.Context.PropertyDescriptor.PropertyType;
            var propTypeIsEnumerable = propType.IsEnumerableType();
            var baseType             = propTypeIsEnumerable ? propType.GetEnumerableType() : propType;

            // We have an enumerable processor that runs at the end of every conversion
            // converting individual instances to IEnumerables and vica versa, so we
            // won't worry about returning in the right way, rather we'll just ensure
            // that the IPublishedContent's are converted to the right types
            // and let the enumerable processor handle the rest.

            // TODO: Validate the base type more?

            // Get the list of types to search through
            // If we have explicitly set a list of allowed types, just use those
            // otherwise attempt to search through loaded assemblies
            IEnumerable <Type> types;

            if (AllowedTypes != null && AllowedTypes.Length > 0)
            {
                types = AllowedTypes;
            }
            else
            {
                types = (IEnumerable <Type>)ApplicationContext.ApplicationCache.StaticCache.GetCacheItem(
                    $"DittoFactoryAttribute_ResolveTypes_{baseType.AssemblyQualifiedName}",
                    () =>
                {
                    // Workaround for http://issues.umbraco.org/issue/U4-9011
                    if (baseType.Assembly.IsAppCodeAssembly())
                    {
                        // This logic is taken from the core type finder so it should be performing the same checks.
                        return(baseType.Assembly
                               .GetTypes()
                               .Where(t => baseType.IsAssignableFrom(t) &&
                                      t.IsClass &&
                                      !t.IsAbstract &&
                                      !t.IsSealed &&
                                      !t.IsNestedPrivate &&
                                      t.GetCustomAttribute <HideFromTypeFinderAttribute>(true) == null)
                               .ToArray());
                    }

                    // Find the appropriate types
                    // There is no non generic version of ResolveTypes so we have to
                    // call it via reflection.
                    var method  = typeof(PluginManager).GetMethod("ResolveTypes");
                    var generic = method.MakeGenericMethod(baseType);
                    return(((IEnumerable <Type>)generic.Invoke(PluginManager.Current, new object[] { true, null })).ToArray());
                });
            }

            // Check for IEnumerable<IPublishedContent> value
            if (this.Value is IEnumerable <IPublishedContent> enumerableValue && enumerableValue != null)
            {
                var items = enumerableValue.Select(x =>
                {
                    var typeName = this.ResolveTypeName(x);
                    var type     = types.FirstOrDefault(y => y.Name.InvariantEquals(typeName));

                    return(type != null ? x.As(type, chainContext: ChainContext) : null);
                });

                return(EnumerableInvocations.Cast(baseType, items));
            }

            // Check for IPublishedContent value
            if (this.Value is IPublishedContent ipublishedContentValue && ipublishedContentValue != null)
            {
                var typeName = this.ResolveTypeName(ipublishedContentValue);
                var type     = types.FirstOrDefault(y => y.Name.InvariantEquals(typeName));
                return(type != null
                    ? ipublishedContentValue.As(type, chainContext : ChainContext)
                    : null);
            }

            // No other possible options
            return(null);
        }
        /// <summary>
        /// Returns the processed value for the given type and property.
        /// </summary>
        /// <param name="content">The content.</param>
        /// <param name="propertyInfo">The property information.</param>
        /// <param name="contextAccessor">The context accessor.</param>
        /// <param name="baseProcessorContext">The base processor context.</param>
        /// <param name="chainContext">The <see cref="DittoChainContext"/> for the current processor chain.</param>
        /// <returns>Returns the processed value.</returns>
        private static object DoGetProcessedValue(
            IPublishedContent content,
            PropertyInfo propertyInfo,
            IDittoContextAccessor contextAccessor,
            DittoProcessorContext baseProcessorContext,
            DittoChainContext chainContext)
        {
            // Check the property for any explicit processor attributes
            var processorAttrs = propertyInfo.GetCustomAttributes <DittoProcessorAttribute>(true)
                                 .OrderBy(x => x.Order)
                                 .ToList();

            if (!processorAttrs.Any())
            {
                // Adds the default processor for this conversion
                processorAttrs.Add(DittoProcessorRegistry.Instance.GetDefaultProcessorFor(baseProcessorContext.TargetType));
            }

            var propertyType = propertyInfo.PropertyType;

            // Check for type registered processors
            processorAttrs.AddRange(propertyType
                                    .GetCustomAttributes <DittoProcessorAttribute>(true)
                                    .OrderBy(x => x.Order));

            // Check any type arguments in generic enumerable types.
            // This should return false against typeof(string) etc also.
            var  typeInfo     = propertyType.GetTypeInfo();
            bool isEnumerable = false;
            Type typeArg      = null;

            if (propertyType.IsCastableEnumerableType())
            {
                typeArg = typeInfo.GenericTypeArguments.First();
                processorAttrs.AddRange(typeInfo
                                        .GenericTypeArguments
                                        .First()
                                        .GetCustomAttributes <DittoProcessorAttribute>(true)
                                        .OrderBy(x => x.Order)
                                        .ToList());

                isEnumerable = true;
            }

            // Check for globally registered processors
            processorAttrs.AddRange(DittoProcessorRegistry.Instance.GetRegisteredProcessorAttributesFor(propertyInfo.PropertyType));

            // Add any core processors onto the end
            processorAttrs.AddRange(DittoProcessorRegistry.Instance.GetPostProcessorAttributes());

            // Create holder for value as it's processed
            object currentValue = content;

            // Process attributes
            foreach (var processorAttr in processorAttrs)
            {
                using (DittoDisposableTimer.DebugDuration <Ditto>($"Processor '{processorAttr.GetType().Name}' ({content.Id})"))
                {
                    // Get the right context type
                    var ctx = chainContext.ProcessorContexts.GetOrCreate(baseProcessorContext, processorAttr.ContextType);

                    // Populate UmbracoContext & ApplicationContext
                    processorAttr.UmbracoContext     = contextAccessor.UmbracoContext;
                    processorAttr.ApplicationContext = contextAccessor.ApplicationContext;

                    // Process value
                    currentValue = processorAttr.ProcessValue(currentValue, ctx, chainContext);
                }
            }

            // The following has to happen after all the processors.
            if (isEnumerable && currentValue != null && currentValue.Equals(Enumerable.Empty <object>()))
            {
                if (propertyType.IsInterface)
                {
                    // You cannot set an enumerable of type from an empty object array.
                    currentValue = EnumerableInvocations.Cast(typeArg, (IEnumerable)currentValue);
                }
                else
                {
                    // This should allow the casting back of IEnumerable<T> to an empty List<T> Collection<T> etc.
                    // I cant think of any that don't have an empty constructor
                    currentValue = propertyType.GetInstance();
                }
            }

            return((currentValue == null && propertyType.IsValueType)
                ? propertyInfo.PropertyType.GetInstance() // Set to default instance of value type
                : currentValue);
        }
Esempio n. 11
0
        /// <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));
        }