1 | //! Contains namespace manipulation types and functions. |
2 | |
3 | use std::borrow::Cow; |
4 | use std::collections::btree_map::Iter as Entries; |
5 | use std::collections::btree_map::{BTreeMap, Entry}; |
6 | use std::collections::HashSet; |
7 | use std::iter::{Map, Rev}; |
8 | use std::slice::Iter; |
9 | |
10 | /// Designates prefix for namespace definitions. |
11 | /// |
12 | /// See [Namespaces in XML][namespace] spec for more information. |
13 | /// |
14 | /// [namespace]: http://www.w3.org/TR/xml-names/#ns-decl |
15 | pub const NS_XMLNS_PREFIX: &str = "xmlns" ; |
16 | |
17 | /// Designates the standard URI for `xmlns` prefix. |
18 | /// |
19 | /// See [A Namespace Name for xmlns Attributes][namespace] for more information. |
20 | /// |
21 | /// [namespace]: http://www.w3.org/2000/xmlns/ |
22 | pub const NS_XMLNS_URI: &str = "http://www.w3.org/2000/xmlns/" ; |
23 | |
24 | /// Designates prefix for a namespace containing several special predefined attributes. |
25 | /// |
26 | /// See [2.10 White Space handling][1], [2.1 Language Identification][2], |
27 | /// [XML Base specification][3] and [xml:id specification][4] for more information. |
28 | /// |
29 | /// [1]: http://www.w3.org/TR/REC-xml/#sec-white-space |
30 | /// [2]: http://www.w3.org/TR/REC-xml/#sec-lang-tag |
31 | /// [3]: http://www.w3.org/TR/xmlbase/ |
32 | /// [4]: http://www.w3.org/TR/xml-id/ |
33 | pub const NS_XML_PREFIX: &str = "xml" ; |
34 | |
35 | /// Designates the standard URI for `xml` prefix. |
36 | /// |
37 | /// See `NS_XML_PREFIX` documentation for more information. |
38 | pub const NS_XML_URI: &str = "http://www.w3.org/XML/1998/namespace" ; |
39 | |
40 | /// Designates the absence of prefix in a qualified name. |
41 | /// |
42 | /// This constant should be used to define or query default namespace which should be used |
43 | /// for element or attribute names without prefix. For example, if a namespace mapping |
44 | /// at a particular point in the document contains correspondence like |
45 | /// |
46 | /// ```none |
47 | /// NS_NO_PREFIX --> urn:some:namespace |
48 | /// ``` |
49 | /// |
50 | /// then all names declared without an explicit prefix `urn:some:namespace` is assumed as |
51 | /// a namespace URI. |
52 | /// |
53 | /// By default empty prefix corresponds to absence of namespace, but this can change either |
54 | /// when writing an XML document (manually) or when reading an XML document (based on namespace |
55 | /// declarations). |
56 | pub const NS_NO_PREFIX: &str = "" ; |
57 | |
58 | /// Designates an empty namespace URI, which is equivalent to absence of namespace. |
59 | /// |
60 | /// This constant should not usually be used directly; it is used to designate that |
61 | /// empty prefix corresponds to absent namespace in `NamespaceStack` instances created with |
62 | /// `NamespaceStack::default()`. Therefore, it can be used to restore `NS_NO_PREFIX` mapping |
63 | /// in a namespace back to its default value. |
64 | pub const NS_EMPTY_URI: &str = "" ; |
65 | |
66 | /// Namespace is a map from prefixes to namespace URIs. |
67 | /// |
68 | /// No prefix (i.e. default namespace) is designated by `NS_NO_PREFIX` constant. |
69 | #[derive (PartialEq, Eq, Clone, Debug)] |
70 | pub struct Namespace(pub BTreeMap<String, String>); |
71 | |
72 | impl Namespace { |
73 | /// Returns an empty namespace. |
74 | #[inline ] |
75 | #[must_use ] |
76 | pub fn empty() -> Namespace { |
77 | Namespace(BTreeMap::new()) |
78 | } |
79 | |
80 | /// Checks whether this namespace is empty. |
81 | #[inline ] |
82 | #[must_use ] |
83 | pub fn is_empty(&self) -> bool { |
84 | self.0.is_empty() |
85 | } |
86 | |
87 | /// Checks whether this namespace is essentially empty, that is, it does not contain |
88 | /// anything but default mappings. |
89 | #[must_use ] |
90 | pub fn is_essentially_empty(&self) -> bool { |
91 | // a shortcut for a namespace which is definitely not empty |
92 | if self.0.len() > 3 { return false; } |
93 | |
94 | self.0.iter().all(|(k, v)| match (&**k, &**v) { |
95 | (NS_NO_PREFIX, NS_EMPTY_URI) => true, |
96 | (NS_XMLNS_PREFIX, NS_XMLNS_URI) => true, |
97 | (NS_XML_PREFIX, NS_XML_URI) => true, |
98 | _ => false |
99 | }) |
100 | } |
101 | |
102 | /// Checks whether this namespace mapping contains the given prefix. |
103 | /// |
104 | /// # Parameters |
105 | /// * `prefix` --- namespace prefix. |
106 | /// |
107 | /// # Return value |
108 | /// `true` if this namespace contains the given prefix, `false` otherwise. |
109 | #[inline ] |
110 | pub fn contains<P: ?Sized + AsRef<str>>(&self, prefix: &P) -> bool { |
111 | self.0.contains_key(prefix.as_ref()) |
112 | } |
113 | |
114 | /// Puts a mapping into this namespace. |
115 | /// |
116 | /// This method does not override any already existing mappings. |
117 | /// |
118 | /// Returns a boolean flag indicating whether the map already contained |
119 | /// the given prefix. |
120 | /// |
121 | /// # Parameters |
122 | /// * `prefix` --- namespace prefix; |
123 | /// * `uri` --- namespace URI. |
124 | /// |
125 | /// # Return value |
126 | /// `true` if `prefix` has been inserted successfully; `false` if the `prefix` |
127 | /// was already present in the namespace. |
128 | pub fn put<P, U>(&mut self, prefix: P, uri: U) -> bool |
129 | where P: Into<String>, U: Into<String> |
130 | { |
131 | match self.0.entry(prefix.into()) { |
132 | Entry::Occupied(_) => false, |
133 | Entry::Vacant(ve) => { |
134 | ve.insert(uri.into()); |
135 | true |
136 | } |
137 | } |
138 | } |
139 | |
140 | /// Puts a mapping into this namespace forcefully. |
141 | /// |
142 | /// This method, unlike `put()`, does replace an already existing mapping. |
143 | /// |
144 | /// Returns previous URI which was assigned to the given prefix, if it is present. |
145 | /// |
146 | /// # Parameters |
147 | /// * `prefix` --- namespace prefix; |
148 | /// * `uri` --- namespace URI. |
149 | /// |
150 | /// # Return value |
151 | /// `Some(uri)` with `uri` being a previous URI assigned to the `prefix`, or |
152 | /// `None` if such prefix was not present in the namespace before. |
153 | pub fn force_put<P, U>(&mut self, prefix: P, uri: U) -> Option<String> |
154 | where P: Into<String>, U: Into<String> |
155 | { |
156 | self.0.insert(prefix.into(), uri.into()) |
157 | } |
158 | |
159 | /// Queries the namespace for the given prefix. |
160 | /// |
161 | /// # Parameters |
162 | /// * `prefix` --- namespace prefix. |
163 | /// |
164 | /// # Return value |
165 | /// Namespace URI corresponding to the given prefix, if it is present. |
166 | pub fn get<'a, P: ?Sized + AsRef<str>>(&'a self, prefix: &P) -> Option<&'a str> { |
167 | self.0.get(prefix.as_ref()).map(|s| &**s) |
168 | } |
169 | |
170 | /// Borrowed namespace for the writer |
171 | #[must_use ] |
172 | pub fn borrow(&self) -> Cow<'_, Self> { |
173 | Cow::Borrowed(self) |
174 | } |
175 | } |
176 | |
177 | /// An alias for iterator type for namespace mappings contained in a namespace. |
178 | pub type NamespaceMappings<'a> = Map< |
179 | Entries<'a, String, String>, |
180 | for<'b> fn((&'b String, &'b String)) -> UriMapping<'b> |
181 | >; |
182 | |
183 | impl<'a> IntoIterator for &'a Namespace { |
184 | type Item = UriMapping<'a>; |
185 | type IntoIter = NamespaceMappings<'a>; |
186 | |
187 | fn into_iter(self) -> Self::IntoIter { |
188 | fn mapper<'a>((prefix: &String, uri: &String): (&'a String, &'a String)) -> UriMapping<'a> { |
189 | (prefix, uri) |
190 | } |
191 | self.0.iter().map(mapper) |
192 | } |
193 | } |
194 | |
195 | /// Namespace stack is a sequence of namespaces. |
196 | /// |
197 | /// Namespace stack is used to represent cumulative namespace consisting of |
198 | /// combined namespaces from nested elements. |
199 | #[derive (Clone, Eq, PartialEq, Debug)] |
200 | pub struct NamespaceStack(pub Vec<Namespace>); |
201 | |
202 | impl NamespaceStack { |
203 | /// Returns an empty namespace stack. |
204 | #[inline ] |
205 | #[must_use ] |
206 | pub fn empty() -> NamespaceStack { |
207 | NamespaceStack(Vec::with_capacity(2)) |
208 | } |
209 | |
210 | /// Returns a namespace stack with default items in it. |
211 | /// |
212 | /// Default items are the following: |
213 | /// |
214 | /// * `xml` → `http://www.w3.org/XML/1998/namespace`; |
215 | /// * `xmlns` → `http://www.w3.org/2000/xmlns/`. |
216 | #[inline ] |
217 | #[must_use ] |
218 | pub fn default() -> NamespaceStack { |
219 | let mut nst = NamespaceStack::empty(); |
220 | nst.push_empty(); |
221 | // xml namespace |
222 | nst.put(NS_XML_PREFIX, NS_XML_URI); |
223 | // xmlns namespace |
224 | nst.put(NS_XMLNS_PREFIX, NS_XMLNS_URI); |
225 | // empty namespace |
226 | nst.put(NS_NO_PREFIX, NS_EMPTY_URI); |
227 | nst |
228 | } |
229 | |
230 | /// Adds an empty namespace to the top of this stack. |
231 | #[inline ] |
232 | pub fn push_empty(&mut self) -> &mut NamespaceStack { |
233 | self.0.push(Namespace::empty()); |
234 | self |
235 | } |
236 | |
237 | /// Removes the topmost namespace in this stack. |
238 | /// |
239 | /// Panics if the stack is empty. |
240 | #[inline ] |
241 | pub fn pop(&mut self) -> Namespace { |
242 | self.0.pop().unwrap() |
243 | } |
244 | |
245 | /// Removes the topmost namespace in this stack. |
246 | /// |
247 | /// Returns `Some(namespace)` if this stack is not empty and `None` otherwise. |
248 | #[inline ] |
249 | pub fn try_pop(&mut self) -> Option<Namespace> { |
250 | self.0.pop() |
251 | } |
252 | |
253 | /// Borrows the topmost namespace mutably, leaving the stack intact. |
254 | /// |
255 | /// Panics if the stack is empty. |
256 | #[inline ] |
257 | pub fn peek_mut(&mut self) -> &mut Namespace { |
258 | self.0.last_mut().unwrap() |
259 | } |
260 | |
261 | /// Borrows the topmost namespace immutably, leaving the stack intact. |
262 | /// |
263 | /// Panics if the stack is empty. |
264 | #[inline ] |
265 | #[must_use ] |
266 | pub fn peek(&self) -> &Namespace { |
267 | self.0.last().unwrap() |
268 | } |
269 | |
270 | /// Puts a mapping into the topmost namespace if this stack does not already contain one. |
271 | /// |
272 | /// Returns a boolean flag indicating whether the insertion has completed successfully. |
273 | /// Note that both key and value are matched and the mapping is inserted if either |
274 | /// namespace prefix is not already mapped, or if it is mapped, but to a different URI. |
275 | /// |
276 | /// # Parameters |
277 | /// * `prefix` --- namespace prefix; |
278 | /// * `uri` --- namespace URI. |
279 | /// |
280 | /// # Return value |
281 | /// `true` if `prefix` has been inserted successfully; `false` if the `prefix` |
282 | /// was already present in the namespace stack. |
283 | pub fn put_checked<P, U>(&mut self, prefix: P, uri: U) -> bool |
284 | where P: Into<String> + AsRef<str>, |
285 | U: Into<String> + AsRef<str> |
286 | { |
287 | if self.0.iter().any(|ns| ns.get(&prefix) == Some(uri.as_ref())) { |
288 | false |
289 | } else { |
290 | self.put(prefix, uri); |
291 | true |
292 | } |
293 | } |
294 | |
295 | /// Puts a mapping into the topmost namespace in this stack. |
296 | /// |
297 | /// This method does not override a mapping in the topmost namespace if it is |
298 | /// already present, however, it does not depend on other namespaces in the stack, |
299 | /// so it is possible to put a mapping which is present in lower namespaces. |
300 | /// |
301 | /// Returns a boolean flag indicating whether the insertion has completed successfully. |
302 | /// |
303 | /// # Parameters |
304 | /// * `prefix` --- namespace prefix; |
305 | /// * `uri` --- namespace URI. |
306 | /// |
307 | /// # Return value |
308 | /// `true` if `prefix` has been inserted successfully; `false` if the `prefix` |
309 | /// was already present in the namespace. |
310 | #[inline ] |
311 | pub fn put<P, U>(&mut self, prefix: P, uri: U) -> bool |
312 | where P: Into<String>, U: Into<String> |
313 | { |
314 | if let Some(ns) = self.0.last_mut() { |
315 | ns.put(prefix, uri) |
316 | } else { |
317 | false |
318 | } |
319 | } |
320 | |
321 | /// Performs a search for the given prefix in the whole stack. |
322 | /// |
323 | /// This method walks the stack from top to bottom, querying each namespace |
324 | /// in order for the given prefix. If none of the namespaces contains the prefix, |
325 | /// `None` is returned. |
326 | /// |
327 | /// # Parameters |
328 | /// * `prefix` --- namespace prefix. |
329 | #[inline ] |
330 | pub fn get<'a, P: ?Sized + AsRef<str>>(&'a self, prefix: &P) -> Option<&'a str> { |
331 | let prefix = prefix.as_ref(); |
332 | for ns in self.0.iter().rev() { |
333 | match ns.get(prefix) { |
334 | None => {}, |
335 | r => return r, |
336 | } |
337 | } |
338 | None |
339 | } |
340 | |
341 | /// Combines this stack of namespaces into a single namespace. |
342 | /// |
343 | /// Namespaces are combined in left-to-right order, that is, rightmost namespace |
344 | /// elements take priority over leftmost ones. |
345 | #[must_use ] |
346 | pub fn squash(&self) -> Namespace { |
347 | let mut result = BTreeMap::new(); |
348 | for ns in &self.0 { |
349 | result.extend(ns.0.iter().map(|(k, v)| (k.clone(), v.clone()))); |
350 | } |
351 | Namespace(result) |
352 | } |
353 | |
354 | /// Returns an object which implements `Extend` using `put_checked()` instead of `put()`. |
355 | /// |
356 | /// See `CheckedTarget` for more information. |
357 | #[inline ] |
358 | pub fn checked_target(&mut self) -> CheckedTarget<'_> { |
359 | CheckedTarget(self) |
360 | } |
361 | |
362 | /// Returns an iterator over all mappings in this namespace stack. |
363 | #[inline ] |
364 | #[must_use ] |
365 | pub fn iter(&self) -> NamespaceStackMappings<'_> { |
366 | self.into_iter() |
367 | } |
368 | } |
369 | |
370 | /// An iterator over mappings from prefixes to URIs in a namespace stack. |
371 | /// |
372 | /// # Example |
373 | /// ``` |
374 | /// # use xml::namespace::NamespaceStack; |
375 | /// let mut nst = NamespaceStack::empty(); |
376 | /// nst.push_empty(); |
377 | /// nst.put("a" , "urn:A" ); |
378 | /// nst.put("b" , "urn:B" ); |
379 | /// nst.push_empty(); |
380 | /// nst.put("c" , "urn:C" ); |
381 | /// |
382 | /// assert_eq!(vec![("c" , "urn:C" ), ("a" , "urn:A" ), ("b" , "urn:B" )], nst.iter().collect::<Vec<_>>()); |
383 | /// ``` |
384 | pub struct NamespaceStackMappings<'a> { |
385 | namespaces: Rev<Iter<'a, Namespace>>, |
386 | current_namespace: Option<NamespaceMappings<'a>>, |
387 | used_keys: HashSet<&'a str>, |
388 | } |
389 | |
390 | impl<'a> NamespaceStackMappings<'a> { |
391 | fn go_to_next_namespace(&mut self) -> bool { |
392 | self.current_namespace = self.namespaces.next().map(|ns: &Namespace| ns.into_iter()); |
393 | self.current_namespace.is_some() |
394 | } |
395 | } |
396 | |
397 | impl<'a> Iterator for NamespaceStackMappings<'a> { |
398 | type Item = UriMapping<'a>; |
399 | |
400 | fn next(&mut self) -> Option<UriMapping<'a>> { |
401 | // If there is no current namespace and no next namespace, we're finished |
402 | if self.current_namespace.is_none() && !self.go_to_next_namespace() { |
403 | return None; |
404 | } |
405 | let next_item = self.current_namespace.as_mut()?.next(); |
406 | |
407 | match next_item { |
408 | // There is an element in the current namespace |
409 | Some((k, v)) => if self.used_keys.contains(&k) { |
410 | // If the current key is used, go to the next one |
411 | self.next() |
412 | } else { |
413 | // Otherwise insert the current key to the set of used keys and |
414 | // return the mapping |
415 | self.used_keys.insert(k); |
416 | Some((k, v)) |
417 | }, |
418 | // Current namespace is exhausted |
419 | None => if self.go_to_next_namespace() { |
420 | // If there is next namespace, continue from it |
421 | self.next() |
422 | } else { |
423 | // No next namespace, exiting |
424 | None |
425 | } |
426 | } |
427 | } |
428 | } |
429 | |
430 | impl<'a> IntoIterator for &'a NamespaceStack { |
431 | type Item = UriMapping<'a>; |
432 | type IntoIter = NamespaceStackMappings<'a>; |
433 | |
434 | fn into_iter(self) -> Self::IntoIter { |
435 | NamespaceStackMappings { |
436 | namespaces: self.0.iter().rev(), |
437 | current_namespace: None, |
438 | used_keys: HashSet::new(), |
439 | } |
440 | } |
441 | } |
442 | |
443 | /// A type alias for a pair of `(prefix, uri)` values returned by namespace iterators. |
444 | pub type UriMapping<'a> = (&'a str, &'a str); |
445 | |
446 | impl<'a> Extend<UriMapping<'a>> for Namespace { |
447 | fn extend<T>(&mut self, iterable: T) where T: IntoIterator<Item=UriMapping<'a>> { |
448 | for (prefix: &str, uri: &str) in iterable { |
449 | self.put(prefix, uri); |
450 | } |
451 | } |
452 | } |
453 | |
454 | impl<'a> Extend<UriMapping<'a>> for NamespaceStack { |
455 | fn extend<T>(&mut self, iterable: T) where T: IntoIterator<Item=UriMapping<'a>> { |
456 | for (prefix: &str, uri: &str) in iterable { |
457 | self.put(prefix, uri); |
458 | } |
459 | } |
460 | } |
461 | |
462 | /// A wrapper around `NamespaceStack` which implements `Extend` using `put_checked()`. |
463 | /// |
464 | /// # Example |
465 | /// |
466 | /// ``` |
467 | /// # use xml::namespace::NamespaceStack; |
468 | /// |
469 | /// let mut nst = NamespaceStack::empty(); |
470 | /// nst.push_empty(); |
471 | /// nst.put("a" , "urn:A" ); |
472 | /// nst.put("b" , "urn:B" ); |
473 | /// nst.push_empty(); |
474 | /// nst.put("c" , "urn:C" ); |
475 | /// |
476 | /// nst.checked_target().extend(vec![("a" , "urn:Z" ), ("b" , "urn:B" ), ("c" , "urn:Y" ), ("d" , "urn:D" )]); |
477 | /// assert_eq!( |
478 | /// vec![("a" , "urn:Z" ), ("c" , "urn:C" ), ("d" , "urn:D" ), ("b" , "urn:B" )], |
479 | /// nst.iter().collect::<Vec<_>>() |
480 | /// ); |
481 | /// ``` |
482 | /// |
483 | /// Compare: |
484 | /// |
485 | /// ``` |
486 | /// # use xml::namespace::NamespaceStack; |
487 | /// # let mut nst = NamespaceStack::empty(); |
488 | /// # nst.push_empty(); |
489 | /// # nst.put("a" , "urn:A" ); |
490 | /// # nst.put("b" , "urn:B" ); |
491 | /// # nst.push_empty(); |
492 | /// # nst.put("c" , "urn:C" ); |
493 | /// |
494 | /// nst.extend(vec![("a" , "urn:Z" ), ("b" , "urn:B" ), ("c" , "urn:Y" ), ("d" , "urn:D" )]); |
495 | /// assert_eq!( |
496 | /// vec![("a" , "urn:Z" ), ("b" , "urn:B" ), ("c" , "urn:C" ), ("d" , "urn:D" )], |
497 | /// nst.iter().collect::<Vec<_>>() |
498 | /// ); |
499 | /// ``` |
500 | pub struct CheckedTarget<'a>(&'a mut NamespaceStack); |
501 | |
502 | impl<'a, 'b> Extend<UriMapping<'b>> for CheckedTarget<'a> { |
503 | fn extend<T>(&mut self, iterable: T) where T: IntoIterator<Item=UriMapping<'b>> { |
504 | for (prefix: &str, uri: &str) in iterable { |
505 | self.0.put_checked(prefix, uri); |
506 | } |
507 | } |
508 | } |
509 | |