hml_rs/names/
namespace_stack.rs

1/*a Copyright
2
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6
7  http://www.apache.org/licenses/LICENSE-2.0
8
9Unless required by applicable law or agreed to in writing, software
10distributed under the License is distributed on an "AS IS" BASIS,
11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12See the License for the specific language governing permissions and
13limitations under the License.
14
15@file    namespace_stack.rs
16@brief   A markup namespace stack with various frames
17 */
18
19/*a Imports
20*/
21use crate::names::Namespace;
22use crate::names::{NSMap, NSNameId, NSPrefixId, NSUriId};
23use std::collections::{HashMap, HashSet};
24
25//a NamespaceStackFrame, NamespaceStack, and StackIter
26//ti NamespaceStackFrameIter
27pub struct NamespaceStackFrameIter<'frame> {
28    frame: &'frame NamespaceStackFrame,
29    i: usize,
30    n: usize,
31}
32
33//ii NamespaceStackIterator
34impl<'frame> NamespaceStackFrameIter<'frame> {
35    fn new(frame: &'frame NamespaceStackFrame) -> Self {
36        let n = frame.order.len();
37        let i = 0;
38        Self { frame, n, i }
39    }
40}
41
42//ii Iterator for NamespaceStackIterator
43impl<'frame> Iterator for NamespaceStackFrameIter<'frame> {
44    type Item = NSMap;
45    fn next(&mut self) -> Option<Self::Item> {
46        if self.i >= self.n {
47            None
48        } else {
49            let prefix_id = self.frame.order[self.i];
50            self.i += 1;
51            if let Some(uri_id) = self.frame.find_mapping(prefix_id) {
52                let map = NSMap::new(prefix_id, *uri_id);
53                Some(map)
54            } else {
55                self.next()
56            }
57        }
58    }
59}
60
61//ti NamespaceStackFrame
62#[derive(Default)]
63struct NamespaceStackFrame {
64    mappings: HashMap<NSPrefixId, NSUriId>,
65    order: Vec<NSPrefixId>,
66}
67
68//ii NamespaceStackFrame
69impl NamespaceStackFrame {
70    //mp add_mapping_by_id
71    pub fn add_mapping_by_id(&mut self, map: NSMap) {
72        self.mappings.insert(map.prefix_id(), map.uri_id());
73    }
74
75    //mp add_mapping_by_id_if_unset
76    pub fn add_mapping_by_id_if_unset(&mut self, map: NSMap) -> bool {
77        if self.mappings.contains_key(&map.prefix_id()) {
78            false
79        } else {
80            self.add_mapping_by_id(map);
81            true
82        }
83    }
84
85    //mp find_mapping
86    /// Find a mapping of a prefix ID
87    fn find_mapping(&self, prefix_id: NSPrefixId) -> Option<&NSUriId> {
88        self.mappings.get(&prefix_id)
89    }
90
91    //mp iter_mappings
92    /// Iterate over the mappings defined in this stack frame (prefix to URI)
93    fn iter_mappings(&self) -> NamespaceStackFrameIter {
94        NamespaceStackFrameIter::new(self)
95    }
96}
97
98//ti NamespaceStackIterator
99pub struct NamespaceStackIterator<'ns, 'b> {
100    stack: &'b NamespaceStack<'ns>,
101    // frame goes len() .. 1
102    frame: usize,
103    // index goes 0..frame.len()
104    frame_iter: Option<std::collections::hash_map::Iter<'b, NSPrefixId, NSUriId>>,
105    // set of NSPrefixId returned so far
106    used: HashSet<NSPrefixId>,
107}
108
109//ii NamespaceStackIterator
110impl<'ns, 'b> NamespaceStackIterator<'ns, 'b> {
111    fn new(stack: &'b NamespaceStack<'ns>) -> Self {
112        let frame = stack.stack_depth();
113        let frame_iter = None;
114        let used = HashSet::new();
115        Self {
116            stack,
117            frame,
118            frame_iter,
119            used,
120        }
121    }
122}
123
124//ii Iterator for NamespaceStackIterator
125impl<'ns, 'b> Iterator for NamespaceStackIterator<'ns, 'b> {
126    type Item = NSMap;
127    fn next(&mut self) -> Option<Self::Item> {
128        if self.frame == 0 {
129            None
130        } else if let Some(iter) = &mut self.frame_iter {
131            if let Some((p_id, u_id)) = iter.next() {
132                if self.used.contains(p_id) {
133                    self.next()
134                } else {
135                    Some(NSMap::new(*p_id, *u_id))
136                }
137            } else {
138                self.frame -= 1;
139                self.frame_iter = None;
140                self.next()
141            }
142        } else {
143            self.frame_iter = Some(self.stack.borrow_frame(self.frame - 1).mappings.iter());
144            self.next()
145        }
146    }
147}
148
149//tp NamespaceStack
150/// A [NamespaceStack] is a use of a [Namespace] structure within a
151/// document; in general they form a pair, with the [Namespace]
152/// created first and a [NamespaceStack] following.
153///
154/// The stack consists of frames, which can be pushed and
155/// popped. Within a frame there are mappings between prefix strings
156/// and URI strings; there will be at most one mapping for each prefix
157/// within the frame. Different stack frames may map the same prefix
158/// differently, though.
159///
160/// A mapping for a prefix string is determined by finding that
161/// mapping in the topmost stack frame if possible; if there is no
162/// mapping in that frame, the next frame down is examined, and so on.
163///
164/// URI and prefix strings are stored within the [Namespace]
165/// structure, and are referred to by 'id's in most of the API - as
166/// are mappings from prefix to URI.
167///
168/// A client of the [NamespaceStack] should add mappings to the [NamespaceStack] with a [NamespaceStack::add] operation, which returns an [NSMap]; it can look up mappings for a prefix string by resolving the prefix string to an ID with [NamespaceStack::find_prefix], and then finding the map using [NamespaceStack::find_mapping].
169///
170/// A client can iterate through all the mappings using the `iter()` method.
171///
172/// # Example
173///
174/// ```
175/// use hml_rs::names::{NamespaceStack, Namespace};
176///
177/// let mut ns  = Namespace::new(true);
178/// let mut nst = NamespaceStack::new(&mut ns);
179///
180/// ```
181///
182pub struct NamespaceStack<'ns> {
183    namespaces: &'ns mut Namespace,
184    frames: Vec<NamespaceStackFrame>,
185}
186
187//ip NamespaceStack
188impl<'ns> NamespaceStack<'ns> {
189    //fp new
190    /// Create a new [NamespaceStack], mutably borrowing the
191    /// [Namespace] for its lifetime
192    pub fn new(namespaces: &'ns mut Namespace) -> Self {
193        let frames = vec![NamespaceStackFrame::default()];
194        let mut s = Self { namespaces, frames };
195        if s.uses_xmlns() {
196            s.add_default_xmls();
197        } else {
198            s.add_null_ns();
199        }
200        s
201    }
202
203    //mp uses_xmlns
204    /// Return true if the [Namespace] was specified to use the XML
205    /// namespace (at creation)
206    pub fn uses_xmlns(&self) -> bool {
207        self.namespaces.uses_xmlns()
208    }
209
210    //mp add_null_ns
211    /// Add the null namespace
212    ///
213    /// This is normally done at the creation of a [NamespaceStack]
214    pub fn add_null_ns(&mut self) {
215        self.add_ns("", "");
216    }
217
218    //mp add_default_xmls
219    /// Add the default XML namespaces to the stack frame
220    ///
221    /// This is normally done at the creation of a [NamespaceStack]
222    pub fn add_default_xmls(&mut self) {
223        self.add_null_ns();
224        self.add_ns("xmlns", "http://www.w3.org/2000/xmlns/");
225        self.add_ns("xml", "http://www.w3.org/XML/1998/namespace");
226    }
227
228    //mp push_frame
229    /// Push a new stack frame on to the [NamespaceStack]
230    pub fn push_frame(&mut self) {
231        self.frames.push(NamespaceStackFrame::default());
232    }
233
234    //mp pop_frame
235    /// Pop the topmost stack frame from the [NamespaceStack]
236    ///
237    /// Panics if the stack is empty
238    pub fn pop_frame(&mut self) {
239        self.frames.pop().unwrap();
240    }
241
242    //mi stack_depth
243    /// Returns the depth of the stack
244    fn stack_depth(&self) -> usize {
245        self.frames.len()
246    }
247
248    //mi borrow_frame
249    /// Borrow the 'nth' frame of the stack
250    fn borrow_frame(&self, n: usize) -> &NamespaceStackFrame {
251        &self.frames[n]
252    }
253
254    //mp add_mapping_by_id
255    /// Add a mapping of NSPrefixId -> NSUriId to the topmost stack frame
256    pub fn add_mapping_by_id(&mut self, map: NSMap) {
257        self.frames.last_mut().unwrap().add_mapping_by_id(map)
258    }
259
260    //mp add_mapping_by_id_if_unset
261    /// Add a mapping if it does not exist *in the topmost stack fram*
262    pub fn add_mapping_by_id_if_unset(&mut self, map: NSMap) -> bool {
263        self.frames
264            .last_mut()
265            .unwrap()
266            .add_mapping_by_id_if_unset(map)
267    }
268
269    //mp find_mapping
270    /// Find a mapping of an [NSPrefixId] at the highest level of the
271    /// stack that it exists
272    ///
273    /// Returns `None` if there is no mapping on the stack at all
274    pub fn find_mapping(&self, prefix_id: NSPrefixId) -> Option<NSUriId> {
275        let n = self.frames.len();
276        for i in 0..n {
277            if let Some(uri_id) = self.frames[n - 1 - i].find_mapping(prefix_id) {
278                return Some(*uri_id);
279            }
280        }
281        None
282    }
283
284    //mp find_prefix_id
285    /// Find the NSPrefixId corresponding to a string within the
286    /// underlying [Namespace]
287    pub fn find_prefix_id(&mut self, prefix: &str) -> Option<NSPrefixId> {
288        self.namespaces.find_prefix(prefix)
289    }
290
291    //ap iter_top_mappings
292    /// Get a slice of the mappings of the topmost frame
293    pub fn iter_top_mappings(&self) -> NamespaceStackFrameIter<'_> {
294        assert!(self.frames.is_empty(), "Namespace stack cannot be empty");
295        self.frames.last().unwrap().iter_mappings()
296    }
297
298    //mp borrow_mapping
299    /// Borrow the two strings corresponding to an [NSMap] within the [Namespace]
300    pub fn borrow_mapping(&self, map: NSMap) -> (&str, &str) {
301        (self.prefix_str(map.prefix_id()), self.uri_str(map.uri_id()))
302    }
303
304    //mp name_str
305    /// Borrow the name corresponding to an [NSNameId] within the [Namespace]
306    pub fn name_str(&self, name: NSNameId) -> &str {
307        self.namespaces.name_str(name, "")
308    }
309
310    //mp prefix_str
311    /// Borrow the prefix corresponding to an [NSPrefixId] within the [Namespace]
312    pub fn prefix_str(&self, prefix: NSPrefixId) -> &str {
313        self.namespaces.prefix_str(prefix, "")
314    }
315
316    //mp uri_str
317    /// Borrow the URI corresponding to an [NSUriId] within the [Namespace]
318    pub fn uri_str(&self, uri: NSUriId) -> &str {
319        self.namespaces.uri_str(uri, "")
320    }
321
322    //mp add_name
323    /// Add a name for a string to the [Namespace]; if it is already
324    /// in the [Namespace] then it is not added but the current
325    /// NSNameId is used.
326    pub fn add_name(&mut self, name: &str) -> NSNameId {
327        self.namespaces.find_or_add_name(name)
328    }
329
330    //mp add_ns
331    /// Add a prefix -> URI mapping to the [Namespace]
332    pub fn add_ns(&mut self, prefix: &str, uri: &str) -> NSMap {
333        let ns_map = self.namespaces.add_mapping(prefix, uri);
334        self.add_mapping_by_id(ns_map);
335        ns_map
336    }
337
338    //mp add_ns_if_unset
339    /// Add a prefix -> URI mapping to the [Namespace]
340    pub fn add_ns_if_unset(&mut self, prefix: &str, uri: &str) -> (NSMap, bool) {
341        let ns_map = self.namespaces.add_mapping(prefix, uri);
342        (ns_map, self.add_mapping_by_id_if_unset(ns_map))
343    }
344
345    //fp fmt_map
346    /// Format an [NSMap] within the [Namespace]
347    pub fn fmt_map<W: std::fmt::Write>(
348        &self,
349        w: &mut W,
350        map: NSMap,
351    ) -> Result<(), std::fmt::Error> {
352        write!(
353            w,
354            "'{}' => '{}'",
355            self.prefix_str(map.prefix_id()),
356            self.uri_str(map.uri_id())
357        )
358    }
359
360    //zz All done
361}
362
363//ip IntoIterator for NamespaceStack
364impl<'ns, 'b> IntoIterator for &'b NamespaceStack<'ns> {
365    type Item = NSMap;
366    type IntoIter = NamespaceStackIterator<'ns, 'b>;
367
368    fn into_iter(self) -> Self::IntoIter {
369        NamespaceStackIterator::new(self)
370    }
371}
372
373//a Test
374#[cfg(test)]
375mod test {
376    use crate::names::{Namespace, NamespaceStack};
377    fn dump_namespace(nst: &NamespaceStack) {
378        for i in nst {
379            let mut s = String::new();
380            nst.fmt_map(&mut s, i).unwrap();
381            println!("{}", s);
382        }
383    }
384    #[test]
385    fn test_defaults() {
386        let mut ns = Namespace::new(false);
387        let mut nst = NamespaceStack::new(&mut ns);
388
389        nst.add_default_xmls();
390
391        assert_eq!(nst.into_iter().count(), 3);
392
393        let pid = nst.find_prefix_id("").unwrap();
394        assert!(pid.is_none());
395        assert_eq!(nst.prefix_str(pid), "");
396        assert_eq!(nst.uri_str(nst.find_mapping(pid).unwrap()), "");
397
398        let pid = nst.find_prefix_id("xml").unwrap();
399        assert_eq!(nst.prefix_str(pid), "xml");
400        assert_eq!(
401            nst.uri_str(nst.find_mapping(pid).unwrap()),
402            "http://www.w3.org/XML/1998/namespace"
403        );
404
405        let pid = nst.find_prefix_id("xmlns").unwrap();
406        assert_eq!(nst.prefix_str(pid), "xmlns");
407        assert_eq!(
408            nst.uri_str(nst.find_mapping(pid).unwrap()),
409            "http://www.w3.org/2000/xmlns/"
410        );
411
412        let pid = nst.find_prefix_id("fred");
413        assert_eq!(pid, None);
414
415        nst.push_frame();
416
417        nst.add_ns("fred", "http://fred.com");
418        assert_eq!(nst.into_iter().count(), 4);
419
420        let pid = nst.find_prefix_id("fred").unwrap();
421        assert_eq!(nst.prefix_str(pid), "fred");
422        assert_eq!(
423            nst.uri_str(nst.find_mapping(pid).unwrap()),
424            "http://fred.com"
425        );
426
427        nst.add_ns_if_unset("fred", "http://NOTfred.com");
428        assert_eq!(
429            nst.uri_str(nst.find_mapping(pid).unwrap()),
430            "http://fred.com"
431        );
432
433        nst.add_ns("fred", "http://fred2.com");
434        assert_eq!(
435            nst.uri_str(nst.find_mapping(pid).unwrap()),
436            "http://fred2.com"
437        );
438
439        nst.add_ns("xml", "http://xml_override");
440        let pid = nst.find_prefix_id("xml").unwrap();
441        assert_eq!(nst.prefix_str(pid), "xml");
442        assert_eq!(
443            nst.uri_str(nst.find_mapping(pid).unwrap()),
444            "http://xml_override"
445        );
446
447        dump_namespace(&nst);
448
449        nst.pop_frame();
450
451        assert_eq!(nst.into_iter().count(), 3);
452
453        let pid = nst.find_prefix_id("").unwrap();
454        assert!(pid.is_none());
455        assert_eq!(nst.prefix_str(pid), "");
456        assert_eq!(nst.uri_str(nst.find_mapping(pid).unwrap()), "");
457
458        let pid = nst.find_prefix_id("xml").unwrap();
459        assert_eq!(nst.prefix_str(pid), "xml");
460        assert_eq!(
461            nst.uri_str(nst.find_mapping(pid).unwrap()),
462            "http://www.w3.org/XML/1998/namespace"
463        );
464
465        let pid = nst.find_prefix_id("xmlns").unwrap();
466        assert_eq!(nst.prefix_str(pid), "xmlns");
467        assert_eq!(
468            nst.uri_str(nst.find_mapping(pid).unwrap()),
469            "http://www.w3.org/2000/xmlns/"
470        );
471
472        let pid = nst.find_prefix_id("fred").unwrap(); // Note not None any more
473        assert_eq!(nst.find_mapping(pid), None);
474    }
475}