public static Type FindBestMatchingType(this Type elementType, IXPathNavigable node)
{
if (elementType == null)
{
throw new ArgumentNullException("elementType");
}
if (node == null)
{
throw new ArgumentNullException("node");
}
var navigator = node.GetNavigator();
var possibleTypes = GetMatchingTypes(elementType).ToList();
var matchingTypes = new List<Type>();
// The node name should already be folded to lower case when the HTML was read
var nodeName = navigator.Name;
foreach (var possibleType in possibleTypes)
{
var attributes =
possibleType.GetCustomAttributes(typeof(SupportedTagAttribute), true)
.OfType<SupportedTagAttribute>();
foreach (var attribute in attributes)
{
if (nodeName.Equals(attribute.TagName, StringComparison.OrdinalIgnoreCase) == false)
{
continue;
}
if (attribute.HasAttributeFilter)
{
// The attribute name should already be folded to lower case when the HTML was read
var queryAttributeName = attribute.AttributeName.ToLowerInvariant();
var matchingAttribute = navigator.GetAttribute(queryAttributeName, string.Empty);
if (matchingAttribute.Equals(attribute.AttributeValue, StringComparison.OrdinalIgnoreCase))
{
matchingTypes.Add(possibleType);
}
}
else
{
matchingTypes.Add(possibleType);
}
}
}
if (matchingTypes.Count == 0)
{
return typeof(AnyHtmlElement);
}
if (matchingTypes.Count > 1)
{
var matchingTypeNames = matchingTypes.Aggregate(string.Empty, (x, y) => x + Environment.NewLine + y);
var message = string.Format(
CultureInfo.CurrentCulture,
Resources.TypeExtensions_MultipleTypeMatchesForNode,
elementType.FullName,
navigator.OuterXml,
matchingTypeNames);
throw new InvalidHtmlElementMatchException(message);
}
return matchingTypes[0];
}