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 TreeBuilder {
22 /// Create a new one
23 pub fn new() -> Self {
24 TreeBuilder {
25 next_tag: None,
26 stack: vec![],
27 prefixes_stack: vec![],
28 root: None,
29 }
30 }
31
32 /// Stack depth
33 pub fn depth(&self) -> usize {
34 self.stack.len()
35 }
36
37 /// Get the top-most element from the stack but don't remove it
38 pub fn top(&mut self) -> Option<&Element> {
39 self.stack.last()
40 }
41
42 /// Pop the top-most element from the stack
43 fn pop(&mut self) -> Option<Element> {
44 self.prefixes_stack.pop();
45 self.stack.pop()
46 }
47
48 /// Unshift the first child of the top element
49 pub fn unshift_child(&mut self) -> Option<Element> {
50 let depth = self.stack.len();
51 if depth > 0 {
52 self.stack[depth - 1].unshift_child()
53 } else {
54 None
55 }
56 }
57
58 /// Lookup XML namespace declaration for given prefix (or no prefix)
59 fn lookup_prefix(&self, prefix: &Option<String>) -> Option<&str> {
60 for nss in self.prefixes_stack.iter().rev() {
61 if let Some(ns) = nss.get(prefix) {
62 return Some(ns);
63 }
64 }
65
66 None
67 }
68
69 fn process_end_tag(&mut self) -> Result<(), Error> {
70 if let Some(el) = self.pop() {
71 if self.depth() > 0 {
72 let top = self.stack.len() - 1;
73 self.stack[top].append_child(el);
74 } else {
75 self.root = Some(el);
76 }
77 }
78
79 Ok(())
80 }
81
82 fn process_text(&mut self, text: String) {
83 if self.depth() > 0 {
84 let top = self.stack.len() - 1;
85 self.stack[top].append_text_node(text);
86 }
87 }
88
89 /// Process a Event that you got out of a RawParser
90 pub fn process_event(&mut self, event: RawEvent) -> Result<(), Error> {
91 match event {
92 RawEvent::XmlDeclaration(_, _) => {}
93
94 RawEvent::ElementHeadOpen(_, (prefix, name)) => {
95 self.next_tag = Some((
96 prefix.map(|prefix| prefix.as_str().to_owned()),
97 name.as_str().to_owned(),
98 Prefixes::default(),
99 BTreeMap::new(),
100 ))
101 }
102
103 RawEvent::Attribute(_, (prefix, name), value) => {
104 self.next_tag
105 .as_mut()
106 .map(
107 |(_, _, ref mut prefixes, ref mut attrs)| match (prefix, name) {
108 (None, xmlns) if xmlns == "xmlns" => {
109 prefixes.insert(None, value);
110 }
111 (Some(xmlns), prefix) if xmlns.as_str() == "xmlns" => {
112 prefixes.insert(Some(prefix.as_str().to_owned()), value);
113 }
114 (Some(prefix), name) => {
115 attrs.insert(
116 format!("{}:{}", prefix, name),
117 value.as_str().to_owned(),
118 );
119 }
120 (None, name) => {
121 attrs.insert(name.as_str().to_owned(), value.as_str().to_owned());
122 }
123 },
124 );
125 }
126
127 RawEvent::ElementHeadClose(_) => {
128 if let Some((prefix, name, prefixes, attrs)) = self.next_tag.take() {
129 self.prefixes_stack.push(prefixes.clone());
130
131 let namespace = self
132 .lookup_prefix(&prefix.clone().map(|prefix| prefix.as_str().to_owned()))
133 .ok_or(Error::MissingNamespace)?
134 .to_owned();
135 let el =
136 Element::new(name.as_str().to_owned(), namespace, prefixes, attrs, vec![]);
137 self.stack.push(el);
138 }
139 }
140
141 RawEvent::ElementFoot(_) => self.process_end_tag()?,
142
143 RawEvent::Text(_, text) => self.process_text(text.as_str().to_owned()),
144 }
145
146 Ok(())
147 }
148}