Exemple #1
0
    /// <summary>
    /// Creates an EnumerableWriter for the specified type.
    /// </summary>
    /// <typeparam name="TItem">The type of item which the enumerable provides.</typeparam>
    /// <param name="enumerableType">Type for the EnumuerableWriter to work with. Must be a recognized Enumerable, or method throws.</param>
    /// <returns>Will always return an instance of <see cref="EnumerableWriter"/></returns>
    private static EnumerableWriter CreateFor <TItem>(Type enumerableType)
    {
        Type ignored = null;

        EnumerableWriter result = null;

        if (enumerableType.IsArray)
        {
            result = new ArrayWriter <TItem>();
        }
        else if (enumerableType.IsGenericIList(out ignored))
        {
            result = new GenericListWriter <TItem>();
        }
        else if (enumerableType.IsNongenericIList())
        {
            result = new NongenericListWriter();
        }
        else if (enumerableType.IsGenericEnumerable(out ignored))
        {
            result = new GenericEnumerableRewriter <TItem>();
        }
        else if (enumerableType.IsNongenericEnumerable())
        {
            result = new NongenericEnumerableRewriter();
        }
        else
        {
            throw new ArgumentException("Unable to produce EnumerableWriter for type " + enumerableType.FullName);
        }

        return(result);
    }
Exemple #2
0
 public static void Approve <T>(IEnumerable <T> enumerable, EnumerableWriter.CustomFormatter <T> formatter)
 {
     Approve(EnumerableWriter.Write(enumerable, formatter));
 }
Exemple #3
0
 public static void Approve <T>(String header, IEnumerable <T> enumerable, EnumerableWriter.CustomFormatter <T> formatter)
 {
     Approve(header + "\r\n\r\n" + EnumerableWriter.Write(enumerable, formatter));
 }
