initial commit
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
namespace Solicitors.HtmlParsing.Models;
|
||||
|
||||
internal class HtmlAttribute(string name) : IHtmlAttribute
|
||||
{
|
||||
public string Name { get; } = name;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Solicitors.HtmlParsing.Models;
|
||||
|
||||
internal class HtmlNode(string tagName, IHtmlAttribute[] attributes)
|
||||
: IHtmlNode
|
||||
{
|
||||
private readonly IHtmlAttribute[] _attributes = attributes;
|
||||
private readonly string _tagName = tagName;
|
||||
|
||||
public virtual bool TryGetByTagName(
|
||||
string tagName,
|
||||
[NotNullWhen(true)] out IHtmlNode? node,
|
||||
bool noChildren = false)
|
||||
{
|
||||
node = null;
|
||||
if (tagName == _tagName)
|
||||
node = this;
|
||||
|
||||
return node is not null;
|
||||
}
|
||||
|
||||
private string[] Classes
|
||||
{
|
||||
get
|
||||
{
|
||||
var classAttribute = _attributes
|
||||
.Where(a => a is HtmlValueAttribute)
|
||||
.Select(a => (a as HtmlValueAttribute)!)
|
||||
.FirstOrDefault(a => a.Name.Equals("class", StringComparison.CurrentCultureIgnoreCase));
|
||||
|
||||
return classAttribute is not null
|
||||
? classAttribute.Value.Substring(1, classAttribute.Value.Length - 2).Split(' ')
|
||||
: [];
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool TryGetByClass(
|
||||
string className,
|
||||
[NotNullWhen(true)] out IHtmlNode? node,
|
||||
bool noChildren = false)
|
||||
{
|
||||
node = null;
|
||||
|
||||
if (Classes.Contains(className, StringComparer.InvariantCultureIgnoreCase))
|
||||
node = this;
|
||||
|
||||
return node is not null;
|
||||
}
|
||||
|
||||
public virtual bool TryGetByTagNameAndClass(
|
||||
string tagName,
|
||||
string className,
|
||||
[NotNullWhen(true)] out IHtmlNode? node,
|
||||
bool noChildren = false)
|
||||
{
|
||||
node = null;
|
||||
if (tagName == _tagName && Classes.Contains(className, StringComparer.InvariantCultureIgnoreCase))
|
||||
node = this;
|
||||
|
||||
return node is not null;
|
||||
}
|
||||
|
||||
public virtual bool TryGetText([NotNullWhen(true)] out string? text)
|
||||
{
|
||||
text = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool TryGetChildren([NotNullWhen(true)] out IEnumerable<IHtmlNode>? children)
|
||||
{
|
||||
children = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool HasAttribute(string attributeName)
|
||||
{
|
||||
return _attributes
|
||||
.Any(a => a.Name.Equals(attributeName, StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
|
||||
public bool TryGetAttributeValue(string attributeName, [NotNullWhen(true)] out string? attributeValue)
|
||||
{
|
||||
attributeValue = null;
|
||||
|
||||
var matchingAttribute = _attributes.FirstOrDefault(
|
||||
a => a.Name.Equals(attributeName, StringComparison.InvariantCultureIgnoreCase)
|
||||
);
|
||||
|
||||
if (matchingAttribute is HtmlValueAttribute valueAttribute)
|
||||
attributeValue = valueAttribute.Value;
|
||||
|
||||
return attributeValue is not null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Solicitors.HtmlParsing.Models;
|
||||
|
||||
internal class HtmlValueAttribute(string name, string value) : HtmlAttribute(name)
|
||||
{
|
||||
public string Value { get; } = value;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Solicitors.HtmlParsing.Models;
|
||||
|
||||
internal interface IHtmlAttribute
|
||||
{
|
||||
string Name { get; }
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Solicitors.HtmlParsing.Models;
|
||||
|
||||
public interface IHtmlNode
|
||||
{
|
||||
/// <summary>
|
||||
/// Searches this node and its direct children for a matching tag name.
|
||||
/// </summary>
|
||||
/// <param name="tagName">The tag name to match.</param>
|
||||
/// <param name="node">If found, the node that matches the tag name.</param>
|
||||
/// <param name="noChildren">If true, only check this node, not its children.</param>
|
||||
/// <returns>True if this node, or any of its direct children, have a matching tag name; false otherwise.</returns>
|
||||
bool TryGetByTagName(
|
||||
string tagName,
|
||||
[NotNullWhen(true)] out IHtmlNode? node,
|
||||
bool noChildren = false);
|
||||
|
||||
/// <summary>
|
||||
/// Searches this node and its direct children for a matching class.
|
||||
/// </summary>
|
||||
/// <param name="className">The class name to match.</param>
|
||||
/// <param name="node">If found, the node that matches the class name.</param>
|
||||
/// <param name="noChildren">If true, only check this node, not its children.</param>
|
||||
/// <returns>True if this node, or any of its direct children, have a matching class name; false otherwise.</returns>
|
||||
bool TryGetByClass(
|
||||
string className,
|
||||
[NotNullWhen(true)] out IHtmlNode? node,
|
||||
bool noChildren = false);
|
||||
|
||||
/// <summary>
|
||||
/// Searches this node and its direct children for a matching tag name and matching class on the same node.
|
||||
/// </summary>
|
||||
/// <param name="tagName">The tag name to match.</param>
|
||||
/// <param name="className">The class name to match.</param>
|
||||
/// <param name="node">If found, the node that matches the tag name and class name.</param>
|
||||
/// <param name="noChildren">If true, only check this node, not its children.</param>
|
||||
/// <returns>True if this node, or any of its direct children, have a matching tag name and class name; false otherwise.</returns>
|
||||
bool TryGetByTagNameAndClass(
|
||||
string tagName,
|
||||
string className,
|
||||
[NotNullWhen(true)] out IHtmlNode? node,
|
||||
bool noChildren = false);
|
||||
|
||||
/// <summary>
|
||||
/// Checks this node (and *not* its children) to see if it is text content.
|
||||
/// </summary>
|
||||
/// <param name="text">If found, the text content.</param>
|
||||
/// <returns>True if the node is a text node, false otherwise.</returns>
|
||||
bool TryGetText([NotNullWhen(true)] out string? text);
|
||||
|
||||
/// <summary>
|
||||
/// Checks this node to see if it has child elements.
|
||||
/// </summary>
|
||||
/// <param name="children">If found, the child elements.</param>
|
||||
/// <returns>True if the node has child elements, false otherwise.</returns>
|
||||
bool TryGetChildren([NotNullWhen(true)] out IEnumerable<IHtmlNode>? children);
|
||||
|
||||
bool HasAttribute(string attributeName);
|
||||
|
||||
bool TryGetAttributeValue(
|
||||
string attributeName,
|
||||
[NotNullWhen(true)] out string? attributeValue);
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Solicitors.HtmlParsing.Models;
|
||||
|
||||
internal class ParentHtmlNode(string tagName, IHtmlAttribute[] attributes, IHtmlNode[] children)
|
||||
: HtmlNode(tagName, attributes)
|
||||
{
|
||||
private readonly IHtmlNode[] _children = children;
|
||||
|
||||
public override bool TryGetByTagName(
|
||||
string tagName,
|
||||
[NotNullWhen(true)] out IHtmlNode? node,
|
||||
bool noChildren = false)
|
||||
{
|
||||
node = null;
|
||||
if (!base.TryGetByTagName(tagName, out node, noChildren) && !noChildren)
|
||||
{
|
||||
var nodesToCheck = new Queue<IHtmlNode>(_children);
|
||||
while (nodesToCheck.TryDequeue(out var child))
|
||||
{
|
||||
if (child.TryGetByTagName(tagName, out var childNode, true))
|
||||
{
|
||||
node = childNode;
|
||||
break;
|
||||
}
|
||||
|
||||
if (child.TryGetChildren(out var grandChildren))
|
||||
foreach (var grandChild in grandChildren)
|
||||
nodesToCheck.Enqueue(grandChild);
|
||||
}
|
||||
}
|
||||
|
||||
return node is not null;
|
||||
}
|
||||
|
||||
public override bool TryGetByClass(
|
||||
string className,
|
||||
[NotNullWhen(true)] out IHtmlNode? node,
|
||||
bool noChildren = false)
|
||||
{
|
||||
node = null;
|
||||
if (!base.TryGetByClass(className, out node, noChildren) && !noChildren)
|
||||
{
|
||||
var nodesToCheck = new Queue<IHtmlNode>(_children);
|
||||
while (nodesToCheck.TryDequeue(out var child))
|
||||
{
|
||||
if (child.TryGetByClass(className, out var childNode, true))
|
||||
{
|
||||
node = childNode;
|
||||
break;
|
||||
}
|
||||
|
||||
if (child.TryGetChildren(out var grandChildren))
|
||||
foreach (var grandChild in grandChildren)
|
||||
nodesToCheck.Enqueue(grandChild);
|
||||
}
|
||||
}
|
||||
|
||||
return node is not null;
|
||||
}
|
||||
|
||||
public override bool TryGetByTagNameAndClass(
|
||||
string tagName,
|
||||
string className,
|
||||
[NotNullWhen(true)] out IHtmlNode? node,
|
||||
bool noChildren = false)
|
||||
{
|
||||
node = null;
|
||||
if (!base.TryGetByTagNameAndClass(tagName, className, out node, noChildren) && !noChildren)
|
||||
{
|
||||
var nodesToCheck = new Queue<IHtmlNode>(_children);
|
||||
while (nodesToCheck.TryDequeue(out var child))
|
||||
{
|
||||
if (child.TryGetByTagNameAndClass(tagName, className, out var childNode, true))
|
||||
{
|
||||
node = childNode;
|
||||
break;
|
||||
}
|
||||
|
||||
if (child.TryGetChildren(out var grandChildren))
|
||||
foreach (var grandChild in grandChildren)
|
||||
nodesToCheck.Enqueue(grandChild);
|
||||
}
|
||||
}
|
||||
|
||||
return node is not null;
|
||||
}
|
||||
|
||||
public override bool TryGetChildren([NotNullWhen(true)] out IEnumerable<IHtmlNode>? children)
|
||||
{
|
||||
children = _children;
|
||||
return _children.Length != 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Solicitors.HtmlParsing.Models;
|
||||
|
||||
internal class StringNode(string content)
|
||||
: IHtmlNode
|
||||
{
|
||||
private readonly string _content = content;
|
||||
|
||||
public bool TryGetByTagName(
|
||||
string tagName,
|
||||
[NotNullWhen(true)] out IHtmlNode? node,
|
||||
bool noChildren = false)
|
||||
{
|
||||
node = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetByClass(
|
||||
string className,
|
||||
[NotNullWhen(true)] out IHtmlNode? node,
|
||||
bool noChildren = false)
|
||||
{
|
||||
node = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetByTagNameAndClass(
|
||||
string tagName,
|
||||
string className,
|
||||
[NotNullWhen(true)] out IHtmlNode? node,
|
||||
bool noChildren = false)
|
||||
{
|
||||
node = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetText([NotNullWhen(true)] out string? text)
|
||||
{
|
||||
text = _content;
|
||||
return !string.IsNullOrEmpty(text);
|
||||
}
|
||||
|
||||
public bool TryGetChildren([NotNullWhen(true)] out IEnumerable<IHtmlNode>? children)
|
||||
{
|
||||
children = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool HasAttribute(string attributeName) => false;
|
||||
|
||||
public bool TryGetAttributeValue(
|
||||
string attributeName,
|
||||
[NotNullWhen(true)] out string? attributeValue)
|
||||
{
|
||||
attributeValue = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user