Exemple #1
0
        private static object ParseAndBind(BaseHtmlAttribute attribute, Type objectType, CQ nodeForProperty)
        {
            // Parse
            var parsedValue = ParserFactory.Instance.GetParser(attribute.GetType()).Parse(nodeForProperty, attribute);

            // Bind
            var boundValue = BinderFactory.Instance.GetBinder(objectType).Handle(parsedValue, nodeForProperty, objectType, attribute);

            return(boundValue);
        }
Exemple #2
0
 /// <summary>
 /// Binds the specified input string to an output object of a certain type.
 /// The value <paramref name="context" /> must be provided for performance reasons, as to avoid parsing the HTML tree multiple times.
 /// </summary>
 /// <param name="input">The input string.  See <see cref="BindingBehavior" /> for examples on what types of information may be passed in.</param>
 /// <param name="context">The context of the currently being-bound input value.  Generally the HTML element corresponding to the input value.</param>
 /// <param name="outputType">The type of output object to generate, whether a POCO, primitive, or other.</param>
 /// <param name="attribute">The optional attribute decorating the property that is currently being handled.</param>
 /// <returns>
 /// The output type object created, and filled with the parsed version of the <paramref name="input" />.
 /// </returns>
 public object Handle(string input, CQ context, Type outputType, BaseHtmlAttribute attribute)
 {
     try
     {
         return(Convert.ChangeType(input, outputType));
     }
     catch (Exception e)
     {
         throw new NapBindingException($"An error occurred binding {input} to type {outputType.FullName}.  See inner exception for details.", e);
     }
 }
Exemple #3
0
        public override object Handle(string input, CQ context, Type outputType, BaseHtmlAttribute attribute)
        {
            DateTime dateTime;
            var      success = DateTime.TryParse(input, out dateTime);

            if (outputType.IsGenericType && outputType.GetGenericTypeDefinition() == typeof(Nullable <>))
            {
                return(success ? (DateTime?)dateTime : null);
            }

            return(dateTime);
        }
Exemple #4
0
        public override object Handle(string input, CQ context, Type outputType, BaseHtmlAttribute attribute)
        {
            try
            {
                var bindingFormat = (attribute as NumericalHtmlElementAttribute)?.BindingFormat ?? DefaultBindingBehavior;

                if (outputType.IsGenericType && outputType.GetGenericTypeDefinition() == typeof(Nullable <>))
                {
                    decimal d;
                    return(decimal.TryParse(bindingFormat.ParseToNumber(input) ?? input, bindingFormat.NumberStyle, bindingFormat.FormatInfo, out d) ? (decimal?)d : null);
                }

                return(decimal.Parse(bindingFormat.ParseToNumber(input) ?? input, bindingFormat.NumberStyle, bindingFormat.FormatInfo));
            }
            catch (Exception e)
            {
                throw new NapBindingException($"An error occurred binding {input} to type {outputType.FullName}.  See inner exception for details.", e);
            }
        }
Exemple #5
0
        /// <summary>
        /// Parses some string value out of an <see cref="HtmlNode" /> selected by an <see cref="IFinder{T}" />
        /// </summary>
        /// <param name="node">The node that we are preparing to bind on.</param>
        /// <param name="attributeInstance">The instance of the attribute that is being used to perform binding.</param>
        /// <returns>The string value of the element to pass to the binder for binding to the POCO.</returns>
        /// <exception cref="ArgumentNullException">Thrown if either <paramref name="node"/> or <paramref name="attributeInstance"/> is null.</exception>
        public string Parse(CQ node, BaseHtmlAttribute attributeInstance)
        {
            if (node == null)
            {
                throw new ArgumentNullException(nameof(node));
            }
            if (attributeInstance == null)
            {
                throw new ArgumentNullException(nameof(attributeInstance));
            }

            try
            {
                return(Parse(node, attributeInstance as HtmlElementAttribute));
            }
            catch (ArgumentNullException ex)
            {
                throw new NapParsingException($"Attribute {attributeInstance.GetType().FullName} is not of the correct type to be handled by ${GetType().FullName}.", ex);
            }
        }
Exemple #6
0
        public object Handle(IEnumerable <CQ> context, Type outputType, BaseHtmlAttribute propertyAttribute)
        {
            // TODO: Cache reflection methods

            // Enumerable case
            var enumerableType = outputType.IsArray ? outputType.GetElementType() : outputType.GetGenericArguments().First();
            var castTypeMethod =
                typeof(Enumerable).GetMethod("Cast", BindingFlags.Static | BindingFlags.Public)
                .MakeGenericMethod(enumerableType);
            object value = context.Select(node => ParseAndBind(propertyAttribute, enumerableType, node));

            value = castTypeMethod.Invoke(null, new[] { value });
            if (ImplementsGenericType(outputType, typeof(List <>))) // List
            {
                var toListMethod =
                    typeof(Enumerable).GetMethod("ToList", BindingFlags.Static | BindingFlags.Public)
                    .MakeGenericMethod(enumerableType);
                value = toListMethod.Invoke(null, new[] { value });
            }
#if IMMUTABLE
            else if (ImplementsGenericType(outputType, typeof(FSharpList <>))) // F# list
            {
                var toFsharplistMethod =
                    typeof(ListModule).GetMethod("OfSeq", BindingFlags.Static | BindingFlags.Public)
                    .MakeGenericMethod(enumerableType);
                value = toFsharplistMethod.Invoke(null, new[] { value });
            }
#endif
            else // other enumerables
            {
                var toArrayMethod =
                    typeof(Enumerable).GetMethod("ToArray", BindingFlags.Static | BindingFlags.Public)
                    .MakeGenericMethod(enumerableType);
                value = toArrayMethod.Invoke(null, new[] { value });
            }
            return(value);
        }
