Exemple #1
0
        protected MemberInfo Normalize(MemberInfo memberInfo)
        {
            if (memberInfo is FickleMethodInfo)
            {
                var methodInfo = (FickleMethodInfo)memberInfo;
                var name = this.normalize(methodInfo.Name);
                var prefix = reservedKeywords.Contains(name) ? replacementPrefix : "";

                if (name != methodInfo.Name || prefix != "")
                {
                    methodInfo = new FickleMethodInfo(methodInfo.DeclaringType, methodInfo.ReturnType, prefix + name, methodInfo.GetParameters(), methodInfo.IsStatic);

                    return methodInfo;
                }
            }
            else if (memberInfo is FicklePropertyInfo)
            {
                var propertyInfo = (FicklePropertyInfo)memberInfo;
                var name = this.normalize(propertyInfo.Name);
                var prefix = reservedKeywords.Contains(name) ? replacementPrefix : "";

                if (name != propertyInfo.Name || prefix != "")
                {
                    propertyInfo = new FicklePropertyInfo(propertyInfo.DeclaringType, propertyInfo.PropertyType, prefix + name);

                    return propertyInfo;
                }
            }

            return memberInfo;
        }
Exemple #2
0
        protected MemberInfo Normalize(MemberInfo memberInfo)
        {
            if (memberInfo is FickleMethodInfo)
            {
                var methodInfo = (FickleMethodInfo)memberInfo;
                var name       = this.normalize(methodInfo.Name);
                var prefix     = reservedKeywords.Contains(name) ? replacementPrefix : "";

                if (name != methodInfo.Name || prefix != "")
                {
                    methodInfo = new FickleMethodInfo(methodInfo.DeclaringType, methodInfo.ReturnType, prefix + name, methodInfo.GetParameters(), methodInfo.IsStatic);

                    return(methodInfo);
                }
            }
            else if (memberInfo is FicklePropertyInfo)
            {
                var propertyInfo = (FicklePropertyInfo)memberInfo;
                var name         = this.normalize(propertyInfo.Name);
                var prefix       = reservedKeywords.Contains(name) ? replacementPrefix : "";

                if (name != propertyInfo.Name || prefix != "")
                {
                    propertyInfo = new FicklePropertyInfo(propertyInfo.DeclaringType, propertyInfo.PropertyType, prefix + name);

                    return(propertyInfo);
                }
            }

            return(memberInfo);
        }
        private Expression CreateScalarPropertiesAsFormEncodedStringMethod(TypeDefinitionExpression expression)
        {
            var self       = Expression.Parameter(expression.Type, "self");
            var properties = ExpressionGatherer.Gather(expression, ServiceExpressionType.PropertyDefinition).Where(c => !(c.Type is FickleListType)).ToList();
            var parameters = properties.OfType <PropertyDefinitionExpression>().ToDictionary(c => c.PropertyName, c => Expression.Property(self, c.PropertyName));
            var path       = string.Join("", properties.OfType <PropertyDefinitionExpression>().Select(c => c.PropertyName + "={" + c.PropertyName + "}"));

            var formatInfo = ObjectiveStringFormatInfo.GetObjectiveStringFormatInfo
                             (
                path,
                c => parameters[c],
                (s, t) => t == typeof(string) ? s : s + "&",
                c => FickleExpression.Call(c, typeof(string), "stringByAppendingString", Expression.Constant("&"))
                             );

            var parameterInfos = new List <ParameterInfo>
            {
                new FickleParameterInfo(typeof(string), "format")
            };

            parameterInfos.AddRange(formatInfo.ParameterExpressions.Select(c => new ObjectiveParameterInfo(c.Type, c.Name, true)));

            var args = new List <Expression>
            {
                Expression.Constant(formatInfo.Format)
            };

            args.AddRange(formatInfo.ValueExpressions);

            var methodInfo = new FickleMethodInfo(typeof(string), typeof(string), "stringWithFormat", parameterInfos.ToArray(), true);
            var methodBody = Expression.Block(FickleExpression.Return(Expression.Call(null, methodInfo, args)).ToStatement());

            return(new MethodDefinitionExpression("scalarPropertiesAsFormEncodedString", new List <Expression>().ToReadOnlyCollection(), typeof(string), methodBody, false, null));
        }
        protected virtual MethodDefinitionExpression CreateSerializeRequestMethod()
        {
            var error         = FickleExpression.Variable("NSError", "error");
            var retval        = FickleExpression.Variable("NSData", "retval");
            var client        = Expression.Parameter(FickleType.Define("PKWebServiceClient"), "client");
            var requestObject = Expression.Parameter(FickleType.Define("id"), "serializeRequest");
            var parameters    = new[] { new FickleParameterInfo(FickleType.Define("NSDictionary"), "obj"), new FickleParameterInfo(FickleType.Define("NSJSONWritingOptions", false, true), "options"), new FickleParameterInfo(FickleType.Define("NSError"), "error", true) };
            var methodInfo    = new FickleMethodInfo(FickleType.Define("NSJSONSerialization"), FickleType.Define("NSData"), "dataWithJSONObject", parameters, true);
            var nsDataType    = FickleType.Define("NSData");

            var body =
                Fx.Block(error, retval)
                .If(Expression.Equal(requestObject, Expression.Constant(null, requestObject.Type)))
                .Assign(retval, Expression.New(FickleType.Define("NSData")))
                .ElseIf(Expression.TypeIs(requestObject, typeof(string)))
                .Assign(retval, FickleExpression.Call(requestObject, typeof(string), "dataUsingEncoding", Expression.Variable(typeof(int), "NSUTF8StringEncoding")))
                .ElseIf(Expression.TypeIs(requestObject, nsDataType))
                .Assign(retval, Expression.Convert(requestObject, nsDataType))
                .Else()
                .Assign(retval, Expression.Call(methodInfo, requestObject, Expression.Convert(Expression.Variable(typeof(int), "NSJSONReadingAllowFragments"), FickleType.Define("NSJSONWritingOptions", false, true)), error))
                .EndIf()
                .Return(retval)
                .EndBlock();

            return(new MethodDefinitionExpression
                   (
                       "webServiceClient",
                       new [] { client, requestObject }.ToReadOnlyCollection(),
                       retval.Type,
                       body,
                       false,
                       null
                   ));
        }
