public static CodeStatement CreateToSourceAssignmentMethod(
            TranslateAttribute attr,
            CodeVariableDeclarationStatement toSource,
            PropertyInfo toSourceProperty, CodeParameterDeclarationExpression fromTargetParam,
            Func <Type, Type, TranslateAttribute> getTypesTranslateAttributeFn)
        {
            var fromTargetProperty = attr.TargetType.GetProperty(toSourceProperty.Name);
            var cantReadFromTarget = fromTargetProperty.GetGetMethod() == null;

            if (cantReadFromTarget)
            {
                return(new CodeCommentStatement(string.Format("Skipping property 'to.{0}' because 'model.{1}' is write-only",
                                                              toSourceProperty.Name, fromTargetProperty.Name)));
            }
            var cantWriteToSource = toSourceProperty.GetSetMethod() == null;

            if (cantWriteToSource)
            {
                return(new CodeCommentStatement(string.Format("Skipping property 'to.{0}' because 'to.{1}' is read-only",
                                                              toSourceProperty.Name, toSourceProperty.Name)));
            }

            //to[property.Name] = this[property.Name] e.g:
            //	to.Name = from.Name;
            if (fromTargetProperty.PropertyType.IsAssignableFrom(toSourceProperty.PropertyType))
            {
                return(toSource.Assign(
                           toSource.RefProperty(toSourceProperty.Name),
                           fromTargetParam.Name.RefArgument().RefProperty(toSourceProperty.Name)));
            }

            //to[property.Name] = from[property.PropertyType.Name].ToTarget() e.g:
            //to.Address = from.Address.ToTarget();

            var toSourcePropertyType     = toSourceProperty.PropertyType;
            var fromTargetPropertyType   = fromTargetProperty.PropertyType;
            var targetAttr               = getTypesTranslateAttributeFn(toSourcePropertyType, fromTargetPropertyType);
            var useExtensionMethods      = attr is TranslateExtensionAttribute;
            var isTargetTranslatableAlso = targetAttr != null;

            if (isTargetTranslatableAlso)
            {
                var toSourceMethodName = targetAttr.GetConvertToSourceMethodName();
                var method             = useExtensionMethods
                                                                        ? fromTargetParam.RefProperty(toSourceProperty.Name).Call(toSourceMethodName)
                                                                        : toSourcePropertyType.Call(toSourceMethodName, fromTargetParam.RefProperty(toSourceProperty.Name));
                return(toSource.Assign(toSourceProperty.Name, method));
            }

            var fromSourceIsGenericList = toSourcePropertyType.IsGenericType && toSourcePropertyType.GetGenericTypeDefinition() == typeof(List <>);
            var toTargetIsGenericList   = fromTargetPropertyType.IsGenericType && fromTargetPropertyType.GetGenericTypeDefinition() == typeof(List <>);
            var bothAreGenericLists     = fromSourceIsGenericList && toTargetIsGenericList;

            if (bothAreGenericLists)
            {
                //to.PhoneNumbers = from.PhoneNumbers.ToPhoneNumbers();
                var propertyListItemTypeAttr = getTypesTranslateAttributeFn(toSourcePropertyType.GetGenericArguments()[0], fromTargetPropertyType.GetGenericArguments()[0]);
                var sourceIsTranslatable     = propertyListItemTypeAttr != null;
                if (sourceIsTranslatable)
                {
                    var toSourcesMethodName = propertyListItemTypeAttr.GetConvertToSourcesMethodName();
                    var method = useExtensionMethods
                                                                ? fromTargetParam.RefProperty(fromTargetProperty.Name).Call(toSourcesMethodName)
                                                                : propertyListItemTypeAttr.SourceType.Call(toSourcesMethodName, fromTargetParam.RefProperty(fromTargetProperty.Name));

                    return(toSource.RefProperty(toSourceProperty.Name).Assign(method));
                }
            }

            //to[property.Name] = this[property.Name].ToString() e.g:
            //	to.Name = from.Name;
            if (toSourceProperty.PropertyType == typeof(string) &&
                StringConverterUtils.CanCreateFromString(fromTargetProperty.PropertyType))
            {
                var fromTargetPropertyRef = fromTargetParam.Name.RefArgument().RefProperty(toSourceProperty.Name);
                return(fromTargetPropertyRef.IfIsNotNull(
                           toSource.Assign(
                               toSource.RefProperty(toSourceProperty.Name),
                               fromTargetPropertyRef.Call("ToString"))));
            }

            // Converting 'System.Collections.Generic.List`1 PhoneNumbers' to 'System.Collections.Generic.List`1 PhoneNumbers' is unsupported
            return(new CodeCommentStatement(string.Format("Converting '{0}.{1} {2}' to '{3}.{4} {5}' is unsupported"
                                                          , toSourceProperty.PropertyType.Namespace, toSourceProperty.PropertyType.Name, toSourceProperty.Name
                                                          , fromTargetProperty.PropertyType.Namespace, fromTargetProperty.PropertyType.Name, fromTargetProperty.Name)));
        }
        public static CodeStatement CreateToTargetAssignmentMethod(
            TranslateAttribute attr,
            CodeParameterDeclarationExpression toTarget,
            PropertyInfo sourceProperty,
            CodeVariableReferenceExpression fromSourceVar,
            Func <Type, Type, TranslateAttribute> getTypesTranslateAttributeFn)
        {
            //model.Name = this.Name;
            var targetProperty    = attr.TargetType.GetProperty(sourceProperty.Name);
            var cantWriteToTarget = targetProperty.GetSetMethod() == null;

            if (cantWriteToTarget)
            {
                return(new CodeCommentStatement(string.Format("Skipping property 'model.{0}' because 'model.{1}' is read-only",
                                                              targetProperty.Name, sourceProperty.Name)));
            }
            var cantReadFromSource = sourceProperty.GetGetMethod() == null;

            if (cantReadFromSource)
            {
                return(new CodeCommentStatement(string.Format("Skipping property 'model.{0}' because 'this.{1}' is write-only",
                                                              targetProperty.Name, sourceProperty.Name)));
            }

            var areBothTheSameTypes = targetProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType);

            if (areBothTheSameTypes)
            {
                return(toTarget.Assign(sourceProperty.Name, fromSourceVar.RefProperty(sourceProperty.Name)));
            }

            //model.BillingAddress = this.BillingAddress.ToTarget();
            var sourcePropertyType       = sourceProperty.PropertyType;
            var targetPropertyType       = targetProperty.PropertyType;
            var sourceAttr               = getTypesTranslateAttributeFn(sourcePropertyType, targetPropertyType);
            var isSourceTranslatableAlso = sourceAttr != null;
            var useExtensionMethods      = attr is TranslateExtensionAttribute;

            if (isSourceTranslatableAlso)
            {
                var toTargetMethodName = sourceAttr.GetConvertToTargetMethodName();
                var method             = fromSourceVar.RefProperty(sourceProperty.Name).Call(toTargetMethodName);

                return(fromSourceVar.RefProperty(sourceProperty.Name).IfIsNotNull(
                           toTarget.Assign(sourceProperty.Name, method)
                           ));
            }

            var sourceIsGenericList = sourcePropertyType.IsGenericType && sourcePropertyType.GetGenericTypeDefinition() == typeof(List <>);
            var targetIsGenericList = targetPropertyType.IsGenericType && targetPropertyType.GetGenericTypeDefinition() == typeof(List <>);
            var bothAreGenericLists = sourceIsGenericList && targetIsGenericList;

            if (bothAreGenericLists)
            {
                //to.PhoneNumbers = from.PhoneNumbers.ToPhoneNumbers();
                var propertyListItemTypeAttr = getTypesTranslateAttributeFn(sourcePropertyType.GetGenericArguments()[0], targetPropertyType.GetGenericArguments()[0]);
                var sourceIsTranslatable     = propertyListItemTypeAttr != null;
                if (sourceIsTranslatable)
                {
                    var toTargetsMethodName = propertyListItemTypeAttr.GetConvertToTargetsMethodName();
                    var method = useExtensionMethods
                                                                ? fromSourceVar.RefProperty(sourceProperty.Name).Call(toTargetsMethodName)
                                                                : propertyListItemTypeAttr.SourceType.Call(toTargetsMethodName, sourceProperty.Name.ThisProperty());

                    return(toTarget.Assign(sourceProperty.Name, method));
                }
            }

            //model.Type = StringConverterUtils.Parse<Target.PhoneNumberType>(this.Type);
            var sourcePropertyIsStringAndTargetIsConvertible = sourceProperty.PropertyType == typeof(string) &&
                                                               StringConverterUtils.CanCreateFromString(targetProperty.PropertyType);

            if (sourcePropertyIsStringAndTargetIsConvertible)
            {
                //model.CardType = StringConverterUtils.Parse<CardType>(this.CardType);
                var methodResult = typeof(StringConverterUtils).CallGeneric(CONVERTER_PARSE_METHOD,
                                                                            new[] { targetProperty.PropertyType.GenericDefinition() },
                                                                            fromSourceVar.RefProperty(sourceProperty.Name));

                return(toTarget.Assign(sourceProperty.Name.ThisProperty(), methodResult));
            }

            // Converting 'System.Collections.Generic.List`1 PhoneNumbers' to 'System.Collections.Generic.List`1 PhoneNumbers' is unsupported
            return(new CodeCommentStatement(string.Format("Converting '{0}.{1} {2}' to '{3}.{4} {5}' is unsupported"
                                                          , sourceProperty.PropertyType.Namespace, sourceProperty.PropertyType.Name, sourceProperty.Name
                                                          , targetProperty.PropertyType.Namespace, targetProperty.PropertyType.Name, targetProperty.Name)));
        }