Exemple #7
0
        /// <summary>
        /// Binds the specified input string to an output object of a certain type.
        /// The value <paramref name="context" /> must be provided for performance reasons, as to avoid parsing the HTML tree multiple times.
        /// </summary>
        /// <param name="input">The input string.  See <see cref="BindingBehavior" /> for examples on what types of information may be passed in.</param>
        /// <param name="context">The context of the currently being-bound input value.  Generally the HTML element corresponding to the input value.</param>
        /// <param name="outputType">The type of output object to generate, whether a POCO, primitive, or other.</param>
        /// <param name="attribute">The optional attribute decorating the property that is currently being handled.</param>
        /// <returns>
        /// The output type object created, and filled with the parsed version of the <paramref name="input" />.
        /// </returns>
        public object Handle(string input, CQ context, Type outputType, BaseHtmlAttribute attribute)
        {
            if (context == null && input == null)
            {
                throw new ArgumentNullException(nameof(input), new ArgumentNullException(nameof(context)));
            }
            if (outputType == null)
            {
                throw new ArgumentNullException(nameof(outputType));
            }

            if (context == null)
            {
                context = new CQ(input);
            }

            var contract      = new ObjectContract(outputType);
            var allProperties = outputType.GetProperties();

            var properties = allProperties.Where(p => p.CanWrite)
                             .Union(
                allProperties.Where(p =>
                                    contract.Parameters.Any(
                                        param => param.Name.Equals(p.Name, StringComparison.InvariantCultureIgnoreCase))));

            // Changing this from setting to creating a hash table of values
            var values = properties.ToDictionary(property => property.Name, property =>
            {
                var propertyAttribute =
                    property.GetCustomAttributes(typeof(BaseHtmlAttribute), true).FirstOrDefault() as BaseHtmlAttribute;
                if (propertyAttribute != null)
                {
                    var enumerableInterface =
                        property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable <>) ?
                        property.PropertyType :
                        property.PropertyType.GetInterfaces()
                        .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable <>));
                    var isEnumerable = enumerableInterface != null && property.PropertyType != typeof(string);

                    // Find
                    var typeOfFinder    = isEnumerable ? typeof(IEnumerable <CQ>) : typeof(CQ);
                    var nodeForProperty = FinderFactory.Instance.GetFinder(typeOfFinder)
                                          .Find(context, propertyAttribute.Selector);

                    // Verify
                    if (nodeForProperty != null)
                    {
                        object value;
                        var nodes      = nodeForProperty as IEnumerable <CQ>;
                        var singleNode = nodeForProperty as CQ;

                        if (nodes != null && isEnumerable)
                        {
                            value = BinderFactory.Instance.GetEnumerableBinder().Handle(nodes, property.PropertyType, propertyAttribute);
                        }
                        else if (singleNode != null)
                        {
                            // Single case
                            value = ParseAndBind(propertyAttribute, property.PropertyType, singleNode);
                        }
                        else
                        {
                            throw new NapBindingException($"Unknown finder type found: {nodeForProperty.GetType().FullName}");
                        }

                        return(value);
                    }
                }

                // Return default type otherwise
                return(property.PropertyType.IsValueType ? Activator.CreateInstance(property.PropertyType) : null);
            });

            // Construct using a greedy constructor mechanis
            object toReturn;

            if (TryCreateNewObject(outputType, values, out toReturn))
            {
                foreach (var value in values)
                {
                    // TODO: Add contract.Properties to cache PropertyInfos
                    var pi =
                        outputType.GetProperties(BindingFlags.Instance | BindingFlags.Public)
                        .FirstOrDefault(p => p.Name.Equals(value.Key, StringComparison.InvariantCultureIgnoreCase));
                    if (pi?.CanWrite ?? false)
                    {
                        pi.SetValue(toReturn, value.Value);
                    }
                }
            }

            return(toReturn);
        }
Exemple #8
0
 /// <summary>
 /// Binds the specified input string to an output object of a certain type.
 /// The value <paramref name="context" /> must be provided for performance reasons, as to avoid parsing the HTML tree multiple times.
 /// </summary>
 /// <param name="input">The input string.  See <see cref="BindingBehavior" /> for examples on what types of information may be passed in.</param>
 /// <param name="context">The context of the currently being-bound input value.  Generally the HTML element corresponding to the input value.</param>
 /// <param name="outputType">The type of output object to generate, whether a POCO, primitive, or other.</param>
 /// <param name="attribute">The optional attribute decorating the property that is currently being handled.</param>
 /// <returns>
 /// The output type object created, and filled with the parsed version of the <paramref name="input" />.
 /// </returns>
 public abstract object Handle(string input, CQ context, Type outputType, BaseHtmlAttribute attribute);
Exemple #9
0
 /// <summary>
 /// Binds the specified input string to an output object of a certain type.
 /// The value <paramref name="context" /> must be provided for performance reasons, as to avoid parsing the HTML tree multiple times.
 /// </summary>
 /// <param name="input">The input string.  See <see cref="BindingBehavior" /> for examples on what types of information may be passed in.</param>
 /// <param name="context">The context of the currently being-bound input value.  Generally the HTML element corresponding to the input value.</param>
 /// <param name="attribute"></param>
 /// <returns>
 /// The output type object created, and filled with the parsed version of the <paramref name="input" />.
 /// </returns>
 public virtual object Handle(string input, CQ context, BaseHtmlAttribute attribute)
 {
     return(Handle(input, context, typeof(T), attribute));
 }
Exemple #10
0
 public override object Handle(string input, CQ context, Type outputType, BaseHtmlAttribute attribute)
 {
     return(input);
 }