Exemple #4
0
 public static void Approve <T>(IEnumerable <T> enumerable, string label)
 {
     Approve(EnumerableWriter.Write(enumerable, label));
 }
        /// <summary>
        /// Copies all properties of the provided source-instance onto the provided destination-instance.
        /// </summary>
        /// <param name="sourceType">Type of the source value.</param>
        /// <param name="destinationType">Type of the destination value.</param>
        /// <param name="source">Source-value to copy.</param>
        /// <param name="destination">Target-value which properties will be copied onto.</param>
        /// <param name="throwOnMismatch">
        /// If true, throws when there is a mismatch between the types being converted.
        /// If false, silently ignores property with mismatch and moves along to the next one.
        /// </param>
        /// <param name="ignorePropertiesWithoutSetter">
        /// Will ignore properties that has only a get function
        /// </param>
        public void CopyProperties(Type sourceType, Type destinationType, object source, object destination, bool throwOnMismatch, bool ignorePropertiesWithoutSetter = false)
        {
            // TODO: future bugfix/optimization
            // - add "stack"/repository for copied instances, so that circuler references will not cause isssues.
            // - will probably also save CPU-cycles by not having to copy same instance several times.
            // (instance-repository needs to follow copy-process, not SoMapper instance.)

            /*
             * Person p;
             * Contact c = p.Contact;
             * Person[] ps = c.Persons // includes p, which includes c, etc.
             */

            Type type1 = sourceType;
            Type type2 = destinationType;

            // just get the properties we can work with, nothing more.
            const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public;

            PropertyInfo[] type1PropertyInfos = type1.GetProperties(flags).Where(p => p.CanRead).ToArray();
            PropertyInfo[] type2PropertyInfos = type2.GetProperties(flags).Where(p => p.CanWrite).ToArray();

            PropertyInfo[] type2PropertyInfosCanRead = type2.GetProperties(flags).Where(p => p.CanRead).ToArray();

            // process properties
            foreach (var type1PropertyInfo in type1PropertyInfos)
            {
                // do null check before other analysis
                object type1PropertyValue = type1PropertyInfo.GetValue(source, null);
                if (type1PropertyValue == null)
                {
                    // no point processing null-values.
                    continue;
                }

                // find target prop
                var type2PropertyInfo = type2PropertyInfos.FirstOrDefault(pi => pi.Name == type1PropertyInfo.Name);
                if (type2PropertyInfo == null)
                {
                    if (throwOnMismatch)
                    {
                        var type2PropertyInfoCanRead =
                            type2PropertyInfosCanRead.FirstOrDefault(pi => pi.Name == type1PropertyInfo.Name);
                        if (ignorePropertiesWithoutSetter && type2PropertyInfoCanRead != null)
                        {
                            continue;
                        }
                        else
                        {
                            string message =
                                string.Format(
                                    "Cannot transfer property '{0}' from type '{1}' to type '{2}': Property does not exist on target type.",
                                    type1PropertyInfo.Name, type1.FullName, type2.FullName);
                            throw new InvalidOperationException(message);
                        }
                    }
                    else
                    {
                        continue;
                    }
                }

                // validate match between preoprties
                Type type1PropertyType = type1PropertyInfo.PropertyType;
                Type type2PropertyType = type2PropertyInfo.PropertyType;

                Type type1ElementType, type2ElementType;

                bool type1IsEnumerable = type1PropertyType.IsEnumerable(out type1ElementType);
                bool type2IsEnumerable = type2PropertyType.IsEnumerable(out type2ElementType);

                // both must be array or not
                bool isCoherent1 = type1IsEnumerable == type2IsEnumerable;
                if (!isCoherent1)
                {
                    if (throwOnMismatch)
                    {
                        string message =
                            string.Format(
                                "Cannot transfer property '{0}' from type '{1}' to type '{2}': Cannot transfer value between enumerable and non-enumerable types.",
                                type1PropertyInfo.Name, type1.FullName, type2.FullName);
                        throw new InvalidOperationException(message);
                    }
                    else
                    {
                        // next property
                        continue;
                    }
                }

                // validate array-rank only if array.
                if (type1IsEnumerable && type1.IsArray)
                {
                    int rank = type1PropertyType.GetArrayRank();
                    if (rank != 1)
                    {
                        // throw. always. this is not a "mismatch"
                        throw new NotSupportedException("Duck-copying multidimensional arrays is not supported.");
                    }
                }

                // validate type-conversion
                Type type1ValidationType, type2ValidationType;

                if (type1IsEnumerable)
                {
                    type1ValidationType = type1ElementType;
                    type2ValidationType = type2ElementType;
                }
                else
                {
                    type1ValidationType = type1PropertyType;
                    type2ValidationType = type2PropertyType;
                }

                bool type1IsPrimitive = type1ValidationType.IsSimple();
                bool type2IsPrimitive = type2ValidationType.IsSimple();

                bool type1IsDecimal = type1ValidationType.IsDecimalNumber();
                bool type2IsDecimal = type2ValidationType.IsDecimalNumber();

                bool type1IsEnum = type1ValidationType.IsEnum;
                bool type2IsEnum = type2ValidationType.IsEnum;

                bool bothAreSamePrimitive = (type1IsPrimitive && type2IsPrimitive && (type1ValidationType == type2ValidationType));
                bool bothAreNonPrimitives = (!type1IsPrimitive && !type2IsPrimitive);
                bool bothAreDecimals      = (type1IsDecimal && type2IsDecimal);
                bool bothAreEnum          = type1IsEnum && type2IsEnum;

                bool isCoherent2 = bothAreNonPrimitives || bothAreSamePrimitive || bothAreEnum || bothAreDecimals;

                if (!isCoherent2)
                {
                    if (throwOnMismatch)
                    {
                        string message =
                            string.Format(
                                "Cannot transfer property '{0}' from type '{1}' to type '{2}': Properties are not coherent across types.",
                                type1PropertyInfo.Name, type1.FullName, type2.FullName);
                        throw new InvalidOperationException(message);
                    }
                    else
                    {
                        // next property
                        continue;
                    }
                }

                // handle everything as Enumerables to simplify codebase
                System.Collections.IEnumerable sourceEnumerable;
                if (!type1IsEnumerable)
                {
                    sourceEnumerable = new[] { type1PropertyValue };
                    // fake a collection-type destination to have the activator create something to insert data into.
                    type2PropertyType = typeof(IEnumerable <>).MakeGenericType(type2ValidationType);
                }
                else
                {
                    sourceEnumerable = (System.Collections.IEnumerable)type1PropertyValue;
                }

                // safely allocate target collection
                System.Collections.IEnumerable destinationEnumerable = (System.Collections.IEnumerable)CreateDefaultEntity(type2PropertyType);
                EnumerableWriter writer = EnumerableWriter.CreateFor(type2PropertyType);

                // do set-conversion
                foreach (object sourceValue in sourceEnumerable)
                {
                    object destinationValue;

                    if (type1IsPrimitive && bothAreSamePrimitive)
                    {
                        // primitive types can be copied directly.
                        destinationValue = sourceValue;
                    }
                    else if (type1IsPrimitive && bothAreDecimals)
                    {
                        destinationValue = CopyDecimal(sourceValue, type2ValidationType);
                    }
                    else if (type1IsPrimitive)
                    {
                        throw new InvalidOperationException("Unsupported property-copy between non-compatible primitive types.");
                    }
                    else if (type1ValidationType == type2ValidationType &&
                             type1ValidationType.IsInterface)
                    {
                        // we cannot reliably create new instances of these, but we can safely copy instance.
                        destinationValue = sourceValue;
                    }
                    else if (bothAreEnum)
                    {
                        destinationValue = Enum.ToObject(type2ValidationType, (int)sourceValue);
                    }
                    else
                    {
                        // complex types needs to be converted by doing a recursive self invocation.

                        // NOTE: we specify validation-types to ensure it works for both enumerables and non-enumerables.
                        destinationValue = CreateDefaultEntity(type2ValidationType);
                        CopyProperties(type1ValidationType, type2ValidationType, sourceValue, destinationValue, throwOnMismatch);
                    }

                    destinationEnumerable = writer.Write(destinationEnumerable, destinationValue);
                }

                object result;
                if (type1IsEnumerable)
                {
                    result = destinationEnumerable;
                }
                else
                {
                    // we need to unwrap our value from the enumerator
                    System.Collections.IEnumerator enumerator = destinationEnumerable.GetEnumerator();
                    if (enumerator.MoveNext())
                    {
                        result = enumerator.Current;
                    }
                    else
                    {
                        // this should never happen. so we throw.
                        throw new Exception("Internal error copying single value between types.");
                    }
                }

                // all that remains is writing copy.
                type2PropertyInfo.SetValue(destination, result, null);
            }
        }
Exemple #6
0
 public string ExecuteQuery(string query)
 {
     return(EnumerableWriter.Write(GetQueryExtracted(), "author"));
 }