public override SelectParameterResult TryCast(BindingData bindingData)
        {
            var parameterRequiringValidation = bindingData.parameterRequiringValidation;
            var baseValue = base.TryCast(bindingData);

            if (baseValue.valid)
            {
                return(baseValue);
            }

            baseValue.value     = GetValue();
            baseValue.valid     = true;
            baseValue.fromQuery = true;
            return(baseValue);

            object GetValue()
            {
                if (parameterRequiringValidation.ParameterType.IsSubClassOfGeneric(typeof(IRefOptional <>)))
                {
                    var instance = RefOptionalHelper.CreateEmpty(
                        parameterRequiringValidation.ParameterType.GenericTypeArguments.First());
                    return(instance);
                }

                return(parameterRequiringValidation.ParameterType.GetDefault());
            }
        }
        public override SelectParameterResult TryCast(BindingData bindingData)
        {
            var parameterRequiringValidation = bindingData.parameterRequiringValidation;
            var baseValue = base.TryCast(bindingData);

            if (baseValue.valid)
            {
                return(baseValue);
            }

            baseValue.valid    = true;
            baseValue.fromBody = true;
            baseValue.value    = GetValue();
            return(baseValue);

            object GetValue()
            {
                var parameterType = parameterRequiringValidation.ParameterType;

                if (parameterType.IsSubClassOfGeneric(typeof(IRefOptional <>)))
                {
                    var refType = parameterType.GenericTypeArguments.First();
                    var parameterTypeGeneric = RefOptionalHelper.CreateEmpty(refType);
                    return(parameterTypeGeneric);
                }

                if (parameterType.IsSubClassOfGeneric(typeof(IRefs <>)))
                {
                    var refType = parameterType.GenericTypeArguments.First();
                    var parameterTypeGeneric = typeof(Refs <>).MakeGenericType(new Type[] { refType });
                    var refIds           = new Guid[] { };
                    var refIdsLookupType = typeof(Func <,>).MakeGenericType(new Type[] { typeof(Guid), refType });
                    var refIdsLookup     = refIdsLookupType.GetDefault();
                    return(Activator.CreateInstance(
                               parameterTypeGeneric, new object[] { refIds }));
                }

                if (parameterType.IsSubClassOfGeneric(typeof(IDictionary <,>)))
                {
                    var parameterTypeGeneric = typeof(Dictionary <,>).MakeGenericType(parameterType.GenericTypeArguments);
                    return(Activator.CreateInstance(parameterTypeGeneric));
                }

                return(parameterType.GetDefault());
            }
        }
