minidom/
tree_builder.rs

1// Copyright (c) 2022 Astro <astro@spaceboyz.net>
2// Copyright (c) 2025 pep <pep@bouah.net>
3//
4// This Source Code Form is subject to the terms of the Mozilla Public
5// License, v. 2.0. If a copy of the MPL was not distributed with this
6// file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
8//! SAX events to DOM tree conversion
9
10use crate::prefixes::{Prefix, Prefixes};
11use crate::{Element, Error};
12use alloc::string::String;
13use alloc::vec::Vec;
14use rxml::{AttrMap, Namespace, RawEvent};
15
16/// Tree-building parser state
17pub struct TreeBuilder {
18    next_tag: Option<(Prefix, String, Prefixes, AttrMap)>,
19    /// Parsing stack
20    stack: Vec<Element>,
21    /// Namespace set stack by prefix
22    prefixes_stack: Vec<Prefixes>,
23    attrs_stack: Vec<(String, String, String)>,
24    /// Document root element if finished
25    pub root: Option<Element>,
26}
27
28impl Default for TreeBuilder {
29    fn default() -> Self {
30        Self::new()
31    }
32}
33
34impl TreeBuilder {
35    /// Create a new one
36    #[must_use]
37    pub fn new() -> Self {
38        TreeBuilder {
39            next_tag: None,
40            stack: Vec::new(),
41            prefixes_stack: Vec::new(),
42            attrs_stack: Vec::new(),
43            root: None,
44        }
45    }
46
47    /// Allow setting prefixes stack.
48    ///
49    /// Useful to provide knowledge of namespaces that would have been declared on parent elements
50    /// not present in the reader.
51    #[must_use]
52    pub fn with_prefixes_stack(mut self, prefixes_stack: Vec<Prefixes>) -> Self {
53        self.prefixes_stack = prefixes_stack;
54        self
55    }
56
57    /// Stack depth
58    #[must_use]
59    pub fn depth(&self) -> usize {
60        self.stack.len()
61    }
62
63    /// Get the top-most element from the stack but don't remove it
64    #[must_use]
65    pub fn top(&mut self) -> Option<&Element> {
66        self.stack.last()
67    }
68
69    /// Pop the top-most element from the stack
70    fn pop(&mut self) -> Option<Element> {
71        self.prefixes_stack.pop();
72        self.stack.pop()
73    }
74
75    /// Unshift the first child of the top element
76    pub fn unshift_child(&mut self) -> Option<Element> {
77        let depth = self.stack.len();
78        if depth > 0 {
79            self.stack[depth - 1].unshift_child()
80        } else {
81            None
82        }
83    }
84
85    /// Lookup XML namespace declaration for given prefix (or no prefix)
86    #[must_use]
87    fn lookup_prefix<'a>(
88        prefixes_stack: &'a [Prefixes],
89        prefix: &'a Option<String>,
90    ) -> Option<&'a str> {
91        for nss in prefixes_stack.iter().rev() {
92            if let Some(ns) = nss.get(prefix) {
93                return Some(ns);
94            }
95        }
96
97        None
98    }
99
100    fn process_end_tag(&mut self) {
101        if let Some(el) = self.pop() {
102            if self.depth() > 0 {
103                let top = self.stack.len() - 1;
104                self.stack[top].append_child(el);
105            } else {
106                self.root = Some(el);
107            }
108        }
109    }
110
111    fn process_text(&mut self, text: String) {
112        if self.depth() > 0 {
113            let top = self.stack.len() - 1;
114            self.stack[top].append_text(text);
115        }
116    }
117
118    /// Process an event that you got out of a `RawParser`.
119    pub fn process_event(&mut self, event: RawEvent) -> Result<(), Error> {
120        match event {
121            RawEvent::XmlDeclaration(_, _) => {}
122
123            RawEvent::ElementHeadOpen(_, (prefix, name)) => {
124                // If self.prefixes_stack has been set via with_prefixes_stack before processing,
125                // ensure these are set on the root element.
126                let prefixes = if self.stack.is_empty() && self.prefixes_stack.len() == 1 {
127                    self.prefixes_stack.pop().unwrap()
128                } else {
129                    Prefixes::default()
130                };
131
132                self.next_tag = Some((
133                    prefix.map(|prefix| prefix.as_str().to_owned()),
134                    name.as_str().to_owned(),
135                    prefixes,
136                    AttrMap::new(),
137                ));
138            }
139
140            RawEvent::Attribute(_, (prefix, name), value) => {
141                if let Some((_, _, ref mut prefixes, ref mut attrs)) = self.next_tag.as_mut() {
142                    match (prefix, name) {
143                        (None, xmlns) if xmlns == "xmlns" => prefixes.insert(None, value),
144                        (Some(xmlns), prefix) if xmlns.as_str() == "xmlns" => {
145                            prefixes.insert(Some(prefix.as_str().to_owned()), value);
146                        }
147                        (Some(prefix), name) => {
148                            self.attrs_stack
149                                .push((prefix.to_string(), name.to_string(), value));
150                        }
151                        (None, name) => {
152                            attrs.insert(Namespace::NONE, name, value.as_str().to_owned());
153                        }
154                    }
155                }
156            }
157
158            RawEvent::ElementHeadClose(_) => {
159                if let Some((prefix, name, prefixes, mut attrs)) = self.next_tag.take() {
160                    self.prefixes_stack.push(prefixes.clone());
161
162                    let namespace = TreeBuilder::lookup_prefix(
163                        &self.prefixes_stack,
164                        &prefix.map(|prefix| prefix.as_str().to_owned()),
165                    )
166                    .ok_or(Error::MissingNamespace)?
167                    .to_owned();
168
169                    for (prefix, attr, value) in self.attrs_stack.drain(..) {
170                        let ns = if prefix == "xml" {
171                            rxml::Namespace::xml()
172                        } else {
173                            &TreeBuilder::lookup_prefix(
174                                &self.prefixes_stack,
175                                &Some(prefix.to_string()),
176                            )
177                            .map(String::from)
178                            .map(Into::<Namespace>::into)
179                            .ok_or(Error::MissingNamespace)?
180                            .to_owned()
181                        };
182                        attrs.insert(ns.clone(), attr.try_into().unwrap(), value.to_string());
183                    }
184
185                    let el = Element::new(name.to_owned(), namespace, prefixes, attrs, Vec::new());
186                    self.stack.push(el);
187                }
188            }
189
190            RawEvent::ElementFoot(_) => self.process_end_tag(),
191
192            RawEvent::Text(_, text) => self.process_text(text.as_str().to_owned()),
193        }
194
195        Ok(())
196    }
197}