Exemple #5
0
        private static MethodCallExpression Call(Expression instance, Type type, Type returnType, string methodName, object arguments, bool isStatic = false)
        {
            var result = GetParametersAndArguments(arguments);

            var methodInfo = new FickleMethodInfo(type, returnType, methodName, result.Item1, isStatic);

            return(Expression.Call(instance, methodInfo, result.Item2));
        }
        protected override Expression VisitMethodDefinitionExpression(MethodDefinitionExpression method)
        {
            ParameterExpression optionsParameter;
            var self            = Expression.Variable(currentType, "self");
            var gatewayCall     = (MethodDefinitionExpression)this.CreateGatewayCallMethod(method, out optionsParameter);
            var newArgs         = gatewayCall.Parameters.Select(c => c != optionsParameter ? c : Expression.Constant(null, c.Type)).ToList();
            var newMethodParams = gatewayCall.Parameters.Where(c => c != optionsParameter).ToList();

            var methodInfo = new FickleMethodInfo(self.Type, gatewayCall.ReturnType, gatewayCall.Name, gatewayCall.Parameters.Cast <ParameterExpression>().Select(c => new FickleParameterInfo(c.Type, c.Name)).ToArray());
            var newBody    = Expression.Call(self, methodInfo, newArgs).ToStatementBlock();
            var gatewayCallMethodWithoutOptions = new MethodDefinitionExpression(gatewayCall.Name, newMethodParams, gatewayCall.ReturnType, newBody, false, gatewayCall.RawAttributes, gatewayCall.Attributes);

            return(new GroupedExpressionsExpression(new [] { gatewayCall, gatewayCallMethodWithoutOptions }, GroupedExpressionsExpressionStyle.Wide));
        }
        private Expression CreateScalarPropertiesAsFormEncodedStringMethod(TypeDefinitionExpression expression)
        {
            var self = Expression.Parameter(expression.Type, "self");
            var properties = ExpressionGatherer.Gather(expression, ServiceExpressionType.PropertyDefinition).Where(c => !(c.Type is FickleListType)).ToList();
            var parameters = properties.OfType<PropertyDefinitionExpression>().ToDictionary(c => c.PropertyName, c => Expression.Property(self, c.PropertyName));
            var path = string.Join("", properties.OfType<PropertyDefinitionExpression>().Select(c => c.PropertyName + "={" + c.PropertyName + "}"));

            var formatInfo = ObjectiveStringFormatInfo.GetObjectiveStringFormatInfo
            (
                path,
                c => parameters[c],
                (s, t) => t == typeof(string) ? s : s + "&",
                c => FickleExpression.Call(c, typeof(string), "stringByAppendingString", Expression.Constant("&"))
            );

            var parameterInfos = new List<ParameterInfo>
            {
                new FickleParameterInfo(typeof(string), "format")
            };

            parameterInfos.AddRange(formatInfo.ParameterExpressions.Select(c => new ObjectiveParameterInfo(c.Type, c.Name, true)));

            var args = new List<Expression>
            {
                Expression.Constant(formatInfo.Format)
            };

            args.AddRange(formatInfo.ValueExpressions);

            var methodInfo = new FickleMethodInfo(typeof(string), typeof(string), "stringWithFormat", parameterInfos.ToArray(), true);
            var methodBody = Expression.Block(FickleExpression.Return(Expression.Call(null, methodInfo, args)).ToStatement());

            return new MethodDefinitionExpression("scalarPropertiesAsFormEncodedString", new List<Expression>().ToReadOnlyCollection(), typeof(string), methodBody, false, null);
        }
        internal static Expression GetDeserializeExpressionProcessValueDeserializer(Type valueType, Expression value, Func<Expression, Expression> processOutputValue)
        {
            Expression outputValue;
            var nsNull = FickleExpression.StaticCall("NSNull", FickleType.Define("id"), "null", null);

            if (TypeSystem.IsPrimitiveType(valueType))
            {
                ConditionalExpression ifExpression;
                var underlyingType = valueType.GetUnwrappedNullableType();

                if (underlyingType.IsNumericType() || underlyingType == typeof(bool))
                {
                    var typeToCompare = new FickleType("NSNumber");

                    if (underlyingType.IsEnum && valueType.GetUnderlyingType() == null)
                    {
                        outputValue = Expression.Convert(FickleExpression.Call(value, typeof(int), "intValue", null), valueType);
                    }
                    else
                    {
                        outputValue = Expression.Convert(value, valueType);
                    }

                    ifExpression = Expression.IfThen(Expression.TypeIs(value, typeToCompare), processOutputValue(outputValue).ToBlock());
                }
                else if (underlyingType.IsEnum)
                {
                    Expression firstIf;

                    if (valueType.IsNullable())
                    {
                        outputValue = Expression.Convert(value, valueType);

                        firstIf = Expression.IfThen(Expression.TypeIs(value, FickleType.Define("NSNumber")), processOutputValue(outputValue).ToBlock());
                    }
                    else
                    {
                        outputValue = Expression.Convert(FickleExpression.Call(value, typeof(int), "intValue", null), valueType);

                        firstIf = Expression.IfThen(Expression.TypeIs(value, FickleType.Define("NSNumber")), processOutputValue(outputValue).ToBlock());
                    }

                    var parsedValue = FickleExpression.Variable(underlyingType, "parsedValue");
                    var success = Expression.Variable(typeof(bool), "success");

                    var parameters = new[] { new FickleParameterInfo(typeof(string), "value"), new FickleParameterInfo(underlyingType, "outValue", true) };
                    var methodInfo = new FickleMethodInfo(null, typeof(bool), TypeSystem.GetPrimitiveName(underlyingType, true) + "TryParse", parameters, true);

                    if (valueType.IsNullable())
                    {
                        outputValue = Expression.Convert(Expression.Condition(success, Expression.Convert(Expression.Convert(parsedValue, valueType), FickleType.Define("id")), Expression.Convert(FickleExpression.StaticCall(FickleType.Define("NSNull"), valueType, "null", null), FickleType.Define("id"))), valueType);
                    }
                    else
                    {
                        outputValue = Expression.Convert(parsedValue, valueType);
                    }

                    var secondIf = Expression.IfThenElse
                    (
                        Expression.TypeIs(value, FickleType.Define("NSString")),
                        FickleExpression.Block
                        (
                            new ParameterExpression[]
                            {
                                parsedValue,
                                success
                            },
                            Expression.IfThen
                            (
                                Expression.Not(Expression.Assign(success, Expression.Call(null, methodInfo, Expression.Convert(value, typeof(string)), parsedValue))),
                                Expression.Assign(parsedValue, Expression.Convert(Expression.Constant(0), underlyingType)).ToStatementBlock()
                            ),
                            processOutputValue(outputValue)
                        ),
                        firstIf
                    );

                    ifExpression = secondIf;
                }
                else
                {
                    ifExpression = Expression.IfThen(Expression.TypeIs(value, typeof(string)), processOutputValue(Expression.Convert(Expression.Convert(value, typeof(object)), valueType)).ToBlock());
                }

                if (valueType.IsNullable())
                {
                    ifExpression = Expression.IfThenElse
                    (
                        Expression.Equal(value, nsNull),
                        processOutputValue(Expression.Convert(nsNull, valueType)).ToBlock(),
                        ifExpression
                    );
                }

                return ifExpression;
            }
            else if (valueType is FickleType && ((FickleType)valueType).ServiceClass != null)
            {
                outputValue = FickleExpression.New(valueType, "initWithPropertyDictionary", FickleExpression.Convert(value, "NSDictionary"));

                return Expression.IfThen(Expression.TypeIs(value, FickleType.Define("NSDictionary")), processOutputValue(outputValue).ToBlock());
            }
            else if (valueType is FickleType && ((FickleType)valueType).ServiceEnum != null)
            {
                return Expression.IfThen(Expression.TypeIs(value, FickleType.Define("NSNumber")), processOutputValue(value).ToBlock());
            }
            else if (valueType is FickleListType)
            {
                var listType = valueType as FickleListType;

                var arrayVar = FickleExpression.Variable("NSMutableArray", "array");
                var variables = new[] { arrayVar };
                var arrayItem = FickleExpression.Parameter(FickleType.Define("id"), "arrayItem");

                var forEachBody = GetDeserializeExpressionProcessValueDeserializer(listType.ListElementType, arrayItem, c => FickleExpression.Call(arrayVar, "addObject", Expression.Convert(c, typeof(object))).ToStatement());

                var ifThen = Expression.IfThen
                (
                    Expression.TypeIs(value, FickleType.Define("NSArray")),
                    FickleExpression.Block
                    (
                        variables,
                        Expression.Assign(arrayVar, FickleExpression.New("NSMutableArray", "initWithCapacity", FickleExpression.Call(Expression.Convert(value, FickleType.Define("NSArray")), typeof(int), "count", null))).ToStatement(),
                        FickleExpression.ForEach(arrayItem, value, FickleExpression.Block(forEachBody)),
                        processOutputValue(Expression.Convert(arrayVar, valueType))
                    )
                );

                return ifThen;
            }
            else
            {
                throw new InvalidOperationException("Unsupported property type: " + valueType);
            }
        }
