/// <summary>
        /// This will return an ExtendableCompilableTypeConverterFactory based around the destination types having a zero parameter constructor and
        /// its data being set through public properties
        /// </summary>
        public static ExtendableCompilableTypeConverterFactory GeneratePropertySetterBasedFactory(
            INameMatcher nameMatcher,
            CompilableTypeConverterByPropertySettingFactory.PropertySettingTypeOptions propertySettingTypeOptions,
            IEnumerable <ICompilablePropertyGetterFactory> basePropertyGetterFactories,
            IEnumerable <PropertyInfo> propertiesToIgnore,
            ByPropertySettingNullSourceBehaviourOptions nullSourceBehaviour,
            IEnumerable <PropertyInfo> initialisedFlagsIfTranslatingNullsToEmptyInstances,
            EnumerableSetNullHandlingOptions enumerableSetNullHandling)
        {
            if (nameMatcher == null)
            {
                throw new ArgumentNullException("nameMatcher");
            }
            if (!Enum.IsDefined(typeof(CompilableTypeConverterByPropertySettingFactory.PropertySettingTypeOptions), propertySettingTypeOptions))
            {
                throw new ArgumentOutOfRangeException("propertySettingTypeOptions");
            }
            if (basePropertyGetterFactories == null)
            {
                throw new ArgumentNullException("basePropertyGetterFactories");
            }
            if (propertiesToIgnore == null)
            {
                throw new ArgumentNullException("propertiesToIgnore");
            }
            if (initialisedFlagsIfTranslatingNullsToEmptyInstances == null)
            {
                throw new ArgumentNullException("initialisedFlagsIfTranslatingNullsToEmptyInstances");
            }
            if (!Enum.IsDefined(typeof(ByPropertySettingNullSourceBehaviourOptions), nullSourceBehaviour))
            {
                throw new ArgumentOutOfRangeException("nullSourceBehaviour");
            }
            if (!Enum.IsDefined(typeof(EnumerableSetNullHandlingOptions), enumerableSetNullHandling))
            {
                throw new ArgumentOutOfRangeException("enumerableSetNullHandling");
            }

            return(new ExtendableCompilableTypeConverterFactory(
                       nameMatcher,
                       basePropertyGetterFactories,
                       propertyGetterFactories => new CompilableTypeConverterByPropertySettingFactory(
                           // Define a ConverterFactoryGenerator to return a CompilableTypeConverterByPropertySettingFactory when new conversions are registered
                           new CombinedCompilablePropertyGetterFactory(propertyGetterFactories),
                           propertySettingTypeOptions,
                           propertiesToIgnore,
                           nullSourceBehaviour,
                           initialisedFlagsIfTranslatingNullsToEmptyInstances
                           ),
                       new CompilableTypeConverterPropertyGetterFactoryExtrapolator(
                           nameMatcher,
                           enumerableSetNullHandling
                           )
                       ));
        }
        public CompilableTypeConverterByPropertySettingFactory(
            ICompilablePropertyGetterFactory propertyGetterFactory,
            PropertySettingTypeOptions propertySettingType,
            IEnumerable <PropertyInfo> propertiesToIgnore,
            ByPropertySettingNullSourceBehaviourOptions nullSourceBehaviour,
            IEnumerable <PropertyInfo> initialisedFlagsIfTranslatingNullsToEmptyInstances)
        {
            if (propertyGetterFactory == null)
            {
                throw new ArgumentNullException("propertyGetterFactory");
            }
            if (!Enum.IsDefined(typeof(PropertySettingTypeOptions), propertySettingType))
            {
                throw new ArgumentOutOfRangeException("propertySettingType");
            }
            if (propertiesToIgnore == null)
            {
                throw new ArgumentNullException("propertiesToIgnore");
            }
            if (!Enum.IsDefined(typeof(ByPropertySettingNullSourceBehaviourOptions), nullSourceBehaviour))
            {
                throw new ArgumentOutOfRangeException("nullSourceBehaviour");
            }
            if (initialisedFlagsIfTranslatingNullsToEmptyInstances == null)
            {
                throw new ArgumentNullException("initialisedFlagsIfTranslatingNullsToEmptyInstances");
            }
            if ((nullSourceBehaviour != ByPropertySettingNullSourceBehaviourOptions.CreateEmptyInstanceWithDefaultPropertyValues) && initialisedFlagsIfTranslatingNullsToEmptyInstances.Any())
            {
                throw new ArgumentException("initialisedFlagsIfTranslatingNullsToEmptyInstances must be empty if nullSourceBehaviour is not CreateEmptyInstanceWithDefaultPropertyValues");
            }

            _propertyGetterFactory = propertyGetterFactory;
            _propertySettingType   = propertySettingType;
            _propertiesToIgnore    = new HashSet <PropertyInfo>(propertiesToIgnore);
            if (_propertiesToIgnore.Any(p => p == null))
            {
                throw new ArgumentException("Null reference encountered in propertiesToIgnore set");
            }
            _nullSourceBehaviour = nullSourceBehaviour;
            _initialisedFlagsIfTranslatingNullsToEmptyInstances = initialisedFlagsIfTranslatingNullsToEmptyInstances.ToList().AsReadOnly();
            if (_initialisedFlagsIfTranslatingNullsToEmptyInstances.Any(p => p == null))
            {
                throw new ArgumentException("Null reference encountered in initialisedFlagsIfTranslatingNullToEmptyInstance set");
            }
            if (_initialisedFlagsIfTranslatingNullsToEmptyInstances.Any(p => (p.PropertyType != typeof(bool)) && (p.PropertyType != typeof(bool?))))
            {
                throw new ArgumentException("Encountered invalid property in initialisedFlagsIfTranslatingNullToEmptyInstance set, PropertyType must be bool or nullable bool");
            }
        }
        public ConverterWrapper(ByPropertySettingNullSourceBehaviourOptions nullSourceBehaviour, EnumerableSetNullHandlingOptions enumerableSetNullHandling)
        {
            if (!Enum.IsDefined(typeof(ByPropertySettingNullSourceBehaviourOptions), nullSourceBehaviour))
            {
                throw new ArgumentOutOfRangeException("nullSourceBehaviour");
            }
            if (!Enum.IsDefined(typeof(EnumerableSetNullHandlingOptions), enumerableSetNullHandling))
            {
                throw new ArgumentOutOfRangeException("enumerableSetNullHandling");
            }

            _nullSourceBehaviour       = nullSourceBehaviour;
            _enumerableSetNullHandling = enumerableSetNullHandling;
            _allPropertiesToIgnoreToPropertySetterConversions      = new List <PropertyInfo>();
            _allInitialisedFlagsIfTranslatingNullsToEmptyInstances = new List <PropertyInfo>();
            _converterCache = new Dictionary <Tuple <Type, Type>, object>();

            // Prepare converter factories (for by-constructor and by-property-setters) using the base types (AssignableType and
            // EnumConversion property getter factories)
            var nameMatcher = new CaseInsensitiveSkipUnderscoreNameMatcher();
            var basePropertyGetterFactories = new ICompilablePropertyGetterFactory[]
            {
                new CompilableAssignableTypesPropertyGetterFactory(nameMatcher),
                new CompilableEnumConversionPropertyGetterFactory(nameMatcher)
            };

            _constructorBasedConverterFactory = ExtendableCompilableTypeConverterFactoryHelpers.GenerateConstructorBasedFactory(
                nameMatcher,
                new ArgsLengthTypeConverterPrioritiserFactory(),
                basePropertyGetterFactories,
                _enumerableSetNullHandling
                );
            _propertySetterBasedConverterFactory = ExtendableCompilableTypeConverterFactoryHelpers.GeneratePropertySetterBasedFactory(
                nameMatcher,
                CompilableTypeConverterByPropertySettingFactory.PropertySettingTypeOptions.MatchAll,
                basePropertyGetterFactories,
                _allPropertiesToIgnoreToPropertySetterConversions,
                _nullSourceBehaviour,
                _allInitialisedFlagsIfTranslatingNullsToEmptyInstances,
                _enumerableSetNullHandling
                );
        }
        public CompilableTypeConverterByPropertySetting(
            IEnumerable <ICompilablePropertyGetter> propertyGetters,
            IEnumerable <PropertyInfo> propertiesToSet,
            ByPropertySettingNullSourceBehaviourOptions nullSourceBehaviour,
            IEnumerable <PropertyInfo> initialisedFlagsIfTranslatingNullToEmptyInstance)
        {
            if (propertyGetters == null)
            {
                throw new ArgumentNullException("propertyGetters");
            }
            if (propertiesToSet == null)
            {
                throw new ArgumentNullException("propertiesToSet");
            }
            if (!Enum.IsDefined(typeof(ByPropertySettingNullSourceBehaviourOptions), nullSourceBehaviour))
            {
                throw new ArgumentOutOfRangeException("nullSourceBehaviour");
            }
            if (initialisedFlagsIfTranslatingNullToEmptyInstance == null)
            {
                throw new ArgumentNullException("initialisedFlagsIfTranslatingNullToEmptyInstance");
            }
            if ((nullSourceBehaviour != ByPropertySettingNullSourceBehaviourOptions.CreateEmptyInstanceWithDefaultPropertyValues) && initialisedFlagsIfTranslatingNullToEmptyInstance.Any())
            {
                throw new ArgumentException("initialisedFlagsIfTranslatingNullToEmptyInstance must be empty if nullSourceBehaviour is not CreateEmptyInstanceWithDefaultPropertyValues");
            }

            // Ensure there are no null references in the property lists
            var propertyGettersList = new List <ICompilablePropertyGetter>();

            foreach (var propertyGetter in propertyGetters)
            {
                if (propertyGetter == null)
                {
                    throw new ArgumentException("Null reference encountered in propertyGetters list");
                }
                if (!propertyGetter.SrcType.Equals(typeof(TSource)))
                {
                    throw new ArgumentException("Encountered invalid SrcType in propertyGetters list, must match type param TSource");
                }
                propertyGettersList.Add(propertyGetter);
            }
            var propertiesToSetList = new List <PropertyInfo>();

            foreach (var property in propertiesToSet)
            {
                if (property == null)
                {
                    throw new ArgumentException("Null reference encountered in propertyGetters list");
                }
                if (!typeof(TDest).HasProperty(property))
                {
                    throw new ArgumentException("Encountered invalid entry in propertiesToSete set, not available on type TDest");
                }
                propertiesToSetList.Add(property);
            }

            // Ensure that the property getters correspond to the target properties
            if (propertyGettersList.Count != propertiesToSetList.Count)
            {
                throw new ArgumentException("Number of propertyGetters specified must match number of propertiesToSet");
            }
            for (var index = 0; index < propertyGettersList.Count; index++)
            {
                if (!propertiesToSetList[index].PropertyType.IsAssignableFrom(propertyGettersList[index].TargetType))
                {
                    throw new ArgumentException("propertyGetter[" + index + "].TargetType is not assignable to corresponding propertyToSet");
                }
            }

            _propertyGetters     = propertyGettersList.AsReadOnly();
            _propertiesToSet     = propertiesToSetList.AsReadOnly();
            _nullSourceBehaviour = nullSourceBehaviour;
            _initialisedFlagsIfTranslatingNullToEmptyInstance = initialisedFlagsIfTranslatingNullToEmptyInstance.ToList().AsReadOnly();
            if (_initialisedFlagsIfTranslatingNullToEmptyInstance.Any(p => p == null))
            {
                throw new ArgumentException("Null reference encountered in initialisedFlagsIfTranslatingNullToEmptyInstance set");
            }
            if (_initialisedFlagsIfTranslatingNullToEmptyInstance.Any(p => (p.PropertyType != typeof(bool)) && (p.PropertyType != typeof(bool?))))
            {
                throw new ArgumentException("Encountered invalid property in initialisedFlagsIfTranslatingNullToEmptyInstance set, PropertyType must be bool or nullable bool");
            }
            if (_initialisedFlagsIfTranslatingNullToEmptyInstance.Any(p => !typeof(TDest).HasProperty(p)))
            {
                throw new ArgumentException("Encountered invalid property in initialisedFlagsIfTranslatingNullToEmptyInstance set, must be a property available on TDest");
            }
            PropertyMappings = propertyGettersList
                               .Select((sourceProperty, index) => new PropertyMappingDetails(
                                           sourceProperty.Property,
                                           propertiesToSetList[index].Name,
                                           propertiesToSetList[index].PropertyType
                                           ));

            // Generate a Expression<Func<TSource, TDest>>, the _rawConverterExpression is still required for the GetTypeConverterExpression
            // method (this may be called to retrieve the raw expression, rather than the Func-wrapped version - eg. by the ListCompilablePropertyGetter,
            // which has a set of TSource objects and wants to translate them into a set of TDest objects)
            var srcParameter = Expression.Parameter(typeof(TSource), "src");

            _converterFuncExpression = Expression.Lambda <Func <TSource, TDest> >(
                GetTypeConverterExpression(srcParameter),
                srcParameter
                );

            // Compile the expression into an actual Func<TSource, TDest> (this is expected to be the most commonly-used form of the data)
            _converter = _converterFuncExpression.Compile();
        }