forked from mirrors/gecko-dev
Depends on D13115 Differential Revision: https://phabricator.services.mozilla.com/D13116 --HG-- rename : third_party/rust/khronos_api/api_webgl/extensions/WEBGL_compressed_texture_atc/extension.xml => third_party/rust/khronos_api/api_webgl/extensions/rejected/WEBGL_compressed_texture_atc/extension.xml rename : third_party/rust/khronos_api/api_webgl/extensions/WEBGL_get_buffer_sub_data_async/extension.xml => third_party/rust/khronos_api/api_webgl/extensions/rejected/WEBGL_get_buffer_sub_data_async/extension.xml rename : third_party/rust/khronos_api/api_webgl/extensions/WEBGL_shared_resources/extension.xml => third_party/rust/khronos_api/api_webgl/extensions/rejected/WEBGL_shared_resources/extension.xml extra : moz-landing-system : lando
485 lines
16 KiB
Rust
485 lines
16 KiB
Rust
//! Contains namespace manipulation types and functions.
|
|
|
|
use std::iter::{Map, Rev};
|
|
use std::collections::btree_map::{BTreeMap, Entry};
|
|
use std::collections::btree_map::Iter as Entries;
|
|
use std::collections::HashSet;
|
|
use std::slice::Iter;
|
|
|
|
/// Designates prefix for namespace definitions.
|
|
///
|
|
/// See [Namespaces in XML][namespace] spec for more information.
|
|
///
|
|
/// [namespace]: http://www.w3.org/TR/xml-names/#ns-decl
|
|
pub const NS_XMLNS_PREFIX: &'static str = "xmlns";
|
|
|
|
/// Designates the standard URI for `xmlns` prefix.
|
|
///
|
|
/// See [A Namespace Name for xmlns Attributes][1] for more information.
|
|
///
|
|
/// [namespace]: http://www.w3.org/2000/xmlns/
|
|
pub const NS_XMLNS_URI: &'static str = "http://www.w3.org/2000/xmlns/";
|
|
|
|
/// Designates prefix for a namespace containing several special predefined attributes.
|
|
///
|
|
/// See [2.10 White Space handling][1], [2.1 Language Identification][2],
|
|
/// [XML Base specification][3] and [xml:id specification][4] for more information.
|
|
///
|
|
/// [1]: http://www.w3.org/TR/REC-xml/#sec-white-space
|
|
/// [2]: http://www.w3.org/TR/REC-xml/#sec-lang-tag
|
|
/// [3]: http://www.w3.org/TR/xmlbase/
|
|
/// [4]: http://www.w3.org/TR/xml-id/
|
|
pub const NS_XML_PREFIX: &'static str = "xml";
|
|
|
|
/// Designates the standard URI for `xml` prefix.
|
|
///
|
|
/// See `NS_XML_PREFIX` documentation for more information.
|
|
pub const NS_XML_URI: &'static str = "http://www.w3.org/XML/1998/namespace";
|
|
|
|
/// Designates the absence of prefix in a qualified name.
|
|
///
|
|
/// This constant should be used to define or query default namespace which should be used
|
|
/// for element or attribute names without prefix. For example, if a namespace mapping
|
|
/// at a particular point in the document contains correspondence like
|
|
///
|
|
/// ```none
|
|
/// NS_NO_PREFIX --> urn:some:namespace
|
|
/// ```
|
|
///
|
|
/// then all names declared without an explicit prefix `urn:some:namespace` is assumed as
|
|
/// a namespace URI.
|
|
///
|
|
/// By default empty prefix corresponds to absence of namespace, but this can change either
|
|
/// when writing an XML document (manually) or when reading an XML document (based on namespace
|
|
/// declarations).
|
|
pub const NS_NO_PREFIX: &'static str = "";
|
|
|
|
/// Designates an empty namespace URI, which is equivalent to absence of namespace.
|
|
///
|
|
/// This constant should not usually be used directly; it is used to designate that
|
|
/// empty prefix corresponds to absent namespace in `NamespaceStack` instances created with
|
|
/// `NamespaceStack::default()`. Therefore, it can be used to restore `NS_NO_PREFIX` mapping
|
|
/// in a namespace back to its default value.
|
|
pub const NS_EMPTY_URI: &'static str = "";
|
|
|
|
/// Namespace is a map from prefixes to namespace URIs.
|
|
///
|
|
/// No prefix (i.e. default namespace) is designated by `NS_NO_PREFIX` constant.
|
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
|
pub struct Namespace(pub BTreeMap<String, String>);
|
|
|
|
impl Namespace {
|
|
/// Returns an empty namespace.
|
|
#[inline]
|
|
pub fn empty() -> Namespace { Namespace(BTreeMap::new()) }
|
|
|
|
/// Checks whether this namespace is empty.
|
|
#[inline]
|
|
pub fn is_empty(&self) -> bool {
|
|
self.0.is_empty()
|
|
}
|
|
|
|
/// Checks whether this namespace is essentially empty, that is, it does not contain
|
|
/// anything but default mappings.
|
|
pub fn is_essentially_empty(&self) -> bool {
|
|
// a shortcut for a namespace which is definitely not empty
|
|
if self.0.len() > 3 { return false; }
|
|
|
|
self.0.iter().all(|(k, v)| match (&**k, &**v) {
|
|
(NS_NO_PREFIX, NS_EMPTY_URI) => true,
|
|
(NS_XMLNS_PREFIX, NS_XMLNS_URI) => true,
|
|
(NS_XML_PREFIX, NS_XML_URI) => true,
|
|
_ => false
|
|
})
|
|
}
|
|
|
|
/// Checks whether this namespace mapping contains the given prefix.
|
|
///
|
|
/// # Parameters
|
|
/// * `prefix` --- namespace prefix.
|
|
///
|
|
/// # Return value
|
|
/// `true` if this namespace contains the given prefix, `false` otherwise.
|
|
#[inline]
|
|
pub fn contains<P: ?Sized+AsRef<str>>(&self, prefix: &P) -> bool {
|
|
self.0.contains_key(prefix.as_ref())
|
|
}
|
|
|
|
/// Puts a mapping into this namespace.
|
|
///
|
|
/// This method does not override any already existing mappings.
|
|
///
|
|
/// Returns a boolean flag indicating whether the map already contained
|
|
/// the given prefix.
|
|
///
|
|
/// # Parameters
|
|
/// * `prefix` --- namespace prefix;
|
|
/// * `uri` --- namespace URI.
|
|
///
|
|
/// # Return value
|
|
/// `true` if `prefix` has been inserted successfully; `false` if the `prefix`
|
|
/// was already present in the namespace.
|
|
pub fn put<P, U>(&mut self, prefix: P, uri: U) -> bool
|
|
where P: Into<String>, U: Into<String>
|
|
{
|
|
match self.0.entry(prefix.into()) {
|
|
Entry::Occupied(_) => false,
|
|
Entry::Vacant(ve) => {
|
|
ve.insert(uri.into());
|
|
true
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Puts a mapping into this namespace forcefully.
|
|
///
|
|
/// This method, unlike `put()`, does replace an already existing mapping.
|
|
///
|
|
/// Returns previous URI which was assigned to the given prefix, if it is present.
|
|
///
|
|
/// # Parameters
|
|
/// * `prefix` --- namespace prefix;
|
|
/// * `uri` --- namespace URI.
|
|
///
|
|
/// # Return value
|
|
/// `Some(uri)` with `uri` being a previous URI assigned to the `prefix`, or
|
|
/// `None` if such prefix was not present in the namespace before.
|
|
pub fn force_put<P, U>(&mut self, prefix: P, uri: U) -> Option<String>
|
|
where P: Into<String>, U: Into<String>
|
|
{
|
|
self.0.insert(prefix.into(), uri.into())
|
|
}
|
|
|
|
/// Queries the namespace for the given prefix.
|
|
///
|
|
/// # Parameters
|
|
/// * `prefix` --- namespace prefix.
|
|
///
|
|
/// # Return value
|
|
/// Namespace URI corresponding to the given prefix, if it is present.
|
|
pub fn get<'a, P: ?Sized+AsRef<str>>(&'a self, prefix: &P) -> Option<&'a str> {
|
|
self.0.get(prefix.as_ref()).map(|s| &**s)
|
|
}
|
|
}
|
|
|
|
/// An alias for iterator type for namespace mappings contained in a namespace.
|
|
pub type NamespaceMappings<'a> = Map<
|
|
Entries<'a, String, String>,
|
|
for<'b> fn((&'b String, &'b String)) -> UriMapping<'b>
|
|
>;
|
|
|
|
impl<'a> IntoIterator for &'a Namespace {
|
|
type Item = UriMapping<'a>;
|
|
type IntoIter = NamespaceMappings<'a>;
|
|
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
fn mapper<'a>((prefix, uri): (&'a String, &'a String)) -> UriMapping<'a> {
|
|
(&*prefix, &*uri)
|
|
}
|
|
self.0.iter().map(mapper)
|
|
}
|
|
}
|
|
|
|
/// Namespace stack is a sequence of namespaces.
|
|
///
|
|
/// Namespace stack is used to represent cumulative namespace consisting of
|
|
/// combined namespaces from nested elements.
|
|
#[derive(Clone, Eq, PartialEq, Debug)]
|
|
pub struct NamespaceStack(pub Vec<Namespace>);
|
|
|
|
impl NamespaceStack {
|
|
/// Returns an empty namespace stack.
|
|
#[inline]
|
|
pub fn empty() -> NamespaceStack { NamespaceStack(Vec::with_capacity(2)) }
|
|
|
|
/// Returns a namespace stack with default items in it.
|
|
///
|
|
/// Default items are the following:
|
|
///
|
|
/// * `xml` → `http://www.w3.org/XML/1998/namespace`;
|
|
/// * `xmlns` → `http://www.w3.org/2000/xmlns/`.
|
|
#[inline]
|
|
pub fn default() -> NamespaceStack {
|
|
let mut nst = NamespaceStack::empty();
|
|
nst.push_empty();
|
|
// xml namespace
|
|
nst.put(NS_XML_PREFIX, NS_XML_URI);
|
|
// xmlns namespace
|
|
nst.put(NS_XMLNS_PREFIX, NS_XMLNS_URI);
|
|
// empty namespace
|
|
nst.put(NS_NO_PREFIX, NS_EMPTY_URI);
|
|
nst
|
|
}
|
|
|
|
/// Adds an empty namespace to the top of this stack.
|
|
#[inline]
|
|
pub fn push_empty(&mut self) -> &mut NamespaceStack {
|
|
self.0.push(Namespace::empty());
|
|
self
|
|
}
|
|
|
|
/// Removes the topmost namespace in this stack.
|
|
///
|
|
/// Panics if the stack is empty.
|
|
#[inline]
|
|
pub fn pop(&mut self) -> Namespace {
|
|
self.0.pop().unwrap()
|
|
}
|
|
|
|
/// Removes the topmost namespace in this stack.
|
|
///
|
|
/// Returns `Some(namespace)` if this stack is not empty and `None` otherwise.
|
|
#[inline]
|
|
pub fn try_pop(&mut self) -> Option<Namespace> {
|
|
self.0.pop()
|
|
}
|
|
|
|
/// Borrows the topmost namespace mutably, leaving the stack intact.
|
|
///
|
|
/// Panics if the stack is empty.
|
|
#[inline]
|
|
pub fn peek_mut(&mut self) -> &mut Namespace {
|
|
self.0.last_mut().unwrap()
|
|
}
|
|
|
|
/// Borrows the topmost namespace immutably, leaving the stack intact.
|
|
///
|
|
/// Panics if the stack is empty.
|
|
#[inline]
|
|
pub fn peek(&self) -> &Namespace {
|
|
self.0.last().unwrap()
|
|
}
|
|
|
|
/// Puts a mapping into the topmost namespace if this stack does not already contain one.
|
|
///
|
|
/// Returns a boolean flag indicating whether the insertion has completed successfully.
|
|
/// Note that both key and value are matched and the mapping is inserted if either
|
|
/// namespace prefix is not already mapped, or if it is mapped, but to a different URI.
|
|
///
|
|
/// # Parameters
|
|
/// * `prefix` --- namespace prefix;
|
|
/// * `uri` --- namespace URI.
|
|
///
|
|
/// # Return value
|
|
/// `true` if `prefix` has been inserted successfully; `false` if the `prefix`
|
|
/// was already present in the namespace stack.
|
|
pub fn put_checked<P, U>(&mut self, prefix: P, uri: U) -> bool
|
|
where P: Into<String> + AsRef<str>,
|
|
U: Into<String> + AsRef<str>
|
|
{
|
|
if self.0.iter().any(|ns| ns.get(&prefix) == Some(uri.as_ref())) {
|
|
false
|
|
} else {
|
|
self.put(prefix, uri);
|
|
true
|
|
}
|
|
}
|
|
|
|
/// Puts a mapping into the topmost namespace in this stack.
|
|
///
|
|
/// This method does not override a mapping in the topmost namespace if it is
|
|
/// already present, however, it does not depend on other namespaces in the stack,
|
|
/// so it is possible to put a mapping which is present in lower namespaces.
|
|
///
|
|
/// Returns a boolean flag indicating whether the insertion has completed successfully.
|
|
///
|
|
/// # Parameters
|
|
/// * `prefix` --- namespace prefix;
|
|
/// * `uri` --- namespace URI.
|
|
///
|
|
/// # Return value
|
|
/// `true` if `prefix` has been inserted successfully; `false` if the `prefix`
|
|
/// was already present in the namespace.
|
|
#[inline]
|
|
pub fn put<P, U>(&mut self, prefix: P, uri: U) -> bool
|
|
where P: Into<String>, U: Into<String>
|
|
{
|
|
self.0.last_mut().unwrap().put(prefix, uri)
|
|
}
|
|
|
|
/// Performs a search for the given prefix in the whole stack.
|
|
///
|
|
/// This method walks the stack from top to bottom, querying each namespace
|
|
/// in order for the given prefix. If none of the namespaces contains the prefix,
|
|
/// `None` is returned.
|
|
///
|
|
/// # Parameters
|
|
/// * `prefix` --- namespace prefix.
|
|
#[inline]
|
|
pub fn get<'a, P: ?Sized+AsRef<str>>(&'a self, prefix: &P) -> Option<&'a str> {
|
|
let prefix = prefix.as_ref();
|
|
for ns in self.0.iter().rev() {
|
|
match ns.get(prefix) {
|
|
None => {},
|
|
r => return r,
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
/// Combines this stack of namespaces into a single namespace.
|
|
///
|
|
/// Namespaces are combined in left-to-right order, that is, rightmost namespace
|
|
/// elements take priority over leftmost ones.
|
|
pub fn squash(&self) -> Namespace {
|
|
let mut result = BTreeMap::new();
|
|
for ns in self.0.iter() {
|
|
result.extend(ns.0.iter().map(|(k, v)| (k.clone(), v.clone())));
|
|
}
|
|
Namespace(result)
|
|
}
|
|
|
|
/// Returns an object which implements `Extend` using `put_checked()` instead of `put()`.
|
|
///
|
|
/// See `CheckedTarget` for more information.
|
|
#[inline]
|
|
pub fn checked_target(&mut self) -> CheckedTarget {
|
|
CheckedTarget(self)
|
|
}
|
|
|
|
/// Returns an iterator over all mappings in this namespace stack.
|
|
#[inline]
|
|
pub fn iter(&self) -> NamespaceStackMappings {
|
|
self.into_iter()
|
|
}
|
|
}
|
|
|
|
/// An iterator over mappings from prefixes to URIs in a namespace stack.
|
|
///
|
|
/// # Example
|
|
/// ```
|
|
/// # use xml::namespace::NamespaceStack;
|
|
/// let mut nst = NamespaceStack::empty();
|
|
/// nst.push_empty();
|
|
/// nst.put("a", "urn:A");
|
|
/// nst.put("b", "urn:B");
|
|
/// nst.push_empty();
|
|
/// nst.put("c", "urn:C");
|
|
///
|
|
/// assert_eq!(vec![("c", "urn:C"), ("a", "urn:A"), ("b", "urn:B")], nst.iter().collect::<Vec<_>>());
|
|
/// ```
|
|
pub struct NamespaceStackMappings<'a> {
|
|
namespaces: Rev<Iter<'a, Namespace>>,
|
|
current_namespace: Option<NamespaceMappings<'a>>,
|
|
used_keys: HashSet<&'a str>
|
|
}
|
|
|
|
impl<'a> NamespaceStackMappings<'a> {
|
|
fn go_to_next_namespace(&mut self) -> bool {
|
|
self.current_namespace = self.namespaces.next().map(|ns| ns.into_iter());
|
|
self.current_namespace.is_some()
|
|
}
|
|
}
|
|
|
|
impl<'a> Iterator for NamespaceStackMappings<'a> {
|
|
type Item = UriMapping<'a>;
|
|
|
|
fn next(&mut self) -> Option<UriMapping<'a>> {
|
|
// If there is no current namespace and no next namespace, we're finished
|
|
if self.current_namespace.is_none() && !self.go_to_next_namespace() {
|
|
return None;
|
|
}
|
|
let next_item = self.current_namespace.as_mut().unwrap().next();
|
|
|
|
match next_item {
|
|
// There is an element in the current namespace
|
|
Some((k, v)) => if self.used_keys.contains(&k) {
|
|
// If the current key is used, go to the next one
|
|
self.next()
|
|
} else {
|
|
// Otherwise insert the current key to the set of used keys and
|
|
// return the mapping
|
|
self.used_keys.insert(k);
|
|
Some((k, v))
|
|
},
|
|
// Current namespace is exhausted
|
|
None => if self.go_to_next_namespace() {
|
|
// If there is next namespace, continue from it
|
|
self.next()
|
|
} else {
|
|
// No next namespace, exiting
|
|
None
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> IntoIterator for &'a NamespaceStack {
|
|
type Item = UriMapping<'a>;
|
|
type IntoIter = NamespaceStackMappings<'a>;
|
|
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
NamespaceStackMappings {
|
|
namespaces: self.0.iter().rev(),
|
|
current_namespace: None,
|
|
used_keys: HashSet::new()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A type alias for a pair of `(prefix, uri)` values returned by namespace iterators.
|
|
pub type UriMapping<'a> = (&'a str, &'a str);
|
|
|
|
impl<'a> Extend<UriMapping<'a>> for Namespace {
|
|
fn extend<T>(&mut self, iterable: T) where T: IntoIterator<Item=UriMapping<'a>> {
|
|
for (prefix, uri) in iterable {
|
|
self.put(prefix, uri);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> Extend<UriMapping<'a>> for NamespaceStack {
|
|
fn extend<T>(&mut self, iterable: T) where T: IntoIterator<Item=UriMapping<'a>> {
|
|
for (prefix, uri) in iterable {
|
|
self.put(prefix, uri);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A wrapper around `NamespaceStack` which implements `Extend` using `put_checked()`.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// # use xml::namespace::NamespaceStack;
|
|
///
|
|
/// let mut nst = NamespaceStack::empty();
|
|
/// nst.push_empty();
|
|
/// nst.put("a", "urn:A");
|
|
/// nst.put("b", "urn:B");
|
|
/// nst.push_empty();
|
|
/// nst.put("c", "urn:C");
|
|
///
|
|
/// nst.checked_target().extend(vec![("a", "urn:Z"), ("b", "urn:B"), ("c", "urn:Y"), ("d", "urn:D")]);
|
|
/// assert_eq!(
|
|
/// vec![("a", "urn:Z"), ("c", "urn:C"), ("d", "urn:D"), ("b", "urn:B")],
|
|
/// nst.iter().collect::<Vec<_>>()
|
|
/// );
|
|
/// ```
|
|
///
|
|
/// Compare:
|
|
///
|
|
/// ```
|
|
/// # use xml::namespace::NamespaceStack;
|
|
/// # let mut nst = NamespaceStack::empty();
|
|
/// # nst.push_empty();
|
|
/// # nst.put("a", "urn:A");
|
|
/// # nst.put("b", "urn:B");
|
|
/// # nst.push_empty();
|
|
/// # nst.put("c", "urn:C");
|
|
///
|
|
/// nst.extend(vec![("a", "urn:Z"), ("b", "urn:B"), ("c", "urn:Y"), ("d", "urn:D")]);
|
|
/// assert_eq!(
|
|
/// vec![("a", "urn:Z"), ("b", "urn:B"), ("c", "urn:C"), ("d", "urn:D")],
|
|
/// nst.iter().collect::<Vec<_>>()
|
|
/// );
|
|
/// ```
|
|
pub struct CheckedTarget<'a>(&'a mut NamespaceStack);
|
|
|
|
impl<'a, 'b> Extend<UriMapping<'b>> for CheckedTarget<'a> {
|
|
fn extend<T>(&mut self, iterable: T) where T: IntoIterator<Item=UriMapping<'b>> {
|
|
for (prefix, uri) in iterable {
|
|
self.0.put_checked(prefix, uri);
|
|
}
|
|
}
|
|
}
|