Exemple #9
0
        private static MethodCallExpression Call(Expression instance, Type type, Type returnType, string methodName, object arguments, bool isStatic = false)
        {
            var result = GetParametersAndArguments(arguments);

            var methodInfo = new FickleMethodInfo(type, returnType, methodName, result.Item1, isStatic);

            return Expression.Call(instance, methodInfo, result.Item2);
        }
        protected override Expression VisitMethodDefinitionExpression(MethodDefinitionExpression method)
        {
            var methodName = method.Name.ToCamelCase();

            methodCount++;

            var self = Expression.Variable(currentType, "self");
            var hostname = Expression.Variable(typeof(string), "hostname");
            var options = FickleExpression.Variable("NSMutableDictionary", "localOptions");
            var url = Expression.Variable(typeof(string), "url");
            var client = Expression.Variable(FickleType.Define(this.CodeGenerationContext.Options.ServiceClientTypeName ?? "PKWebServiceClient"), "client");
            var responseType = ObjectiveBinderHelpers.GetWrappedResponseType(this.CodeGenerationContext, method.ReturnType);
            var variables = new [] { url, client, options, hostname };
            var declaredHostname = currentTypeDefinitionExpression.Attributes["Hostname"];
            var declaredPath = method.Attributes["Path"];
            var path = StringUriUtils.Combine("http://%@", declaredPath);
            var httpMethod = method.Attributes["Method"];

            var parametersByName = method.Parameters.ToDictionary(c => ((ParameterExpression)c).Name, c => (ParameterExpression)c, StringComparer.InvariantCultureIgnoreCase);

            var formatInfo = ObjectiveStringFormatInfo.GetObjectiveStringFormatInfo(path, c => parametersByName[c]);

            var args = formatInfo.ValueExpressions;
            var parameters = formatInfo.ParameterExpressions;

            var parameterInfos = new List<FickleParameterInfo>
            {
                new ObjectiveParameterInfo(typeof(string), "s"),
                new ObjectiveParameterInfo(typeof(string), "hostname", true)
            };

            parameterInfos.AddRange(parameters.Select(c => new ObjectiveParameterInfo(c.Type, c.Name, true)));

            var methodInfo = new FickleMethodInfo(typeof(string), typeof(string), "stringWithFormat", parameterInfos.ToArray(), true);

            args.InsertRange(0, new Expression[] { Expression.Constant(formatInfo.Format), hostname });

            var newParameters = new List<Expression>(method.Parameters);
            var callback = Expression.Parameter(new FickleDelegateType(typeof(void), new FickleParameterInfo(responseType, "response")), "callback");

            newParameters.Add(callback);

            Expression blockArg = Expression.Parameter(FickleType.Define("id"), "arg1");

            var returnType = method.ReturnType;

            if (ObjectiveBinderHelpers.NeedsValueResponseWrapper(method.ReturnType))
            {
                returnType = FickleType.Define(ObjectiveBinderHelpers.GetValueResponseWrapperTypeName(method.ReturnType));
            }

            var conversion = Expression.Convert(blockArg, returnType);
            var body = FickleExpression.Call(callback, "Invoke", conversion).ToStatement();
            var conversionBlock = FickleExpression.SimpleLambda(null, body, new Expression[0], blockArg);

            Expression clientCallExpression;

            if (httpMethod.Equals("get", StringComparison.InvariantCultureIgnoreCase))
            {
                clientCallExpression = FickleExpression.Call(client, "getWithCallback", conversionBlock);
            }
            else
            {
                var contentParameterName = method.Attributes["Content"];
                var contentFormat = method.Attributes["ContentFormat"];

                if (string.IsNullOrEmpty(contentParameterName))
                {
                    clientCallExpression = FickleExpression.Call(client, "postWithRequestObject", new
                    {
                        requestObject = Expression.Constant(null, typeof(object)),
                        andCallback = conversionBlock
                    });
                }
                else
                {
                    var content = parametersByName[contentParameterName];

                    clientCallExpression = FickleExpression.Call(client, "postWithRequestObject", new
                    {
                        requestObject = FickleExpression.Call(self, typeof(object), this.GetNormalizeRequestMethodName(content.Type, contentFormat), new { serializeRequest=Expression.Convert(content, typeof(object)), paramName=Expression.Constant(contentParameterName) }),
                        andCallback = conversionBlock
                    });
                }
            }

            var error = FickleExpression.Variable("NSError", "error");

            var parseErrorResult = FickleExpression.Call(self, "webServiceClient", new
            {
                client,
                createErrorResponseWithErrorCode = "JsonDeserializationError",
                andMessage = FickleExpression.Call(error, "localizedDescription", null)
            });

            Expression parseResultBlock;

            var nsdataParam = Expression.Parameter(FickleType.Define("NSData"), "data");
            var jsonObjectWithDataParameters = new[] { new FickleParameterInfo(FickleType.Define("NSDictionary"), "obj"), new FickleParameterInfo(typeof(int), "options"), new FickleParameterInfo(FickleType.Define("NSError", true), "error", true) };
            var objectWithDataMethodInfo = new FickleMethodInfo(FickleType.Define("NSJSONSerialization"), FickleType.Define("NSData"), "JSONObjectWithData", jsonObjectWithDataParameters, true);
            var deserializedValue = Expression.Parameter(FickleType.Define("id"), "deserializedValue");

            if (method.ReturnType == typeof(void))
            {
                var responseObject = FickleExpression.Variable(responseType, "responseObject");

                parseResultBlock = FickleExpression.SimpleLambda
                (
                    FickleType.Define("id"),
                    FickleExpression.GroupedWide
                    (
                        Expression.Assign(responseObject, FickleExpression.New(responseType, "init", null)).ToStatement(),
                        Expression.Assign(deserializedValue, Expression.Call(objectWithDataMethodInfo, nsdataParam, FickleExpression.Variable(typeof(int), "NSJSONReadingAllowFragments"), error)).ToStatement(),
                        Expression.IfThen(Expression.Equal(deserializedValue, Expression.Constant(null)), FickleExpression.Return(parseErrorResult).ToStatement().ToBlock()),
                        FickleExpression.Return(responseObject).ToStatement()
                    ),
                    new Expression[] { deserializedValue, responseObject, error },
                    nsdataParam
                );
            }
            else if (TypeSystem.IsPrimitiveType(method.ReturnType) || method.ReturnType is FickleListType)
            {
                var responseObject = FickleExpression.Variable(responseType, "responseObject");
                var needToBoxValue = ObjectiveBinderHelpers.ValueResponseValueNeedsBoxing(method.ReturnType);

                parseResultBlock = FickleExpression.SimpleLambda
                (
                    FickleType.Define("id"),
                    FickleExpression.GroupedWide
                    (
                        Expression.Assign(responseObject, FickleExpression.New(responseType, "init", null)).ToStatement(),
                        Expression.Assign(deserializedValue, Expression.Call(objectWithDataMethodInfo, nsdataParam, FickleExpression.Variable(typeof(int), "NSJSONReadingAllowFragments"), error)).ToStatement(),
                        Expression.IfThen(Expression.Equal(deserializedValue, Expression.Constant(null)), FickleExpression.Return(parseErrorResult).ToStatement().ToBlock()),
                        PropertiesFromDictionaryExpressonBinder.GetDeserializeExpressionProcessValueDeserializer(method.ReturnType, deserializedValue, c => FickleExpression.Call(responseObject, typeof(void), "setValue", needToBoxValue  ? Expression.Convert(c, typeof(object)) : c).ToStatement()),
                        FickleExpression.Return(responseObject).ToStatement()
                    ),
                    new Expression[] { deserializedValue, responseObject, error },
                    nsdataParam
                );
            }
            else
            {
                parseResultBlock = FickleExpression.SimpleLambda
                (
                    FickleType.Define("id"),
                    FickleExpression.GroupedWide
                    (
                        Expression.Assign(deserializedValue, Expression.Call(objectWithDataMethodInfo, nsdataParam, FickleExpression.Variable(typeof(int), "NSJSONReadingAllowFragments"), error)).ToStatement(),
                        PropertiesFromDictionaryExpressonBinder.GetDeserializeExpressionProcessValueDeserializer(method.ReturnType, deserializedValue, c => FickleExpression.Return(c).ToStatement()),
                        FickleExpression.Return(parseErrorResult).ToStatement()
                    ),
                    new Expression[] { deserializedValue, error },
                    nsdataParam
                );
            }

            parseResultBlock = FickleExpression.Call(parseResultBlock, parseResultBlock.Type, "copy", null);

            var block = FickleExpression.Block
            (
                variables,
                Expression.Assign(callback, FickleExpression.Call(callback, callback.Type, "copy", null)),
                Expression.Assign(options, FickleExpression.Call(FickleExpression.Property(self, FickleType.Define("NSDictionary"), "options"), "NSMutableDictionary", "mutableCopyWithZone", new
                {
                    zone = Expression.Constant(null, FickleType.Define("NSZone"))
                })),
                FickleExpression.Call(options, typeof(void), "setObject", new { value = FickleExpression.StaticCall(responseType, "class", null), forKey = "$ResponseClass" }).ToStatement(),
                Expression.Assign(hostname, FickleExpression.Call(options, typeof(string), "objectForKey", Expression.Constant("hostname"))),
                Expression.IfThen(Expression.Equal(hostname, Expression.Constant(null)), Expression.Assign(hostname, Expression.Constant(declaredHostname)).ToStatement().ToBlock()),
                FickleExpression.Grouped
                (
                    FickleExpression.Call(options, "setObject", new
                    {
                        obj = parseResultBlock,
                        forKey = "$ParseResultBlock"
                    }).ToStatement(),
                    method.ReturnType.GetUnwrappedNullableType() == typeof(bool) ?
                    FickleExpression.Call(options, "setObject", new
                    {
                        obj = Expression.Convert(Expression.Constant(1), typeof(object))
                    }).ToStatement() : null
                ),
                Expression.Assign(url, Expression.Call(null, methodInfo, args)),
                Expression.Assign(client, FickleExpression.Call(Expression.Variable(currentType, "self"), "PKWebServiceClient", "createClientWithURL", new
                {
                    url,
                    options
                })),
                Expression.Assign(FickleExpression.Property(client, currentType, "delegate"), self),
                clientCallExpression
            );

            return new MethodDefinitionExpression(methodName, newParameters.ToReadOnlyCollection(), typeof(void), block, false, null);
        }
        protected virtual MethodDefinitionExpression CreateSerializeRequestMethod()
        {
            var error = FickleExpression.Variable("NSError", "error");
            var retval = FickleExpression.Variable("NSData", "retval");
            var client = Expression.Parameter(FickleType.Define("PKWebServiceClient"), "client");
            var requestObject = Expression.Parameter(FickleType.Define("id"), "serializeRequest");
            var parameters = new[] { new FickleParameterInfo(FickleType.Define("NSDictionary"), "obj"), new FickleParameterInfo(FickleType.Define("NSJSONWritingOptions", false, true), "options"), new FickleParameterInfo(FickleType.Define("NSError"), "error", true) };
            var methodInfo = new FickleMethodInfo(FickleType.Define("NSJSONSerialization"), FickleType.Define("NSData"), "dataWithJSONObject", parameters, true);

            var body = FickleExpression.Block
            (
                new[] { error, retval },
                Expression.IfThenElse
                (
                    Expression.TypeIs(requestObject, typeof(string)),
                    Expression.Assign(retval, FickleExpression.Call(requestObject, typeof(string), "dataUsingEncoding", Expression.Variable(typeof(int), "NSUTF8StringEncoding"))).ToStatement().ToBlock(),
                    Expression.Assign(retval, Expression.Call(methodInfo, requestObject, Expression.Convert(Expression.Variable(typeof(int), "NSJSONReadingAllowFragments"), FickleType.Define("NSJSONWritingOptions", false, true)), error)).ToStatement().ToBlock()
                ),
                FickleExpression.Return(retval)
            );

            return new MethodDefinitionExpression
            (
                "webServiceClient",
                new [] { client, requestObject }.ToReadOnlyCollection<Expression>(),
                retval.Type,
                body,
                false,
                null
            );
        }
        protected override Expression VisitMethodDefinitionExpression(MethodDefinitionExpression method)
        {
            ParameterExpression optionsParameter;
            var self = Expression.Variable(currentType, "self");
            var gatewayCall = (MethodDefinitionExpression)this.CreateGatewayCallMethod(method, out optionsParameter);
            var newArgs = gatewayCall.Parameters.Select(c => c != optionsParameter ? c : Expression.Constant(null, c.Type)).ToList();
            var newMethodParams = gatewayCall.Parameters.Where(c => c != optionsParameter).ToList();

            var methodInfo = new FickleMethodInfo(self.Type, gatewayCall.ReturnType, gatewayCall.Name, gatewayCall.Parameters.Cast<ParameterExpression>().Select(c => new FickleParameterInfo(c.Type, c.Name)).ToArray());
            var newBody = Expression.Call(self, methodInfo, newArgs).ToStatementBlock();
            var gatewayCallMethodWithoutOptions = new MethodDefinitionExpression(gatewayCall.Name, newMethodParams, gatewayCall.ReturnType, newBody, false, gatewayCall.RawAttributes, gatewayCall.Attributes);

            return new GroupedExpressionsExpression(new [] { gatewayCall, gatewayCallMethodWithoutOptions }, GroupedExpressionsExpressionStyle.Wide);
        }
        protected Expression CreateGatewayCallMethod(MethodDefinitionExpression method, out ParameterExpression optionsParameter)
        {
            var methodName = method.Name.ToCamelCase();

            methodCount++;

            var self = Expression.Variable(currentType, "self");
            var hostname = Expression.Variable(typeof(string), "hostname");
            var port = Expression.Variable(typeof(string), "port");
            var protocol = Expression.Variable(typeof(string), "protocol");
            var optionsParam = FickleExpression.Parameter("NSDictionary", "options");
            var localOptions = FickleExpression.Variable("NSMutableDictionary", "localOptions");
            var requestObject = FickleExpression.Variable("NSObject", "requestObject");
            var requestObjectValue = (Expression)Expression.Constant(null, typeof(object));
            var url = Expression.Variable(typeof(string), "url");
            var responseType = ObjectiveBinderHelpers.GetWrappedResponseType(this.CodeGenerationContext, method.ReturnType);
            var variables = new [] { url, localOptions, protocol, hostname, requestObject, port };
            var declaredHostname = currentTypeDefinitionExpression.Attributes["Hostname"];
            var declaredPath = method.Attributes["Path"];
            var path = StringUriUtils.Combine("%@://%@%@", declaredPath);
            var httpMethod = method.Attributes["Method"];
            var declaredProtocol = Convert.ToBoolean(method.Attributes["Secure"]) ? "https" : "http";
            var retryBlockVariable = Expression.Variable(new FickleDelegateType(typeof(void), new FickleParameterInfo(FickleType.Define("id"), "block")), "retryBlock");
            var retryBlockParameter = Expression.Variable(FickleType.Define("id"), "block");

            var parametersByName = method.Parameters.ToDictionary(c => ((ParameterExpression)c).Name, c => (ParameterExpression)c, StringComparer.InvariantCultureIgnoreCase);

            var formatInfo = ObjectiveStringFormatInfo.GetObjectiveStringFormatInfo(path, c => parametersByName[c]);

            var args = formatInfo.ValueExpressions;
            var parameters = formatInfo.ParameterExpressions;

            var parameterInfos = new List<FickleParameterInfo>
            {
                new ObjectiveParameterInfo(typeof(string), "s"),
                new ObjectiveParameterInfo(typeof(string), "protocol", true),
                new ObjectiveParameterInfo(typeof(string), "hostname", true),
                new ObjectiveParameterInfo(typeof(string), "port", true),
            };

            parameterInfos.AddRange(parameters.Select(c => new ObjectiveParameterInfo(c.Type, c.Name, true)));

            var methodInfo = new FickleMethodInfo(typeof(string), typeof(string), "stringWithFormat", parameterInfos.ToArray(), true);

            args.InsertRange(0, new Expression[] { Expression.Constant(formatInfo.Format), protocol, hostname, port });

            var newParameters = new List<Expression>(method.Parameters) { optionsParam };
            var callback = Expression.Parameter(new FickleDelegateType(typeof(void), new FickleParameterInfo(responseType, "response")), "callback");

            newParameters.Add(callback);

            Expression blockArg = Expression.Parameter(FickleType.Define("id"), "arg1");

            var returnType = method.ReturnType;

            if (ObjectiveBinderHelpers.NeedsValueResponseWrapper(method.ReturnType))
            {
                returnType = FickleType.Define(ObjectiveBinderHelpers.GetValueResponseWrapperTypeName(method.ReturnType));
            }

            var responseFilter = FickleExpression.Property(self, "FKGatewayResponseFilter", "responseFilter");
            var conversion = Expression.Convert(blockArg, returnType);

            var innerRetryBlockBody = FickleExpression.Block
            (
                FickleExpression.Call(Expression.Convert(retryBlockParameter, retryBlockVariable.Type), typeof(void), "Invoke", new { block = retryBlockParameter }).ToStatement()
            );

            var innerRetryBlock = (Expression)FickleExpression.SimpleLambda
            (
                typeof(void), innerRetryBlockBody, new Expression[0]
            );

            var body = FickleExpression.GroupedWide
            (
                Expression.IfThen(Expression.NotEqual(responseFilter, Expression.Constant(null, responseFilter.Type)), Expression.Assign(blockArg, FickleExpression.Call(responseFilter, typeof(object), "gateway", new { value = self, receivedResponse = blockArg, fromRequestURL = url, withRequestObject = requestObject, retryBlock = innerRetryBlock, andOptions = localOptions })).ToStatementBlock()),
                Expression.IfThen
                (
                    Expression.AndAlso(Expression.NotEqual(blockArg, Expression.Constant(null, blockArg.Type)), Expression.NotEqual(callback, Expression.Constant(null, callback.Type))),
                    FickleExpression.Call(callback, "Invoke", conversion).ToStatementBlock()
                )
            );

            var conversionBlock = FickleExpression.SimpleLambda(null, body, new Expression[0], blockArg);

            var error = FickleExpression.Variable("NSError", "error");

            Expression parseResultBlock;

            var nsdataParam = Expression.Parameter(FickleType.Define("NSData"), "data");
            var clientParam = Expression.Parameter(FickleType.Define("PKWebServiceClient"), "client");
            var jsonObjectWithDataParameters = new[] { new FickleParameterInfo(FickleType.Define("NSDictionary"), "obj"), new FickleParameterInfo(typeof(int), "options"), new FickleParameterInfo(FickleType.Define("NSError", true), "error", true) };
            var objectWithDataMethodInfo = new FickleMethodInfo(FickleType.Define("NSJSONSerialization"), FickleType.Define("NSData"), "JSONObjectWithData", jsonObjectWithDataParameters, true);
            var deserializedValue = Expression.Parameter(FickleType.Define("id"), "deserializedValue");

            var parseErrorResult = FickleExpression.Call(self, "webServiceClient", new
            {
                clientParam,
                createErrorResponseWithErrorCode = "JsonDeserializationError",
                andMessage = FickleExpression.Call(error, "localizedDescription", null)
            });

            if (method.ReturnType == typeof(void))
            {
                var responseObject = FickleExpression.Variable(responseType, "responseObject");

                parseResultBlock = FickleExpression.SimpleLambda
                (
                    FickleType.Define("id"),
                    FickleExpression.GroupedWide
                    (
                        Expression.Assign(responseObject, FickleExpression.New(responseType, "init", null)).ToStatement(),
                        Expression.Assign(deserializedValue, Expression.Call(objectWithDataMethodInfo, nsdataParam, FickleExpression.Variable(typeof(int), "NSJSONReadingAllowFragments"), error)).ToStatement(),
                        Expression.IfThen(Expression.Equal(deserializedValue, Expression.Constant(null)), FickleExpression.Return(parseErrorResult).ToStatementBlock()),
                        FickleExpression.Return(responseObject).ToStatement()
                    ),
                    new Expression[] { deserializedValue, responseObject, error },
                    clientParam, nsdataParam
                );
            }
            else if (TypeSystem.IsPrimitiveType(method.ReturnType) || method.ReturnType is FickleListType)
            {
                var responseObject = FickleExpression.Variable(responseType, "responseObject");
                var needToBoxValue = ObjectiveBinderHelpers.ValueResponseValueNeedsBoxing(method.ReturnType);

                parseResultBlock = FickleExpression.SimpleLambda
                (
                    FickleType.Define("id"),
                    FickleExpression.GroupedWide
                    (
                        Expression.Assign(responseObject, FickleExpression.New(responseType, "init", null)).ToStatement(),
                        Expression.Assign(deserializedValue, Expression.Call(objectWithDataMethodInfo, nsdataParam, FickleExpression.Variable(typeof(int), "NSJSONReadingAllowFragments"), error)).ToStatement(),
                        Expression.IfThen(Expression.Equal(deserializedValue, Expression.Constant(null)), FickleExpression.Return(parseErrorResult).ToStatementBlock()),
                        PropertiesFromDictionaryExpressonBinder.GetDeserializeExpressionProcessValueDeserializer(method.ReturnType, deserializedValue, c => FickleExpression.Call(responseObject, typeof(void), "setValue", needToBoxValue  ? Expression.Convert(c, typeof(object)) : c).ToStatement()),
                        FickleExpression.Return(responseObject).ToStatement()
                    ),
                    new Expression[] { deserializedValue, responseObject, error },
                    clientParam, nsdataParam
                );
            }
            else
            {
                parseResultBlock = FickleExpression.SimpleLambda
                (
                    FickleType.Define("id"),
                    FickleExpression.GroupedWide
                    (
                        Expression.Assign(deserializedValue, Expression.Call(objectWithDataMethodInfo, nsdataParam, FickleExpression.Variable(typeof(int), "NSJSONReadingAllowFragments"), error)).ToStatement(),
                        PropertiesFromDictionaryExpressonBinder.GetDeserializeExpressionProcessValueDeserializer(method.ReturnType, deserializedValue, c => FickleExpression.Return(c).ToStatement()),
                        FickleExpression.Return(parseErrorResult).ToStatement()
                    ),
                    new Expression[] { deserializedValue, error },
                    clientParam, nsdataParam
                );
            }

            var uniqueNameMaker = new UniqueNameMaker(c => newParameters.Cast<ParameterExpression>().Any(d => d.Name.EqualsIgnoreCaseInvariant(c)));

            var key = FickleExpression.Variable(typeof(string), uniqueNameMaker.Make("key"));

            var integrateOptions = FickleExpression.ForEach
            (
                key,
                optionsParam,
                FickleExpression.Call(localOptions, typeof(void), "setObject", new { value = FickleExpression.Call(optionsParam, typeof(object), "objectForKey", key), forKey = key }).ToStatementBlock()
            );

            parseResultBlock = FickleExpression.Call(parseResultBlock, parseResultBlock.Type, "copy", null);

            var client = Expression.Variable(FickleType.Define(this.CodeGenerationContext.Options.ServiceClientTypeName ?? "PKWebServiceClient"), "client");

            Expression clientCallExpression;

            if (httpMethod.Equals("get", StringComparison.InvariantCultureIgnoreCase))
            {
                clientCallExpression = FickleExpression.Call(client, "getWithCallback", conversionBlock);
            }
            else
            {
                var contentParameterName = method.Attributes["Content"];
                var contentFormat = method.Attributes["ContentFormat"];

                if (string.IsNullOrEmpty(contentParameterName))
                {
                    clientCallExpression = FickleExpression.Call(client, "postWithRequestObject", new
                    {
                        requestObject,
                        andCallback = conversionBlock
                    });
                }
                else
                {
                    var content = parametersByName[contentParameterName];

                    requestObjectValue = content.Type == typeof(byte[]) ? (Expression)content : FickleExpression.Call(self, typeof(object), this.GetNormalizeRequestMethodName(content.Type, contentFormat), new { serializeRequest = Expression.Convert(content, typeof(object)), paramName = Expression.Constant(contentParameterName) });

                    clientCallExpression = FickleExpression.Call(client, "postWithRequestObject", new
                    {
                        requestObject,
                        andCallback = conversionBlock
                    });
                }
            }

            var retryBlock = (Expression)FickleExpression.SimpleLambda
            (
                typeof(void),
                FickleExpression.StatementisedGroupedExpression
                (
                    Expression.Assign(client, FickleExpression.Call(Expression.Variable(currentType, "self"), "PKWebServiceClient", "createClientWithURL", new
                    {
                        url,
                        options = localOptions
                    })),
                    Expression.Assign(FickleExpression.Property(client, currentType, "delegate"), self),
                    clientCallExpression
                ),
                new Expression[] { client },
                retryBlockParameter
            );

            retryBlock = FickleExpression.Call(retryBlock, retryBlock.Type, "copy", null);

            var block = FickleExpression.Block
            (
                variables.Concat(retryBlockVariable).ToArray(),
                Expression.Assign(requestObject, requestObjectValue),
                Expression.Assign(callback, FickleExpression.Call(callback, callback.Type, "copy", null)),
                Expression.Assign(localOptions, FickleExpression.Call(FickleExpression.Property(self, FickleType.Define("NSDictionary"), "options"), "NSMutableDictionary", "mutableCopyWithZone", new
                {
                    zone = Expression.Constant(null, FickleType.Define("NSZone"))
                })),
                Expression.IfThen(Expression.NotEqual(requestObject, Expression.Constant(null)), FickleExpression.Call(localOptions, typeof(void), "setObject", new { value = requestObject, forKey = "$RequestObject" }).ToStatementBlock()),
                FickleExpression.Call(localOptions, typeof(void), "setObject", new { value = FickleExpression.StaticCall(responseType, "class", null), forKey = "$ResponseClass" }).ToStatement(),
                Expression.Assign(hostname, FickleExpression.Call(localOptions, typeof(string), "objectForKey", Expression.Constant("hostname"))),
                Expression.IfThen(Expression.Equal(hostname, Expression.Constant(null)), Expression.Assign(hostname, Expression.Constant(declaredHostname)).ToStatementBlock()),
                Expression.Assign(protocol, FickleExpression.Call(localOptions, typeof(string), "objectForKey", Expression.Constant("protocol"))),
                Expression.IfThen(Expression.Equal(protocol, Expression.Constant(null)), Expression.Assign(protocol, Expression.Constant(declaredProtocol)).ToStatementBlock()),
                Expression.Assign(port, FickleExpression.Call(FickleExpression.Call(localOptions, FickleType.Define("NSNumber"), "objectForKey", Expression.Constant("port")), typeof(string), "stringValue", null)),
                Expression.IfThenElse(Expression.Equal(port, Expression.Constant(null)), Expression.Assign(port, Expression.Constant("")).ToStatementBlock(), Expression.Assign(port, FickleExpression.Call(Expression.Constant(":"), typeof(string), "stringByAppendingString", port)).ToStatementBlock()),
                FickleExpression.Grouped
                (
                    FickleExpression.Call(localOptions, "setObject", new
                    {
                        obj = parseResultBlock,
                        forKey = "$ParseResultBlock"
                    }).ToStatement(),
                    method.ReturnType.GetUnwrappedNullableType() == typeof(bool) ?
                    FickleExpression.Call(localOptions, "setObject", new
                    {
                        obj = Expression.Convert(Expression.Constant(1), typeof(object))
                    }).ToStatement() : null
                ),
                Expression.Assign(url, Expression.Call(null, methodInfo, args)),
                FickleExpression.Call(localOptions, typeof(void), "setObject", new { value = url, forKey = "$RequestURL" }).ToStatement(),
                integrateOptions,
                Expression.Assign(retryBlockVariable, retryBlock),
                FickleExpression.Call(retryBlockVariable, typeof(void), "Invoke", retryBlockVariable).ToStatement()
            );

            optionsParameter = optionsParam;

            return new MethodDefinitionExpression(methodName, newParameters.ToReadOnlyCollection(), typeof(void), block, false, null);
        }
        internal static Expression GetDeserializeExpressionProcessValueDeserializer(Type valueType, Expression value, Func <Expression, Expression> processOutputValue)
        {
            Expression outputValue;
            var        nsNull = FickleExpression.StaticCall("NSNull", FickleType.Define("id"), "null", null);

            if (TypeSystem.IsPrimitiveType(valueType))
            {
                ConditionalExpression ifExpression;
                var underlyingType = valueType.GetUnwrappedNullableType();

                if (underlyingType.IsNumericType() || underlyingType == typeof(bool))
                {
                    var typeToCompare = new FickleType("NSNumber");

                    if (underlyingType.IsEnum && valueType.GetUnderlyingType() == null)
                    {
                        outputValue = Expression.Convert(FickleExpression.Call(value, typeof(int), "intValue", null), valueType);
                    }
                    else
                    {
                        outputValue = Expression.Convert(value, valueType);
                    }

                    ifExpression = Expression.IfThen(Expression.TypeIs(value, typeToCompare), processOutputValue(outputValue).ToBlock());
                }
                else if (underlyingType.IsEnum)
                {
                    Expression firstIf;

                    if (valueType.IsNullable())
                    {
                        outputValue = Expression.Convert(value, valueType);

                        firstIf = Expression.IfThen(Expression.TypeIs(value, FickleType.Define("NSNumber")), processOutputValue(outputValue).ToBlock());
                    }
                    else
                    {
                        outputValue = Expression.Convert(FickleExpression.Call(value, typeof(int), "intValue", null), valueType);

                        firstIf = Expression.IfThen(Expression.TypeIs(value, FickleType.Define("NSNumber")), processOutputValue(outputValue).ToBlock());
                    }

                    var parsedValue = FickleExpression.Variable(underlyingType, "parsedValue");
                    var success     = Expression.Variable(typeof(bool), "success");

                    var parameters = new[] { new FickleParameterInfo(typeof(string), "value"), new FickleParameterInfo(underlyingType, "outValue", true) };
                    var methodInfo = new FickleMethodInfo(null, typeof(bool), TypeSystem.GetPrimitiveName(underlyingType, true) + "TryParse", parameters, true);

                    if (valueType.IsNullable())
                    {
                        outputValue = Expression.Convert(Expression.Condition(success, Expression.Convert(Expression.Convert(parsedValue, valueType), FickleType.Define("id")), Expression.Convert(FickleExpression.StaticCall(FickleType.Define("NSNull"), valueType, "null", null), FickleType.Define("id"))), valueType);
                    }
                    else
                    {
                        outputValue = Expression.Convert(parsedValue, valueType);
                    }

                    var secondIf = Expression.IfThenElse
                                   (
                        Expression.TypeIs(value, FickleType.Define("NSString")),
                        FickleExpression.Block
                        (
                            new ParameterExpression[]
                    {
                        parsedValue,
                        success
                    },
                            Expression.IfThen
                            (
                                Expression.Not(Expression.Assign(success, Expression.Call(null, methodInfo, Expression.Convert(value, typeof(string)), parsedValue))),
                                Expression.Assign(parsedValue, Expression.Convert(Expression.Constant(0), underlyingType)).ToStatementBlock()
                            ),
                            processOutputValue(outputValue)
                        ),
                        firstIf
                                   );

                    ifExpression = secondIf;
                }
                else
                {
                    ifExpression = Expression.IfThen(Expression.TypeIs(value, typeof(string)), processOutputValue(Expression.Convert(Expression.Convert(value, typeof(object)), valueType)).ToBlock());
                }

                if (valueType.IsNullable())
                {
                    ifExpression = Expression.IfThenElse
                                   (
                        Expression.Equal(value, nsNull),
                        processOutputValue(Expression.Convert(nsNull, valueType)).ToBlock(),
                        ifExpression
                                   );
                }

                return(ifExpression);
            }
            else if (valueType is FickleType && ((FickleType)valueType).ServiceClass != null)
            {
                outputValue = FickleExpression.New(valueType, "initWithPropertyDictionary", FickleExpression.Convert(value, "NSDictionary"));

                return(Expression.IfThen(Expression.TypeIs(value, FickleType.Define("NSDictionary")), processOutputValue(outputValue).ToBlock()));
            }
            else if (valueType is FickleType && ((FickleType)valueType).ServiceEnum != null)
            {
                return(Expression.IfThen(Expression.TypeIs(value, FickleType.Define("NSNumber")), processOutputValue(value).ToBlock()));
            }
            else if (valueType is FickleListType)
            {
                var listType = valueType as FickleListType;

                var arrayVar  = FickleExpression.Variable("NSMutableArray", "array");
                var variables = new[] { arrayVar };
                var arrayItem = FickleExpression.Parameter(FickleType.Define("id"), "arrayItem");

                var forEachBody = GetDeserializeExpressionProcessValueDeserializer(listType.ListElementType, arrayItem, c => FickleExpression.Call(arrayVar, "addObject", Expression.Convert(c, typeof(object))).ToStatement());

                var ifThen = Expression.IfThen
                             (
                    Expression.TypeIs(value, FickleType.Define("NSArray")),
                    FickleExpression.Block
                    (
                        variables,
                        Expression.Assign(arrayVar, FickleExpression.New("NSMutableArray", "initWithCapacity", FickleExpression.Call(Expression.Convert(value, FickleType.Define("NSArray")), typeof(int), "count", null))).ToStatement(),
                        FickleExpression.ForEach(arrayItem, value, FickleExpression.Block(forEachBody)),
                        processOutputValue(Expression.Convert(arrayVar, valueType))
                    )
                             );

                return(ifThen);
            }
            else
            {
                throw new InvalidOperationException("Unsupported property type: " + valueType);
            }
        }
        protected Expression CreateGatewayCallMethod(MethodDefinitionExpression method, out ParameterExpression optionsParameter)
        {
            var methodName = method.Name.ToCamelCase();

            methodCount++;

            var self               = Expression.Variable(currentType, "self");
            var hostname           = Expression.Variable(typeof(string), "hostname");
            var port               = Expression.Variable(typeof(string), "port");
            var protocol           = Expression.Variable(typeof(string), "protocol");
            var optionsParam       = FickleExpression.Parameter("NSDictionary", "options");
            var localOptions       = FickleExpression.Variable("NSMutableDictionary", "localOptions");
            var requestObject      = FickleExpression.Variable("NSObject", "requestObject");
            var requestObjectValue = (Expression)Expression.Constant(null, typeof(object));
            var url                 = Expression.Variable(typeof(string), "url");
            var responseType        = ObjectiveBinderHelpers.GetWrappedResponseType(this.CodeGenerationContext, method.ReturnType);
            var variables           = new [] { url, localOptions, protocol, hostname, requestObject, port };
            var declaredHostname    = currentTypeDefinitionExpression.Attributes["Hostname"];
            var declaredPath        = method.Attributes["Path"];
            var path                = StringUriUtils.Combine("%@://%@%@", declaredPath);
            var httpMethod          = method.Attributes["Method"];
            var declaredProtocol    = Convert.ToBoolean(method.Attributes["Secure"]) ? "https" : "http";
            var retryBlockVariable  = Expression.Variable(new FickleDelegateType(typeof(void), new FickleParameterInfo(FickleType.Define("id"), "block")), "retryBlock");
            var retryBlockParameter = Expression.Variable(FickleType.Define("id"), "block");

            var parametersByName = method.Parameters.ToDictionary(c => ((ParameterExpression)c).Name, c => (ParameterExpression)c, StringComparer.InvariantCultureIgnoreCase);

            var formatInfo = ObjectiveStringFormatInfo.GetObjectiveStringFormatInfo(path, c => parametersByName[c]);

            var args       = formatInfo.ValueExpressions;
            var parameters = formatInfo.ParameterExpressions;

            var parameterInfos = new List <FickleParameterInfo>
            {
                new ObjectiveParameterInfo(typeof(string), "s"),
                new ObjectiveParameterInfo(typeof(string), "protocol", true),
                new ObjectiveParameterInfo(typeof(string), "hostname", true),
                new ObjectiveParameterInfo(typeof(string), "port", true),
            };

            parameterInfos.AddRange(parameters.Select(c => new ObjectiveParameterInfo(c.Type, c.Name, true)));

            var methodInfo = new FickleMethodInfo(typeof(string), typeof(string), "stringWithFormat", parameterInfos.ToArray(), true);

            args.InsertRange(0, new Expression[] { Expression.Constant(formatInfo.Format), protocol, hostname, port });

            var newParameters = new List <Expression>(method.Parameters)
            {
                optionsParam
            };
            var callback = Expression.Parameter(new FickleDelegateType(typeof(void), new FickleParameterInfo(responseType, "response")), "callback");

            newParameters.Add(callback);

            Expression blockArg = Expression.Parameter(FickleType.Define("id"), "arg1");

            var returnType = method.ReturnType;

            if (ObjectiveBinderHelpers.NeedsValueResponseWrapper(method.ReturnType))
            {
                returnType = FickleType.Define(ObjectiveBinderHelpers.GetValueResponseWrapperTypeName(method.ReturnType));
            }

            var responseFilter = FickleExpression.Property(self, "FKGatewayResponseFilter", "responseFilter");
            var conversion     = Expression.Convert(blockArg, returnType);

            var innerRetryBlockBody = FickleExpression.Block
                                      (
                FickleExpression.Call(Expression.Convert(retryBlockParameter, retryBlockVariable.Type), typeof(void), "Invoke", new { block = retryBlockParameter }).ToStatement()
                                      );

            var innerRetryBlock = (Expression)FickleExpression.SimpleLambda
                                  (
                typeof(void), innerRetryBlockBody, new Expression[0]
                                  );

            var body = FickleExpression.GroupedWide
                       (
                Expression.IfThen(Expression.NotEqual(responseFilter, Expression.Constant(null, responseFilter.Type)), Expression.Assign(blockArg, FickleExpression.Call(responseFilter, typeof(object), "gateway", new { value = self, receivedResponse = blockArg, fromRequestURL = url, withRequestObject = requestObject, retryBlock = innerRetryBlock, andOptions = localOptions })).ToStatementBlock()),
                Expression.IfThen
                (
                    Expression.AndAlso(Expression.NotEqual(blockArg, Expression.Constant(null, blockArg.Type)), Expression.NotEqual(callback, Expression.Constant(null, callback.Type))),
                    FickleExpression.Call(callback, "Invoke", conversion).ToStatementBlock()
                )
                       );

            var conversionBlock = FickleExpression.SimpleLambda(null, body, new Expression[0], blockArg);

            var error = FickleExpression.Variable("NSError", "error");

            Expression parseResultBlock;

            var nsdataParam = Expression.Parameter(FickleType.Define("NSData"), "data");
            var clientParam = Expression.Parameter(FickleType.Define("PKWebServiceClient"), "client");
            var jsonObjectWithDataParameters = new[] { new FickleParameterInfo(FickleType.Define("NSDictionary"), "obj"), new FickleParameterInfo(typeof(int), "options"), new FickleParameterInfo(FickleType.Define("NSError", true), "error", true) };
            var objectWithDataMethodInfo     = new FickleMethodInfo(FickleType.Define("NSJSONSerialization"), FickleType.Define("NSData"), "JSONObjectWithData", jsonObjectWithDataParameters, true);
            var deserializedValue            = Expression.Parameter(FickleType.Define("id"), "deserializedValue");

            var parseErrorResult = FickleExpression.Call(self, "webServiceClient", new
            {
                clientParam,
                createErrorResponseWithErrorCode = "JsonDeserializationError",
                andMessage = FickleExpression.Call(error, "localizedDescription", null)
            });

            if (method.ReturnType == typeof(void))
            {
                var responseObject = FickleExpression.Variable(responseType, "responseObject");

                parseResultBlock = FickleExpression.SimpleLambda
                                   (
                    FickleType.Define("id"),
                    FickleExpression.GroupedWide
                    (
                        Expression.Assign(responseObject, FickleExpression.New(responseType, "init", null)).ToStatement(),
                        Expression.Assign(deserializedValue, Expression.Call(objectWithDataMethodInfo, nsdataParam, FickleExpression.Variable(typeof(int), "NSJSONReadingAllowFragments"), error)).ToStatement(),
                        Expression.IfThen(Expression.Equal(deserializedValue, Expression.Constant(null)), FickleExpression.Return(parseErrorResult).ToStatementBlock()),
                        FickleExpression.Return(responseObject).ToStatement()
                    ),
                    new Expression[] { deserializedValue, responseObject, error },
                    clientParam, nsdataParam
                                   );
            }
            else if (TypeSystem.IsPrimitiveType(method.ReturnType) || method.ReturnType is FickleListType)
            {
                var responseObject = FickleExpression.Variable(responseType, "responseObject");
                var needToBoxValue = ObjectiveBinderHelpers.ValueResponseValueNeedsBoxing(method.ReturnType);

                parseResultBlock = FickleExpression.SimpleLambda
                                   (
                    FickleType.Define("id"),
                    FickleExpression.GroupedWide
                    (
                        Expression.Assign(responseObject, FickleExpression.New(responseType, "init", null)).ToStatement(),
                        Expression.Assign(deserializedValue, Expression.Call(objectWithDataMethodInfo, nsdataParam, FickleExpression.Variable(typeof(int), "NSJSONReadingAllowFragments"), error)).ToStatement(),
                        Expression.IfThen(Expression.Equal(deserializedValue, Expression.Constant(null)), FickleExpression.Return(parseErrorResult).ToStatementBlock()),
                        PropertiesFromDictionaryExpressonBinder.GetDeserializeExpressionProcessValueDeserializer(method.ReturnType, deserializedValue, c => FickleExpression.Call(responseObject, typeof(void), "setValue", needToBoxValue  ? Expression.Convert(c, typeof(object)) : c).ToStatement()),
                        FickleExpression.Return(responseObject).ToStatement()
                    ),
                    new Expression[] { deserializedValue, responseObject, error },
                    clientParam, nsdataParam
                                   );
            }
            else
            {
                parseResultBlock = FickleExpression.SimpleLambda
                                   (
                    FickleType.Define("id"),
                    FickleExpression.GroupedWide
                    (
                        Expression.Assign(deserializedValue, Expression.Call(objectWithDataMethodInfo, nsdataParam, FickleExpression.Variable(typeof(int), "NSJSONReadingAllowFragments"), error)).ToStatement(),
                        PropertiesFromDictionaryExpressonBinder.GetDeserializeExpressionProcessValueDeserializer(method.ReturnType, deserializedValue, c => FickleExpression.Return(c).ToStatement()),
                        FickleExpression.Return(parseErrorResult).ToStatement()
                    ),
                    new Expression[] { deserializedValue, error },
                    clientParam, nsdataParam
                                   );
            }

            var uniqueNameMaker = new UniqueNameMaker(c => newParameters.Cast <ParameterExpression>().Any(d => d.Name.EqualsIgnoreCaseInvariant(c)));

            var key = FickleExpression.Variable(typeof(string), uniqueNameMaker.Make("key"));

            var integrateOptions = FickleExpression.ForEach
                                   (
                key,
                optionsParam,
                FickleExpression.Call(localOptions, typeof(void), "setObject", new { value = FickleExpression.Call(optionsParam, typeof(object), "objectForKey", key), forKey = key }).ToStatementBlock()
                                   );

            parseResultBlock = FickleExpression.Call(parseResultBlock, parseResultBlock.Type, "copy", null);

            var client = Expression.Variable(FickleType.Define(this.CodeGenerationContext.Options.ServiceClientTypeName ?? "PKWebServiceClient"), "client");

            Expression clientCallExpression;

            if (httpMethod.Equals("get", StringComparison.InvariantCultureIgnoreCase))
            {
                clientCallExpression = FickleExpression.Call(client, "getWithCallback", conversionBlock);
            }
            else
            {
                var contentParameterName = method.Attributes["Content"];
                var contentFormat        = method.Attributes["ContentFormat"];

                if (string.IsNullOrEmpty(contentParameterName))
                {
                    clientCallExpression = FickleExpression.Call(client, "postWithRequestObject", new
                    {
                        requestObject,
                        andCallback = conversionBlock
                    });
                }
                else
                {
                    var content = parametersByName[contentParameterName];

                    requestObjectValue = content.Type == typeof(byte[]) ? (Expression)content : FickleExpression.Call(self, typeof(object), this.GetNormalizeRequestMethodName(content.Type, contentFormat), new { serializeRequest = Expression.Convert(content, typeof(object)), paramName = Expression.Constant(contentParameterName) });

                    clientCallExpression = FickleExpression.Call(client, "postWithRequestObject", new
                    {
                        requestObject,
                        andCallback = conversionBlock
                    });
                }
            }

            var retryBlock = (Expression)FickleExpression.SimpleLambda
                             (
                typeof(void),
                FickleExpression.StatementisedGroupedExpression
                (
                    Expression.Assign(client, FickleExpression.Call(Expression.Variable(currentType, "self"), "PKWebServiceClient", "createClientWithURL", new
            {
                url,
                options = localOptions
            })),
                    Expression.Assign(FickleExpression.Property(client, currentType, "delegate"), self),
                    clientCallExpression
                ),
                new Expression[] { client },
                retryBlockParameter
                             );

            retryBlock = FickleExpression.Call(retryBlock, retryBlock.Type, "copy", null);

            var block = FickleExpression.Block
                        (
                variables.Concat(retryBlockVariable).ToArray(),
                Expression.Assign(requestObject, requestObjectValue),
                Expression.Assign(callback, FickleExpression.Call(callback, callback.Type, "copy", null)),
                Expression.Assign(localOptions, FickleExpression.Call(FickleExpression.Property(self, FickleType.Define("NSDictionary"), "options"), "NSMutableDictionary", "mutableCopyWithZone", new
            {
                zone = Expression.Constant(null, FickleType.Define("NSZone"))
            })),
                Expression.IfThen(Expression.NotEqual(requestObject, Expression.Constant(null)), FickleExpression.Call(localOptions, typeof(void), "setObject", new { value = requestObject, forKey = "$RequestObject" }).ToStatementBlock()),
                FickleExpression.Call(localOptions, typeof(void), "setObject", new { value = FickleExpression.StaticCall(responseType, "class", null), forKey = "$ResponseClass" }).ToStatement(),
                Expression.Assign(hostname, FickleExpression.Call(localOptions, typeof(string), "objectForKey", Expression.Constant("hostname"))),
                Expression.IfThen(Expression.Equal(hostname, Expression.Constant(null)), Expression.Assign(hostname, Expression.Constant(declaredHostname)).ToStatementBlock()),
                Expression.Assign(protocol, FickleExpression.Call(localOptions, typeof(string), "objectForKey", Expression.Constant("protocol"))),
                Expression.IfThen(Expression.Equal(protocol, Expression.Constant(null)), Expression.Assign(protocol, Expression.Constant(declaredProtocol)).ToStatementBlock()),
                Expression.Assign(port, FickleExpression.Call(FickleExpression.Call(localOptions, FickleType.Define("NSNumber"), "objectForKey", Expression.Constant("port")), typeof(string), "stringValue", null)),
                Expression.IfThenElse(Expression.Equal(port, Expression.Constant(null)), Expression.Assign(port, Expression.Constant("")).ToStatementBlock(), Expression.Assign(port, FickleExpression.Call(Expression.Constant(":"), typeof(string), "stringByAppendingString", port)).ToStatementBlock()),
                FickleExpression.Grouped
                (
                    FickleExpression.Call(localOptions, "setObject", new
            {
                obj    = parseResultBlock,
                forKey = "$ParseResultBlock"
            }).ToStatement(),
                    method.ReturnType.GetUnwrappedNullableType() == typeof(bool) ?
                    FickleExpression.Call(localOptions, "setObject", new
            {
                obj = Expression.Convert(Expression.Constant(1), typeof(object))
            }).ToStatement() : null
                ),
                Expression.Assign(url, Expression.Call(null, methodInfo, args)),
                FickleExpression.Call(localOptions, typeof(void), "setObject", new { value = url, forKey = "$RequestURL" }).ToStatement(),
                integrateOptions,
                Expression.Assign(retryBlockVariable, retryBlock),
                FickleExpression.Call(retryBlockVariable, typeof(void), "Invoke", retryBlockVariable).ToStatement()
                        );

            optionsParameter = optionsParam;

            return(new MethodDefinitionExpression(methodName, newParameters.ToReadOnlyCollection(), typeof(void), block, false, null));
        }