예제 #3
0
        public static object ReadJsonStatic(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (objectType == typeof(string))
            {
                if (reader.TokenType == JsonToken.String)
                {
                    var stringValue = reader.Value as string;
                    return(stringValue);
                }
                if (reader.TokenType == JsonToken.Null)
                {
                    return(null);
                }
            }

            if (objectType.IsSubClassOfGeneric(typeof(IReferenceable)))
            {
                if (objectType.IsSubClassOfGeneric(typeof(IRef <>)))
                {
                    var id = GetGuid();
                    if (id.IsDefault())
                    {
                        return(existingValue);
                    }
                    var refType = typeof(Ref <>).MakeGenericType(objectType.GenericTypeArguments);
                    return(Activator.CreateInstance(refType, id));
                }
            }

            if (objectType.IsSubClassOfGeneric(typeof(IReferenceableOptional)))
            {
                if (objectType.IsSubClassOfGeneric(typeof(IRefOptional <>)))
                {
                    var id = GetGuidMaybe();

                    if (id == null)
                    {
                        return(RefOptionalHelper.CreateEmpty(objectType.GenericTypeArguments.First()));
                    }

                    var refType = typeof(RefOptional <>).MakeGenericType(objectType.GenericTypeArguments);
                    return(Activator.CreateInstance(refType, id));
                }
            }

            if (objectType.IsSubClassOfGeneric(typeof(IReferences)))
            {
                if (objectType.IsSubClassOfGeneric(typeof(IRefs <>)))
                {
                    var ids     = GetGuids();
                    var refType = typeof(Refs <>).MakeGenericType(objectType.GenericTypeArguments);
                    return(Activator.CreateInstance(refType, ids));
                }
            }

            if (objectType.IsSubClassOfGeneric(typeof(IDictionary <,>)))
            {
                var dictionaryType = typeof(Dictionary <,>).MakeGenericType(objectType.GenericTypeArguments);
                var instance       = Activator.CreateInstance(dictionaryType);

                if (reader.TokenType == JsonToken.StartArray)
                {
                    var addMethod = dictionaryType.GetMethod("Add", BindingFlags.Public | BindingFlags.Instance);
                    while (reader.Read())
                    {
                        if (reader.TokenType == JsonToken.EndArray)
                        {
                            return(instance);
                        }

                        if (reader.TokenType != JsonToken.StartObject)
                        {
                            Console.WriteLine($"{reader.TokenType} != JsonToken.StartObject");
                            continue;
                        }
                        if (!reader.Read())
                        {
                            Console.WriteLine($"Reader terminated w/o finding JsonToken.EndArray");
                            return(instance);
                        }

                        if (!GetValue("key", objectType.GenericTypeArguments.First(), out object keyValue))
                        {
                            continue;
                        }

                        if (!GetValue("value", objectType.GenericTypeArguments.Last(), out object valueValue))
                        {
                            continue;
                        }

                        addMethod.Invoke(instance, new object[] { keyValue, valueValue });

                        if (reader.TokenType != JsonToken.EndObject)
                        {
                            Console.WriteLine($"{reader.TokenType} != JsonToken.EndObject");
                        }
                        //if (!reader.Read())
                        //{
                        //    Console.WriteLine($"Reader terminated w/o finding JsonToken.EndArray (after kvp).");
                        //    return instance;
                        //}
                        bool GetValue(string expectedName, Type type, out object value)
                        {
                            if (reader.TokenType != JsonToken.PropertyName)
                            {
                                value = null;
                                return(false);
                            }

                            var jprop = JProperty.Load(reader);
                            //Console.WriteLine($"Property[{jprop.Name}] = ({type.FullName}) {jprop.Value.Value<string>()}");
                            var name        = jprop.Name;
                            var valueReader = jprop.Value.CreateReader();

                            value = serializer.Deserialize(valueReader, type);
                            if (expectedName.Equals(name, StringComparison.InvariantCultureIgnoreCase))
                            {
                                return(true);
                            }

                            Console.WriteLine($"`{expectedName}` != `{name}`.");
                            return(false);
                        }
                    }
                    return(instance);
                }

                if (reader.TokenType == JsonToken.StartObject)
                {
                    if (!reader.Read())
                    {
                        return(instance);
                    }
                    var addMethod = dictionaryType.GetMethod("Add", BindingFlags.Public | BindingFlags.Instance);
                    do
                    {
                        Console.WriteLine($"{reader.Path} = ({reader.TokenType}) {reader.Value}");
                        if (reader.TokenType == JsonToken.EndObject)
                        {
                            return(instance);
                        }

                        if (reader.TokenType == JsonToken.PropertyName)
                        {
                            var jprop = JProperty.Load(reader);
                            var type  = objectType.GenericTypeArguments.Last();
                            Console.WriteLine($"Property[{jprop.Name}] = ({type.FullName}) {jprop.Value.Value<string>()}");
                            var name  = jprop.Name;
                            var value = serializer.Deserialize(jprop.Value.CreateReader(), type);
                            //var value = GetValue();
                            //object GetValue()
                            //{
                            //    if (jprop.Value.Type == JTokenType.String)
                            //        return jprop.Value.Value<string>();

                            //    if (jprop.Value.Type == JTokenType.Guid)
                            //        return jprop.Value.Value<Guid>();

                            //    return jprop.Value.Value<object>();
                            //}
                            //var value = serializer.Deserialize(reader, type);
                            addMethod.Invoke(instance, new object[] { name, value });
                            continue;
                        }
                        else
                        {
                            Console.WriteLine($"Using path for {reader.TokenType}: {reader.Path} = {reader.Value}");
                            addMethod.Invoke(instance, new object[] { reader.Path, reader.Value });
                            if (!reader.Read())
                            {
                                return(instance);
                            }
                        }
                    } while (true);
                }
                return(instance);
            }

            if (objectType == typeof(byte[]))
            {
                if (reader.TokenType == JsonToken.String)
                {
                    var bytesString = reader.Value as string;
                    return(bytesString.FromBase64String());
                }
            }

            if (objectType == typeof(Type))
            {
                if (reader.TokenType == JsonToken.String)
                {
                    var bytesString = reader.Value as string;
                    return(bytesString.GetClrType(
                               matched => matched,
                               () => default(Type)));
                }
                return(default(Type));
            }

            if (objectType.IsEnum)
            {
                if (reader.TokenType == JsonToken.String)
                {
                    var enumString = reader.Value as string;
                    if (Enum.TryParse(objectType, enumString, out object enumValue))
                    {
                        return(enumValue);
                    }
                }
            }

            return(existingValue);


            Guid GetGuid()
            {
                if (reader.TokenType == JsonToken.String)
                {
                    var guidString = reader.Value as string;
                    return(Guid.Parse(guidString));
                }
                if (reader.TokenType == JsonToken.Null)
                {
                    return(default(Guid));
                }
                throw new Exception();
            }

            Guid?GetGuidMaybe()
            {
                if (reader.TokenType == JsonToken.Null)
                {
                    return(default(Guid?));
                }
                return(GetGuid());
            }

            Guid[] GetGuids()
            {
                if (reader.TokenType == JsonToken.Null)
                {
                    return new Guid[] { }
                }
                ;

                IEnumerable <Guid> Enumerate()
                {
                    while (reader.TokenType != JsonToken.EndArray)
                    {
                        if (!reader.Read())
                        {
                            yield break;
                        }
                        var guidStr = reader.ReadAsString();
                        yield return(Guid.Parse(guidStr));
                    }
                }

                return(Enumerate().ToArray());
            }
        }
        public TResult Bind <TResult>(Type objectType, JsonReader reader,
                                      IApplication application,
                                      Func <object, TResult> onParsed,
                                      Func <string, TResult> onDidNotBind,
                                      Func <string, TResult> onBindingFailure)
        {
            if (objectType.IsSubClassOfGeneric(typeof(IRef <>)))
            {
                return(GetGuid(
                           (id) =>
                {
                    var refType = typeof(Ref <>).MakeGenericType(objectType.GenericTypeArguments);
                    var value = Activator.CreateInstance(refType, id);
                    return onParsed(value);
                },
                           onDidNotBind,
                           onBindingFailure));
            }

            if (objectType.IsSubClassOfGeneric(typeof(IRefOptional <>)))
            {
                return(GetGuidMaybe(
                           id =>
                {
                    if (!id.HasValue)
                    {
                        var emptyValue = RefOptionalHelper.CreateEmpty(objectType.GenericTypeArguments.First());
                        return onParsed(emptyValue);
                    }
                    var refType = typeof(RefOptional <>).MakeGenericType(objectType.GenericTypeArguments);
                    var value = Activator.CreateInstance(refType, id.Value);
                    return onParsed(value);
                }));
            }

            if (objectType.IsSubClassOfGeneric(typeof(IRefs <>)))
            {
                return(GetGuids(
                           ids =>
                {
                    var refType = typeof(Refs <>).MakeGenericType(objectType.GenericTypeArguments);
                    var value = Activator.CreateInstance(refType, ids);
                    return onParsed(value);
                }));
            }

            if (objectType.IsSubClassOfGeneric(typeof(IDictionary <,>)))
            {
                var dictionaryKeyType   = objectType.GenericTypeArguments[0];
                var dictionaryValueType = objectType.GenericTypeArguments[1];
                var dictionaryType      = typeof(Dictionary <,>).MakeGenericType(objectType.GenericTypeArguments);
                var instance            = Activator.CreateInstance(dictionaryType);

                if (reader.TokenType != JsonToken.StartObject)
                {
                    return(onParsed(instance));
                }
                if (!reader.Read())
                {
                    return(onParsed(instance));
                }

                var addMethod = dictionaryType.GetMethod("Add", BindingFlags.Public | BindingFlags.Instance);
                do
                {
                    if (reader.TokenType == JsonToken.EndObject)
                    {
                        return(onParsed(instance));
                    }
                    instance = StandardStringBindingsAttribute.BindDirect(dictionaryKeyType, reader.Path,
                                                                          keyValue =>
                    {
                        var valueValue = Bind(dictionaryValueType, reader,
                                              application,
                                              v => v,
                                              why => dictionaryValueType.GetDefault(),
                                              why => dictionaryValueType.GetDefault());
                        addMethod.Invoke(instance, new object[] { keyValue, valueValue });
                        return(instance);
                    },
                                                                          (why) => instance,
                                                                          (why) => instance);
                } while (reader.Read());
            }

            if (objectType == typeof(byte[]))
            {
                if (reader.TokenType == JsonToken.String)
                {
                    var bytesString = reader.Value as string;
                    var value       = bytesString.FromBase64String();
                    return(onParsed(value));
                }
            }

            if (objectType.IsAssignableFrom(typeof(Type)))
            {
                if (application is IApiApplication)
                {
                    var apiApplication = application as IApiApplication;
                    if (reader.TokenType == JsonToken.String)
                    {
                        var stringValue = reader.Value as string;
                        var(success, type) = apiApplication.GetResourceType(stringValue,
                                                                            type => (true, type),
                                                                            () => (false, default(Type)));
                        if (success)
                        {
                            return(onParsed(type));
                        }
                    }
                }
            }

            if (objectType.IsNullable())
            {
                var underlyingType = objectType.GetNullableUnderlyingType();
                if (reader.TokenType == JsonToken.Null)
                {
                    return(onParsed(objectType.GetDefault()));
                }
                return(Bind(underlyingType, reader,
                            application,
                            obj => onParsed(obj.AsNullable()),
                            (why) => onParsed(objectType.GetDefault()),
                            (why) => onParsed(objectType.GetDefault())));
            }

            // As a last ditch effort, see if the JToken deserialization will work.
            var token = JToken.ReadFrom(reader);

            return(BindDirect(objectType, token,
                              onParsed,
                              onDidNotBind,
                              onBindingFailure));


            TR GetGuid <TR>(
                Func <Guid, TR> onGot,
                Func <string, TR> onIgnored,
                Func <string, TR> onFailed)
            {
                if (reader.TokenType == JsonToken.String)
                {
                    var guidString = reader.Value as string;
                    if (Guid.TryParse(guidString, out Guid guid))
                    {
                        return(onGot(guid));
                    }
                    return(onFailed($"Cannot parse `{guidString}` as a Guid."));
                }
                if (reader.TokenType == JsonToken.StartObject)
                {
                    if (!reader.Read())
                    {
                        return(onIgnored("Empty object"));
                    }
                    return(GetGuid(
                               guid =>
                    {
                        while (reader.TokenType != JsonToken.EndObject)
                        {
                            if (!reader.Read())
                            {
                                break;
                            }
                        }
                        return onGot(guid);
                    },
                               onIgnored,
                               onFailed));
                }
                if (reader.TokenType == JsonToken.PropertyName)
                {
                    var propertyName = reader.Value as string;
                    if (!reader.Read())
                    {
                        return(onFailed("Property did not have value."));
                    }
                    if (propertyName.ToLower() == "uuid")
                    {
                        return(GetGuid(onGot, onIgnored, onFailed));
                    }
                    if (propertyName.ToLower() == "id")
                    {
                        return(GetGuid(onGot, onIgnored, onFailed));
                    }
                    if (!reader.Read())
                    {
                        return(onIgnored("'uuid' Property not found."));
                    }
                    return(GetGuid(onGot, onIgnored, onFailed));
                }
                return(onFailed($"Cannot decode token of type `{reader.TokenType}` to UUID."));
            }

            TResult GetGuidMaybe(Func <Guid?, TResult> callback)
            {
                if (reader.TokenType == JsonToken.Null)
                {
                    return(callback(default(Guid?)));
                }
                return(GetGuid(
                           (x) => callback(x),
                           onDidNotBind,
                           onBindingFailure));
            }

            TResult GetGuids(Func <Guid[], TResult> onGot)
            {
                if (reader.TokenType == JsonToken.Null)
                {
                    return(onGot(new Guid[] { }));
                }

                if (reader.TokenType == JsonToken.StartArray)
                {
                    var list = new List <Guid>();
                    while (reader.Read())
                    {
                        if (reader.TokenType == JsonToken.EndArray)
                        {
                            break;
                        }

                        var result = GetGuid(
                            g => new
                        {
                            why     = string.Empty,
                            g       = g.AsOptional(),
                            success = true,
                        },
                            why => new
                        {
                            why     = why,
                            g       = default(Guid?),
                            success = true,
                        },
                            why => new
                        {
                            why     = why,
                            g       = default(Guid?),
                            success = false,
                        });

                        if (!result.success)
                        {
                            return(onBindingFailure(result.why));
                        }
                        if (result.g.HasValue)
                        {
                            list.Add(result.g.Value);
                        }
                    }
                    return(onGot(list.ToArray()));
                }

                return(onBindingFailure($"Cannot decode token of type `{reader.TokenType}` to {objectType.FullName}."));
            }
        }
        public static TResult BindDirect <TResult>(Type type, JToken content,
                                                   Func <object, TResult> onParsed,
                                                   Func <string, TResult> onDidNotBind,
                                                   Func <string, TResult> onBindingFailure)
        {
            if (type.IsAssignableFrom(typeof(Guid)))
            {
                if (content.Type == JTokenType.Guid)
                {
                    var guidValue = content.Value <Guid>();
                    return(onParsed(guidValue));
                }
                if (content.Type == JTokenType.String)
                {
                    var stringValue = content.Value <string>();
                    if (Guid.TryParse(stringValue, out Guid guidValue))
                    {
                        return(onParsed(guidValue));
                    }
                    return(onBindingFailure($"Cannot convert `{stringValue}` to Guid."));
                }
                var webId = ReadObject <WebId>(content);
                if (webId.IsDefaultOrNull())
                {
                    return(onBindingFailure("Null value for GUID."));
                }
                var guidValueMaybe = webId.ToGuid();
                if (!guidValueMaybe.HasValue)
                {
                    return(onBindingFailure("Null WebId cannot be converted to a Guid."));
                }
                var webIdGuidValue = guidValueMaybe.Value;
                return(onParsed(webIdGuidValue));
            }

            if (type.IsSubClassOfGeneric(typeof(IRef <>)))
            {
                var activatableType = typeof(Ref <>).MakeGenericType(type.GenericTypeArguments);
                if (content.Type == JTokenType.Guid)
                {
                    var guidValue = content.Value <Guid>();
                    var iref      = Activator.CreateInstance(activatableType, guidValue);
                    return(onParsed(iref));
                }
                if (content.Type == JTokenType.String)
                {
                    return(StandardStringBindingsAttribute.BindDirect(type,
                                                                      content.Value <string>(),
                                                                      onParsed,
                                                                      onDidNotBind,
                                                                      onBindingFailure));
                }
                if (content.Type == JTokenType.Object)
                {
                    var objectContent = (content as JObject);
                    if (objectContent.TryGetValue("uuid", StringComparison.OrdinalIgnoreCase, out JToken idContentUuid))
                    {
                        return(BindDirect(type, idContentUuid, onParsed, onDidNotBind, onBindingFailure));
                    }
                    if (objectContent.TryGetValue("id", StringComparison.OrdinalIgnoreCase, out JToken idContentId))
                    {
                        return(BindDirect(type, idContentId, onParsed, onDidNotBind, onBindingFailure));
                    }
                    var guidStr = objectContent.ToString();
                    return(StandardStringBindingsAttribute.BindDirect(type,
                                                                      guidStr,
                                                                      onParsed,
                                                                      onDidNotBind,
                                                                      onBindingFailure));
                }
                if (content.Type == JTokenType.Null)
                {
                    return(onBindingFailure("Value was null."));
                }
            }

            if (type.IsSubClassOfGeneric(typeof(IRefOptional <>)))
            {
                if (content.Type == JTokenType.Guid)
                {
                    var guidValue       = content.Value <Guid>();
                    var activatableType = typeof(IRefOptional <>).MakeGenericType(type.GenericTypeArguments);
                    var iref            = Activator.CreateInstance(activatableType, guidValue);
                    return(onParsed(iref));
                }
                if (content.Type == JTokenType.Object)
                {
                    var objectContent = (content as JObject);
                    if (objectContent.TryGetValue("uuid", StringComparison.OrdinalIgnoreCase, out JToken idContent))
                    {
                        return(BindDirect(type, idContent, onParsed, onDidNotBind, onBindingFailure));
                    }
                    var guidStr = objectContent.ToString();
                    return(StandardStringBindingsAttribute.BindDirect(type,
                                                                      guidStr,
                                                                      onParsed,
                                                                      onDidNotBind,
                                                                      onBindingFailure));
                }
                if (content.Type == JTokenType.Null)
                {
                    var irefOptional = RefOptionalHelper.CreateEmpty(type.GenericTypeArguments.First());
                    return(onParsed(irefOptional));
                }
                // Standard string binding fallback at end of function will work for this case
                // if (content.Type == JTokenType.String)
            }

            if (type.IsSubClassOfGeneric(typeof(IRefs <>)))
            {
                var activatableType = typeof(Refs <>).MakeGenericType(type.GenericTypeArguments);
                if (content.Type == JTokenType.Guid)
                {
                    var guidValue = content.Value <Guid>();
                    var guids     = guidValue.AsArray();
                    var irefs     = Activator.CreateInstance(activatableType, guids);
                    return(onParsed(irefs));
                }
                if (content.Type == JTokenType.String)
                {
                    return(StandardStringBindingsAttribute.BindDirect(type,
                                                                      content.Value <string>(),
                                                                      onParsed,
                                                                      onDidNotBind,
                                                                      onBindingFailure));
                }
                if (content.Type == JTokenType.Array)
                {
                    var guids = ReadArray(content)
                                .Select(
                        token => BindDirect(typeof(Guid), token,
                                            guid => (Guid?)((Guid)guid),
                                            (why) => default(Guid?),
                                            (why) => default(Guid?)))
                                .SelectWhereHasValue()
                                .ToArray();

                    var irefs = Activator.CreateInstance(activatableType, guids);
                    return(onParsed(irefs));
                }
            }

            if (type == typeof(int))
            {
                if (content.Type == JTokenType.Float)
                {
                    var floatValue = content.Value <double>();
                    var intValue   = (int)floatValue;
                    return(onParsed(intValue));
                }
                if (content.Type == JTokenType.Integer)
                {
                    var intValue = content.Value <int>();
                    return(onParsed(intValue));
                }
                if (content.Type == JTokenType.String)
                {
                    var stringValue = content.Value <string>();
                    return(StandardStringBindingsAttribute.BindDirect(type, stringValue,
                                                                      onParsed,
                                                                      onDidNotBind,
                                                                      onBindingFailure));
                }
                return(onDidNotBind($"Cannot convert `{content.Type}` to  {typeof(int).FullName}"));
            }

            if (type == typeof(double))
            {
                if (content.Type == JTokenType.Float)
                {
                    var floatValue = content.Value <double>();
                    return(onParsed(floatValue));
                }
                if (content.Type == JTokenType.Integer)
                {
                    var intValue   = content.Value <int>();
                    var floatValue = (double)intValue;
                    return(onParsed(floatValue));
                }
                if (content.Type == JTokenType.String)
                {
                    var stringValue = content.Value <string>();
                    return(StandardStringBindingsAttribute.BindDirect(type, stringValue,
                                                                      onParsed,
                                                                      onDidNotBind,
                                                                      onBindingFailure));
                }
                return(onDidNotBind($"Cannot convert `{content.Type}` to  {typeof(double).FullName}"));
            }

            if (type == typeof(decimal))
            {
                if (content.Type == JTokenType.Float)
                {
                    var floatValue    = content.Value <double>();
                    var decimalValule = (decimal)floatValue;
                    return(onParsed(decimalValule));
                }
                if (content.Type == JTokenType.Integer)
                {
                    var intValue   = content.Value <int>();
                    var floatValue = (decimal)intValue;
                    return(onParsed(floatValue));
                }
                if (content.Type == JTokenType.String)
                {
                    var stringValue = content.Value <string>();
                    return(StandardStringBindingsAttribute.BindDirect(type, stringValue,
                                                                      onParsed,
                                                                      onDidNotBind,
                                                                      onBindingFailure));
                }
                return(onDidNotBind($"Cannot convert `{content.Type}` to  {typeof(decimal).FullName}"));
            }

            if (type == typeof(DateTime))
            {
                if (content.Type == JTokenType.Date)
                {
                    var dateValue = content.Value <DateTime>();
                    return(onParsed(dateValue));
                }
                if (content.Type == JTokenType.Integer)
                {
                    var intValue  = content.Value <long>();
                    var dateValue = new DateTime(intValue);
                    return(onParsed(dateValue));
                }
                if (content.Type == JTokenType.String)
                {
                    var stringValue = content.Value <string>();
                    return(StandardStringBindingsAttribute.BindDirect(type, stringValue,
                                                                      onParsed,
                                                                      onDidNotBind,
                                                                      onBindingFailure));
                }
                return(onDidNotBind($"Cannot convert `{content.Type}` to  {typeof(DateTime).FullName}"));
            }

            if (type == typeof(bool))
            {
                if (content.Type == JTokenType.Boolean)
                {
                    var boolValue = content.Value <bool>();
                    return(onParsed(boolValue));
                }
                if (content.Type == JTokenType.Integer)
                {
                    var intValue  = content.Value <int>();
                    var boolValue = intValue != 0;
                    return(onParsed(boolValue));
                }
                if (content.Type == JTokenType.String)
                {
                    var stringValue = content.Value <string>();
                    return(StandardStringBindingsAttribute.BindDirect(type, stringValue,
                                                                      onParsed,
                                                                      onDidNotBind,
                                                                      onBindingFailure));
                }
                return(onDidNotBind($"Cannot convert `{content.Type}` to  {typeof(bool).FullName}"));
            }

            if (type == typeof(Func <Task <byte[]> >))
            {
                if (content.Type == JTokenType.Bytes)
                {
                    var bytes = content.Value <byte[]>();
                    Func <Task <byte[]> > callback = () => bytes.AsTask();
                    return(onParsed(callback));
                }
                if (content.Type == JTokenType.String)
                {
                    var stringValue = content.Value <string>();
                    if (stringValue.TryParseBase64String(out byte[] bytes))
                    {
                        Func <Task <byte[]> > callback = () => bytes.AsTask();
                        return(onParsed(callback));
                    }
                    if (Uri.TryCreate(stringValue, UriKind.Absolute, out Uri url))
                    {
                        Func <Task <byte[]> > callback = async() =>
                        {
                            using (var client = new HttpClient())
                            {
                                using (var response = await client.GetAsync(url))
                                {
                                    var bytes = await response.Content.ReadAsByteArrayAsync();

                                    return(bytes);
                                }
                            }
                        };
                        return(onParsed(callback));
                    }
                    return(GetHttpContentFromBase64(stringValue,
                                                    context =>
                    {
                        Func <Task <byte[]> > callback = context.ReadAsByteArrayAsync;
                        return onParsed(callback);
                    },
                                                    (prefix) => onBindingFailure($"Not a valid base64 string or a valid URL.")));
                }
            }

            if (type == typeof(HttpContent) || type.IsSubclassOf(typeof(HttpContent)))
            {
                if (content.Type == JTokenType.String)
                {
                    var contentEncodedBase64String = content.Value <string>();
                    return(contentEncodedBase64String.MatchRegexInvoke(
                               "data:(?<contentType>[^;]+);base64,(?<base64Data>.+)",
                               (contentType, base64Data) => base64Data.PairWithValue(contentType),
                               types => types.First(
                                   (ct, next) =>
                    {
                        var base64EncodedData = ct.Key;
                        var data = base64EncodedData.FromBase64String();
                        var contentType = ct.Value;
                        var httpContent = new ByteArrayContent(data);
                        httpContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);
                        return onParsed(httpContent);
                    },
                                   () => onBindingFailure(
                                       $"Could not decode JSON Binary prefix:{contentEncodedBase64String.Substring(0, 25)}"))));
                }
                return(onDidNotBind($"Cannot convert `{content.Type}` to  {typeof(HttpContent).FullName}"));
            }

            if (type == typeof(Func <Task <HttpContent> >))
            {
                if (content.Type == JTokenType.String)
                {
                    var stringValue = content.Value <string>();
                    return(GetHttpContentFromBase64(stringValue,
                                                    (content) =>
                    {
                        Func <Task <HttpContent> > func = () => content.AsTask();
                        return onParsed(content);
                    },
                                                    (prefix) =>
                    {
                        if (Uri.TryCreate(stringValue, UriKind.Absolute, out Uri url))
                        {
                            Func <Task <HttpContent> > callback = async() =>
                            {
                                using (var client = new HttpClient())
                                {
                                    using (var response = await client.GetAsync(url))
                                    {
                                        // Resource disposal creates issues so a copy is needed
                                        var data = await response.Content.ReadAsByteArrayAsync();
                                        var httpContent = new ByteArrayContent(data);
                                        foreach (var header in response.Content.Headers)
                                        {
                                            httpContent.Headers.Add(header.Key, header.Value);
                                        }
                                        return httpContent;
                                    }
                                }
                            };
                            return onParsed(callback);
                        }
                        return onBindingFailure($"Not a valid base64 string or a valid URL.");
                    }));
                }
                return(onDidNotBind($"Cannot convert `{content.Type}` to  {typeof(Func<Task<HttpContent>>).FullName}"));
            }

            TResult GetHttpContentFromBase64(string contentEncodedBase64String,
                                             Func <HttpContent, TResult> onSuccess,
                                             Func <string, TResult> onFailure)
            {
                return(contentEncodedBase64String.MatchRegexInvoke(
                           "data:(?<contentType>[^;]+);base64,(?<base64Data>.+)",
                           (contentType, base64Data) => base64Data.PairWithValue(contentType),
                           types => types.First(
                               (ct, next) =>
                {
                    var base64EncodedData = ct.Key;
                    var data = base64EncodedData.FromBase64String();
                    var contentType = ct.Value;
                    var httpContent = new ByteArrayContent(data);
                    httpContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);
                    return onSuccess(httpContent);
                },
                               () => onFailure(contentEncodedBase64String.Substring(0, 25)))));
            }

            if (type.IsNullable())
            {
                var nullableT = type.GetNullableUnderlyingType();
                return(BindDirect(nullableT, content,
                                  (v) =>
                {
                    var nullableV = v.AsNullable();
                    return onParsed(nullableV);
                },
                                  (why) => onParsed(type.GetDefault()),
                                  (why) => onParsed(type.GetDefault())));
            }

            if (type.IsAssignableFrom(typeof(IDictionary <,>)))
            {
                var keyType     = type.GenericTypeArguments[0];
                var valueType   = type.GenericTypeArguments[1];
                var refType     = typeof(Dictionary <,>).MakeGenericType(type.GenericTypeArguments);
                var refInstance = Activator.CreateInstance(refType);
                var addMethod   = refType.GetMethod("Add");
                //Dictionary<string, int> dict;
                //dict.Add()
                foreach (var kvpToken in ReadDictionary(content))
                {
                    var    keyToken   = kvpToken.Key;
                    var    valueToken = kvpToken.Value;
                    string result     = StandardStringBindingsAttribute.BindDirect(keyType, keyToken,
                                                                                   keyValue =>
                    {
                        return(BindDirect(valueType, valueToken,
                                          valueValue =>
                        {
                            addMethod.Invoke(refInstance,
                                             new object[] { keyValue, valueValue });
                            return string.Empty;
                        },
                                          (why) => why,
                                          (why) => why));
                    },
                                                                                   (why) => why,
                                                                                   (why) => why);
                }

                return(onParsed(refInstance));
            }

            if (type == typeof(object))
            {
                var objectValue = ReadObject(content);
                return(onParsed(objectValue));
            }

            if (content is JObject)
            {
                var jObj     = content as JObject;
                var jsonText = jObj.ToString();
                var value    = JsonConvert.DeserializeObject(jsonText, type);
                return(onParsed(value));
            }

            if (content.Type == JTokenType.String)
            {
                return(StandardStringBindingsAttribute.BindDirect(type,
                                                                  content.Value <string>(),
                                                                  onParsed,
                                                                  onDidNotBind,
                                                                  onBindingFailure));
            }

            //if (content.Type == JTokenType.Object || content.Type == JTokenType.Array)
            //{
            //    try
            //    {
            //        var value = Newtonsoft.Json.JsonConvert.DeserializeObject(
            //            content.ToString(), type, bindConvert);
            //        return onParsed(value);
            //    }
            //    catch (Newtonsoft.Json.JsonSerializationException)
            //    {
            //        throw;
            //    }
            //}

            if (content.Type == JTokenType.Null)
            {
                // PropertyAttribute will not recognize null values as specified w/o this.
                if (type.IsAssignableFrom(typeof(string)))
                {
                    return(onParsed((string)null));
                }

                //var defaultValue = type.GetDefault();
                //return onParsed(defaultValue);
            }

            return(onDidNotBind($"Could not find binding for type {type.FullName}"));
        }
