Skip to main content

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 std::cell::RefCell;
7use std::collections::HashSet;
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: HashSet<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: HashSet::new(),
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: HashSet::new(),
114            matches: Vec::new(),
115        }
116    }
117}