use crate::abi::structs::_xmlNode;
use crate::xml::xpath::ast::{Axis, NameTest, NodeTest};
use crate::xml::xpath::types::NodeSet;
pub unsafe fn traverse_axis(
context_node: *mut _xmlNode,
axis: Axis,
node_test: &NodeTest,
include_attributes: bool,
include_namespaces: bool,
) -> NodeSet {
let mut result = NodeSet::new();
match axis {
Axis::Child => child_axis(context_node, node_test, &mut result),
Axis::Descendant => descendant_axis(context_node, node_test, &mut result),
Axis::Parent => parent_axis(context_node, node_test, &mut result),
Axis::Ancestor => ancestor_axis(context_node, node_test, &mut result, false),
Axis::AncestorOrSelf => ancestor_axis(context_node, node_test, &mut result, true),
Axis::FollowingSibling => following_sibling_axis(context_node, node_test, &mut result),
Axis::PrecedingSibling => preceding_sibling_axis(context_node, node_test, &mut result),
Axis::Following => following_axis(context_node, node_test, &mut result),
Axis::Preceding => preceding_axis(context_node, node_test, &mut result),
Axis::Attribute => {
if include_attributes {
attribute_axis(context_node, node_test, &mut result);
}
}
Axis::Namespace => {
if include_namespaces {
namespace_axis(context_node, node_test, &mut result);
}
}
Axis::Self_ => self_axis(context_node, node_test, &mut result),
Axis::DescendantOrSelf => {
self_axis(context_node, node_test, &mut result);
descendant_axis(context_node, node_test, &mut result);
}
}
result
}
unsafe fn child_axis(node: *mut _xmlNode, node_test: &NodeTest, result: &mut NodeSet) {
if node.is_null() {
return;
}
let mut child = (*node).children;
while !child.is_null() {
if matches_node_test(child, node_test) {
result.push(child);
}
child = (*child).next;
}
}
unsafe fn descendant_axis(node: *mut _xmlNode, node_test: &NodeTest, result: &mut NodeSet) {
if node.is_null() {
return;
}
let mut child = (*node).children;
while !child.is_null() {
if matches_node_test(child, node_test) {
result.push(child);
}
descendant_axis(child, node_test, result);
child = (*child).next;
}
}
unsafe fn parent_axis(node: *mut _xmlNode, node_test: &NodeTest, result: &mut NodeSet) {
if node.is_null() {
return;
}
let parent = (*node).parent;
if !parent.is_null() && matches_node_test(parent, node_test) {
result.push(parent);
}
}
unsafe fn ancestor_axis(
node: *mut _xmlNode,
node_test: &NodeTest,
result: &mut NodeSet,
include_self: bool,
) {
if node.is_null() {
return;
}
if include_self && matches_node_test(node, node_test) {
result.push(node);
}
let mut n = (*node).parent;
while !n.is_null() {
if matches_node_test(n, node_test) {
result.push(n);
}
n = (*n).parent;
}
}
unsafe fn following_sibling_axis(node: *mut _xmlNode, node_test: &NodeTest, result: &mut NodeSet) {
if node.is_null() {
return;
}
let mut n = (*node).next;
while !n.is_null() {
if matches_node_test(n, node_test) {
result.push(n);
}
n = (*n).next;
}
}
unsafe fn preceding_sibling_axis(node: *mut _xmlNode, node_test: &NodeTest, result: &mut NodeSet) {
if node.is_null() {
return;
}
let mut n = (*node).prev;
while !n.is_null() {
if matches_node_test(n, node_test) {
result.push(n);
}
n = (*n).prev;
}
}
unsafe fn following_axis(node: *mut _xmlNode, node_test: &NodeTest, result: &mut NodeSet) {
if node.is_null() {
return;
}
let mut n = node;
loop {
let next_sibling = (*n).next;
if !next_sibling.is_null() {
traverse_subtree(next_sibling, node_test, result);
let mut s = next_sibling;
while !(*s).next.is_null() {
s = (*s).next;
}
n = s;
continue;
}
n = (*n).parent;
if n.is_null() || matches_node_test_for_any(n) {
break;
}
}
}
unsafe fn preceding_axis(node: *mut _xmlNode, node_test: &NodeTest, result: &mut NodeSet) {
if node.is_null() {
return;
}
let mut n = node;
loop {
let prev_sibling = (*n).prev;
if !prev_sibling.is_null() {
traverse_subtree_reverse(prev_sibling, node_test, result);
n = prev_sibling;
continue;
}
n = (*n).parent;
if n.is_null() {
break;
}
break;
}
}
unsafe fn attribute_axis(node: *mut _xmlNode, node_test: &NodeTest, result: &mut NodeSet) {
if node.is_null() {
return;
}
let mut prop = (*node).properties;
while !prop.is_null() {
let attr_node = prop as *mut _xmlNode;
if matches_node_test(attr_node, node_test) {
result.push(attr_node);
}
prop = (*prop).next;
}
}
unsafe fn namespace_axis(node: *mut _xmlNode, node_test: &NodeTest, result: &mut NodeSet) {
if node.is_null() {
return;
}
let mut ns_def = (*node).nsDef;
while !ns_def.is_null() {
ns_def = (*ns_def).next;
}
let mut n = (*node).parent;
while !n.is_null() {
let mut ns_def = (*n).nsDef;
while !ns_def.is_null() {
ns_def = (*ns_def).next;
}
n = (*n).parent;
}
}
unsafe fn self_axis(node: *mut _xmlNode, node_test: &NodeTest, result: &mut NodeSet) {
if !node.is_null() && matches_node_test(node, node_test) {
result.push(node);
}
}
unsafe fn traverse_subtree(node: *mut _xmlNode, node_test: &NodeTest, result: &mut NodeSet) {
if node.is_null() {
return;
}
if matches_node_test(node, node_test) {
result.push(node);
}
let mut child = (*node).children;
while !child.is_null() {
traverse_subtree(child, node_test, result);
child = (*child).next;
}
}
unsafe fn traverse_subtree_reverse(
node: *mut _xmlNode,
node_test: &NodeTest,
result: &mut NodeSet,
) {
if node.is_null() {
return;
}
let mut child = (*node).children;
if !child.is_null() {
let mut last = child;
while !(*last).next.is_null() {
last = (*last).next;
}
let mut n = last;
loop {
traverse_subtree_reverse(n, node_test, result);
if n == child {
break;
}
n = (*n).prev;
}
}
if matches_node_test(node, node_test) {
result.push(node);
}
}
pub unsafe fn matches_node_test(node: *mut _xmlNode, node_test: &NodeTest) -> bool {
if node.is_null() {
return false;
}
let node_ref = &*node;
let node_type = node_ref.type_;
match node_test {
NodeTest::Node => true,
NodeTest::Text => node_type == 3 || node_type == 4, NodeTest::Comment => node_type == 8,
NodeTest::ProcessingInstruction(target) => {
if node_type == 7 {
if let Some(target) = target {
let name = crate::xml::string::xmlstr_to_string(node_ref.name);
name == *target
} else {
true
}
} else {
false
}
}
NodeTest::NameTest(name_test) => matches_name_test(node, name_test),
NodeTest::Wildcard => {
node_type == 1
}
NodeTest::NsWildcard(prefix) => {
if node_type == 1 {
if let Some(ns) = node_ref.ns.as_ref() {
let ns_prefix = crate::xml::string::xmlstr_to_string(ns.prefix);
ns_prefix == *prefix
} else {
prefix.is_empty()
}
} else {
false
}
}
}
}
unsafe fn matches_name_test(node: *mut _xmlNode, name_test: &NameTest) -> bool {
if node.is_null() {
return false;
}
let node_ref = &*node;
match name_test {
NameTest::Any => {
node_ref.type_ == 1 || node_ref.type_ == 2 || node_ref.type_ == 13
}
NameTest::LocalName(local) => {
let name = crate::xml::string::xmlstr_to_string(node_ref.name);
name == *local
}
NameTest::QName { prefix, local } => {
let name = crate::xml::string::xmlstr_to_string(node_ref.name);
if name != *local {
return false;
}
if let Some(ns) = node_ref.ns.as_ref() {
let ns_prefix = crate::xml::string::xmlstr_to_string(ns.prefix);
ns_prefix == *prefix
} else {
prefix.is_empty()
}
}
}
}
unsafe fn matches_node_test_for_any(_node: *mut _xmlNode) -> bool {
true
}
#[cfg(test)]
mod tests {
use super::*;
use crate::xml::xpath::ast::{NameTest, NodeTest};
#[test]
fn test_node_test_matches() {
assert!(matches!(NodeTest::Node, NodeTest::Node));
}
}