xml_3dm/node/matched_nodes.rs
1//! Container for matched nodes.
2//!
3//! A `MatchedNodes` container holds references to branch nodes that are
4//! matched to a specific base node.
5
6use rustc_hash::FxHashSet;
7use std::cell::RefCell;
8use std::rc::{Rc, Weak};
9
10use super::{NodeInner, NodeRef};
11use crate::node::MatchType;
12
13/// A weak reference to a node.
14pub type WeakNodeRef = Weak<RefCell<NodeInner>>;
15
16/// Container for a set of branch nodes matched to a base node.
17///
18/// In the Java implementation, this uses a `HashSet` with identity-based
19/// equality. We achieve the same by using node IDs for hashing.
20#[derive(Debug, Clone)]
21pub struct MatchedNodes {
22 /// Weak reference to the owner base node.
23 owner: WeakNodeRef,
24 /// Set of matched branch node IDs.
25 match_ids: FxHashSet<u64>,
26 /// Strong references to matched branch nodes, keyed by node ID.
27 /// We keep these to prevent nodes from being dropped.
28 matches: Vec<NodeRef>,
29}
30
31impl MatchedNodes {
32 /// Creates a new container for matched nodes.
33 ///
34 /// # Arguments
35 /// * `owner` - Weak reference to the base node that owns this container.
36 pub fn new(owner: WeakNodeRef) -> Self {
37 MatchedNodes {
38 owner,
39 match_ids: FxHashSet::default(),
40 matches: Vec::new(),
41 }
42 }
43
44 /// Returns a weak reference to the owner base node.
45 pub fn owner(&self) -> &WeakNodeRef {
46 &self.owner
47 }
48
49 /// Adds a branch node to the match set.
50 ///
51 /// # Arguments
52 /// * `node` - The branch node to add.
53 pub fn add_match(&mut self, node: NodeRef) {
54 let id = node.borrow().id();
55 if self.match_ids.insert(id) {
56 self.matches.push(node);
57 }
58 }
59
60 /// Removes a branch node from the match set.
61 ///
62 /// # Arguments
63 /// * `node` - The branch node to remove.
64 pub fn del_match(&mut self, node: &NodeRef) {
65 let id = node.borrow().id();
66 if self.match_ids.remove(&id) {
67 self.matches.retain(|n| n.borrow().id() != id);
68 }
69 }
70
71 /// Clears all matches.
72 pub fn clear_matches(&mut self) {
73 self.match_ids.clear();
74 self.matches.clear();
75 }
76
77 /// Returns the matched nodes.
78 pub fn matches(&self) -> &[NodeRef] {
79 &self.matches
80 }
81
82 /// Returns the number of matched nodes.
83 pub fn match_count(&self) -> usize {
84 self.matches.len()
85 }
86
87 /// Checks if a node is in the match set.
88 pub fn contains(&self, node: &NodeRef) -> bool {
89 let id = node.borrow().id();
90 self.match_ids.contains(&id)
91 }
92
93 /// Gets the first node that is fully matched to the owner.
94 ///
95 /// A full match means both content and children match (MATCH_FULL).
96 pub fn full_match(&self) -> Option<NodeRef> {
97 for node in &self.matches {
98 let borrowed = node.borrow();
99 if let Some(match_type) = borrowed.match_type() {
100 if match_type.contains(MatchType::FULL) {
101 return Some(Rc::clone(node));
102 }
103 }
104 }
105 None
106 }
107}
108
109impl Default for MatchedNodes {
110 fn default() -> Self {
111 MatchedNodes {
112 owner: Weak::new(),
113 match_ids: FxHashSet::default(),
114 matches: Vec::new(),
115 }
116 }
117}