예제 #6
0
        public static TResult BindDirect <TResult>(Type type, string content,
                                                   Func <object, TResult> onParsed,
                                                   Func <string, TResult> onDidNotBind,
                                                   Func <string, TResult> onBindingFailure)
        {
            if (type == typeof(string))
            {
                var stringValue = content;
                return(onParsed((object)stringValue));
            }
            if (type == typeof(Guid))
            {
                if (Guid.TryParse(content, out Guid stringGuidValue))
                {
                    return(onParsed(stringGuidValue));
                }
                return(onBindingFailure($"Failed to convert `{content}` to type `{typeof(Guid).FullName}`."));
            }
            if (type == typeof(Guid[]))
            {
                if (content.IsNullOrWhiteSpace())
                {
                    return(onParsed(new Guid[] { }));
                }
                if (content.StartsWith('['))
                {
                    content = content
                              .TrimStart('[')
                              .TrimEnd(']');
                }
                var tokens = content.Split(','.AsArray());
                var guids  = tokens
                             .Select(
                    token => BindDirect(typeof(Guid), token,
                                        guid => guid,
                                        (why) => default(Guid?),
                                        (why) => default(Guid?)))
                             .Cast <Guid?>()
                             .Where(v => v.HasValue)
                             .Select(v => v.Value)
                             .ToArray();
                return(onParsed(guids));
            }
            if (type == typeof(DateTime))
            {
                return(ParseDate(content,
                                 (currentDateString) => onDidNotBind(
                                     $"Failed to convert {content} to `{typeof(DateTime).FullName}`.")));

                TResult ParseDate(string dateString, Func <string, TResult> onParseFailed)
                {
                    if (dateString.IsNullOrWhiteSpace())
                    {
                        return(onParseFailed(dateString));
                    }

                    if (DateTime.TryParse(dateString, out DateTime dateValue))
                    {
                        return(onParsed(dateValue));
                    }

                    // Common format not supported by TryParse
                    if (DateTime.TryParseExact(dateString, "ddd MMM d yyyy HH:mm:ss 'GMT'K",
                                               null, System.Globalization.DateTimeStyles.AllowWhiteSpaces, out dateValue))
                    {
                        return(onParsed(dateValue));
                    }

                    var startOfDescText = dateString.IndexOf('(');

                    if (startOfDescText > 0)
                    {
                        var cleanerText = content.Substring(0, startOfDescText);
                        return(ParseDate(cleanerText,
                                         failedText =>
                        {
                            var decodedContent = System.Net.WebUtility.UrlDecode(failedText);
                            if (decodedContent != failedText)
                            {
                                return ParseDate(decodedContent,
                                                 (failedDecodedText) => onParseFailed(failedDecodedText));
                            }
                            return onParseFailed(failedText);
                        }));
                    }

                    var decodedContent = System.Net.WebUtility.UrlDecode(dateString);

                    if (decodedContent != dateString)
                    {
                        return(ParseDate(decodedContent,
                                         (failedDecodedText) => onParseFailed(failedDecodedText)));
                    }
                    return(onParseFailed(dateString));
                }
            }
            if (type == typeof(DateTimeOffset))
            {
                if (DateTimeOffset.TryParse(content, out DateTimeOffset dateValue))
                {
                    return(onParsed(dateValue));
                }
                return(onDidNotBind($"Failed to convert {content} to `{typeof(DateTimeOffset).FullName}`."));
            }
            if (type == typeof(int))
            {
                if (int.TryParse(content, out int intValue))
                {
                    return(onParsed(intValue));
                }
                return(onBindingFailure($"Failed to convert {content} to `{typeof(int).FullName}`."));
            }
            if (type == typeof(double))
            {
                if (double.TryParse(content, out double doubleValue))
                {
                    return(onParsed(doubleValue));
                }
                return(onBindingFailure($"Failed to convert {content} to `{typeof(double).FullName}`."));
            }
            if (type == typeof(decimal))
            {
                if (decimal.TryParse(content, out decimal decimalValue))
                {
                    return(onParsed(decimalValue));
                }
                return(onBindingFailure($"Failed to convert {content} to `{typeof(decimal).FullName}`."));
            }
            if (type == typeof(bool))
            {
                if (content.IsDefaultNullOrEmpty())
                {
                    return(onDidNotBind("Value not provided."));
                }

                if ("t" == content.ToLower())
                {
                    return(onParsed(true));
                }

                if ("on" == content.ToLower()) // used in check boxes
                {
                    return(onParsed(true));
                }

                if ("f" == content)
                {
                    return(onParsed(false));
                }

                if ("off" == content.ToLower()) // used in some check boxes
                {
                    return(onParsed(false));
                }

                // TryParse may convert "on" to false TODO: Test theory
                if (bool.TryParse(content, out bool boolValue))
                {
                    return(onParsed(boolValue));
                }

                return(onDidNotBind($"Failed to convert {content} to `{typeof(bool).FullName}`."));
            }
            if (type == typeof(Uri))
            {
                if (content.IsDefaultNullOrEmpty())
                {
                    return(onBindingFailure("URL value was empty"));
                }
                if (Uri.TryCreate(content.Trim('"'.AsArray()), UriKind.RelativeOrAbsolute, out Uri uriValue))
                {
                    return(onParsed(uriValue));
                }
                return(onBindingFailure($"Failed to convert {content} to `{typeof(Uri).FullName}`."));
            }
            if (type == typeof(Type))
            {
                return(content.GetClrType(
                           typeInstance => onParsed(typeInstance),
                           () => onDidNotBind(
                               $"`{content}` is not a recognizable resource type or CLR type.")));
                //() => HttpApplication.GetResourceType(content,
                //        (typeInstance) => onParsed(typeInstance),
                //        () => content.GetClrType(
                //            typeInstance => onParsed(typeInstance),
                //            () => onDidNotBind(
                //                $"`{content}` is not a recognizable resource type or CLR type."))));
            }
            if (type == typeof(Stream))
            {
                return(BindDirect(typeof(byte[]), content,
                                  byteArrayValueObj =>
                {
                    var byteArrayValue = (byte[])byteArrayValueObj;
                    return onParsed(new MemoryStream(byteArrayValue));
                },
                                  onDidNotBind,
                                  onBindingFailure));
            }
            if (type == typeof(byte[]))
            {
                if (content.TryParseBase64String(out byte[] byteArrayValue))
                {
                    return(onParsed(byteArrayValue));
                }
                return(onDidNotBind($"Failed to convert {content} to `{typeof(byte[]).FullName}` as base64 string."));
            }
            if (type == typeof(WebId))
            {
                if (!Guid.TryParse(content, out Guid guidValue))
                {
                    return(onBindingFailure($"Could not convert `{content}` to GUID"));
                }
                var webIdObj = (object)new WebId()
                {
                    UUID = guidValue
                };
                return(onParsed(webIdObj));
            }
            if (type == typeof(Controllers.DateTimeEmpty))
            {
                if (String.Compare(content.ToLower(), "false") == 0)
                {
                    return(onParsed(new Controllers.DateTimeEmpty()));
                }
                return(onBindingFailure($"Failed to convert {content} to `{typeof(Controllers.DateTimeEmpty).FullName}`."));
            }
            if (type == typeof(Controllers.DateTimeQuery))
            {
                if (DateTime.TryParse(content, out DateTime startEnd))
                {
                    return(onParsed(new Controllers.DateTimeQuery(startEnd, startEnd)));
                }
                return(onBindingFailure($"Failed to convert {content} to `{typeof(Controllers.DateTimeQuery).FullName}`."));
            }
            if (type == typeof(object))
            {
                var objValue = content;
                return(onParsed(objValue));
            }

            if (type.IsSubClassOfGeneric(typeof(IRef <>)))
            {
                return(BindDirect(typeof(Guid), content,
                                  (id) =>
                {
                    var resourceType = type.GenericTypeArguments.First();
                    var instantiatableType = typeof(EastFive.Ref <>).MakeGenericType(resourceType);
                    var instance = Activator.CreateInstance(instantiatableType, new object[] { id });
                    return onParsed(instance);
                },
                                  onDidNotBind,
                                  (why) => onBindingFailure(why)));
            }

            if (type.IsSubClassOfGeneric(typeof(IRefOptional <>)))
            {
                var referredType = type.GenericTypeArguments.First();

                TResult emptyOptional()
                {
                    var refInst = RefOptionalHelper.CreateEmpty(referredType);

                    return(onParsed(refInst));
                };

                if (content.IsNullOrWhiteSpace())
                {
                    return(emptyOptional());
                }
                if (content.ToLower() == "empty")
                {
                    return(emptyOptional());
                }
                if (content.ToLower() == "null")
                {
                    return(emptyOptional());
                }

                var refType = typeof(IRef <>).MakeGenericType(referredType);
                return(BindDirect(refType, content,
                                  (v) =>
                {
                    var refOptionalType = typeof(RefOptional <>).MakeGenericType(referredType);
                    var refInst = Activator.CreateInstance(refOptionalType, new object[] { v });
                    return onParsed(refInst);
                },
                                  (why) => emptyOptional(),
                                  (why) => emptyOptional()));
            }
            if (type.IsSubClassOfGeneric(typeof(IRefs <>)))
            {
                return(BindDirect(typeof(Guid[]), content,
                                  (ids) =>
                {
                    var resourceType = type.GenericTypeArguments.First();
                    var instantiatableType = typeof(Refs <>).MakeGenericType(resourceType);
                    var instance = Activator.CreateInstance(instantiatableType, new object[] { ids });
                    return onParsed(instance);
                },
                                  onDidNotBind,
                                  (why) => onBindingFailure(why)));
            }
            if (type.IsSubClassOfGeneric(typeof(Nullable <>)))
            {
                var underlyingType = type.GetNullableUnderlyingType();
                return(BindDirect(underlyingType, content,
                                  (nonNullable) =>
                {
                    var nullable = nonNullable.AsNullable();
                    return onParsed(nullable);
                },
                                  (why) => onParsed(type.GetDefault()),
                                  (why) => onParsed(type.GetDefault())));
            }

            if (type.IsEnum)
            {
                if (Enum.TryParse(type, content, out object value))
                {
                    return(onParsed(value));
                }

                var validValues = Enum.GetNames(type).Join(", ");
                return(onDidNotBind($"Value `{content}` is not a valid value for `{type.FullName}.` Valid values are [{validValues}]."));
            }

            if (type.IsArray)
            {
                return(content.MatchRegexInvoke(
                           @"(\[(?<index>[0-9]+)\]=)?(?<value>([^\;]|(?<=\\)\;)+)",
                           (index, value) => index.PairWithValue(value),
                           onMatched: tpls =>
                {
                    // either abc;def
                    // or [0]=abc;[1]=def
                    var matchesDictionary = tpls.Any(kvp => string.IsNullOrEmpty(kvp.Key))
                            ? tpls
                                            .Select(
                        (kvp, index) => kvp.Value.PairWithKey(index))
                                            .ToDictionary()
                            : tpls
                                            .TryWhere(
                        (KeyValuePair <string, string> kvp, out int indexedValue) =>
                        int.TryParse(kvp.Key, out indexedValue))
                                            .Select(
                        match => match.item.Value.PairWithKey(match.@out))
                                            .ToDictionary();


                    // matchesDictionary.Keys will throw if empty
                    var ordered = matchesDictionary.IsDefaultNullOrEmpty()?
                                  new (bool, string)[] { }