minidom/
tree_builder.rs

1// Copyright (c) 2022 Astro <astro@spaceboyz.net>
2
3//! SAX events to DOM tree conversion
4
5use crate::prefixes::{Prefix, Prefixes};
6use crate::{Element, Error};
7use rxml::RawEvent;
8use std::collections::BTreeMap;
9
10/// Tree-building parser state
11pub struct TreeBuilder {
12    next_tag: Option<(Prefix, String, Prefixes, BTreeMap<String, String>)>,
13    /// Parsing stack
14    stack: Vec<Element>,
15    /// Namespace set stack by prefix
16    prefixes_stack: Vec<Prefixes>,
17    /// Document root element if finished
18    pub root: Option<Element>,
19}
20
21impl Default for TreeBuilder {
22    fn default() -> Self {
23        Self::new()
24    }
25}
26
27impl TreeBuilder {
28    /// Create a new one
29    #[must_use]
30    pub fn new() -> Self {
31        TreeBuilder {
32            next_tag: None,
33            stack: vec![],
34            prefixes_stack: vec![],
35            root: None,
36        }
37    }
38
39    /// Allow setting prefixes stack.
40    ///
41    /// Useful to provide knowledge of namespaces that would have been declared on parent elements
42    /// not present in the reader.
43    #[must_use]
44    pub fn with_prefixes_stack(mut self, prefixes_stack: Vec<Prefixes>) -> Self {
45        self.prefixes_stack = prefixes_stack;
46        self
47    }
48
49    /// Stack depth
50    #[must_use]
51    pub fn depth(&self) -> usize {
52        self.stack.len()
53    }
54
55    /// Get the top-most element from the stack but don't remove it
56    #[must_use]
57    pub fn top(&mut self) -> Option<&Element> {
58        self.stack.last()
59    }
60
61    /// Pop the top-most element from the stack
62    fn pop(&mut self) -> Option<Element> {
63        self.prefixes_stack.pop();
64        self.stack.pop()
65    }
66
67    /// Unshift the first child of the top element
68    pub fn unshift_child(&mut self) -> Option<Element> {
69        let depth = self.stack.len();
70        if depth > 0 {
71            self.stack[depth - 1].unshift_child()
72        } else {
73            None
74        }
75    }
76
77    /// Lookup XML namespace declaration for given prefix (or no prefix)
78    #[must_use]
79    fn lookup_prefix(&self, prefix: &Option<String>) -> Option<&str> {
80        for nss in self.prefixes_stack.iter().rev() {
81            if let Some(ns) = nss.get(prefix) {
82                return Some(ns);
83            }
84        }
85
86        None
87    }
88
89    fn process_end_tag(&mut self) {
90        if let Some(el) = self.pop() {
91            if self.depth() > 0 {
92                let top = self.stack.len() - 1;
93                self.stack[top].append_child(el);
94            } else {
95                self.root = Some(el);
96            }
97        }
98    }
99
100    fn process_text(&mut self, text: String) {
101        if self.depth() > 0 {
102            let top = self.stack.len() - 1;
103            self.stack[top].append_text(text);
104        }
105    }
106
107    /// Process an event that you got out of a `RawParser`.
108    pub fn process_event(&mut self, event: RawEvent) -> Result<(), Error> {
109        match event {
110            RawEvent::XmlDeclaration(_, _) => {}
111
112            RawEvent::ElementHeadOpen(_, (prefix, name)) => {
113                self.next_tag = Some((
114                    prefix.map(|prefix| prefix.as_str().to_owned()),
115                    name.as_str().to_owned(),
116                    Prefixes::default(),
117                    BTreeMap::new(),
118                ));
119            }
120
121            RawEvent::Attribute(_, (prefix, name), value) => {
122                if let Some((_, _, ref mut prefixes, ref mut attrs)) = self.next_tag.as_mut() {
123                    match (prefix, name) {
124                        (None, xmlns) if xmlns == "xmlns" => prefixes.insert(None, value),
125                        (Some(xmlns), prefix) if xmlns.as_str() == "xmlns" => {
126                            prefixes.insert(Some(prefix.as_str().to_owned()), value);
127                        }
128                        (Some(prefix), name) => {
129                            attrs.insert(format!("{prefix}:{name}"), value.as_str().to_owned());
130                        }
131                        (None, name) => {
132                            attrs.insert(name.as_str().to_owned(), value.as_str().to_owned());
133                        }
134                    }
135                }
136            }
137
138            RawEvent::ElementHeadClose(_) => {
139                if let Some((prefix, name, prefixes, attrs)) = self.next_tag.take() {
140                    self.prefixes_stack.push(prefixes.clone());
141
142                    let namespace = self
143                        .lookup_prefix(&prefix.map(|prefix| prefix.as_str().to_owned()))
144                        .ok_or(Error::MissingNamespace)?
145                        .to_owned();
146                    let el =
147                        Element::new(name.as_str().to_owned(), namespace, prefixes, attrs, vec![]);
148                    self.stack.push(el);
149                }
150            }
151
152            RawEvent::ElementFoot(_) => self.process_end_tag(),
153
154            RawEvent::Text(_, text) => self.process_text(text.as_str().to_owned()),
155        }
156
157        Ok(())
158    }
159}