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); }
/// <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); } }
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); }
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); } }
/// <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); } }
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); }
/// <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); }
/// <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);
/// <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)); }
public override object Handle(string input, CQ context, Type outputType, BaseHtmlAttribute attribute) { return(input); }