use {escape, Xml};
use element_builder::{BuilderError, ElementBuilder};
use parser::Parser;
use std::fmt;
use std::slice;
use std::collections::HashMap;
use std::iter::IntoIterator;
use std::str::FromStr;
#[derive(Clone, PartialEq, Debug)]
pub struct Element {
pub name: String,
pub ns: Option<String>,
pub attributes: HashMap<(String, Option<String>), String>,
pub children: Vec<Xml>,
#[doc(hidden)]
pub prefixes: HashMap<String, String>,
#[doc(hidden)]
pub default_ns: Option<String>
}
fn fmt_elem(elem: &Element, parent: Option<&Element>, all_prefixes: &HashMap<String, String>,
f: &mut fmt::Formatter) -> fmt::Result {
let mut all_prefixes = all_prefixes.clone();
all_prefixes.extend(elem.prefixes.iter().map(|(k, v)| (k.clone(), v.clone()) ));
try!(if elem.ns != elem.default_ns {
let prefix = all_prefixes.get(elem.ns.as_ref().map(|x| &x[..]).unwrap_or(""))
.expect("No namespace prefix bound");
write!(f, "<{}:{}", *prefix, elem.name)
} else {
write!(f, "<{}", elem.name)
});
if !elem.attributes.iter().any(|(&(ref name, _), _)| *name == "xmlns") {
match (parent, &elem.default_ns) {
(None, &Some(ref ns)) => try!(write!(f, " xmlns='{}'", *ns)),
(Some(parent), ns) if parent.default_ns != *ns => {
try!(write!(f, " xmlns='{}'", ns.as_ref().map(|x| &x[..]).unwrap_or("")))
},
_ => ()
}
}
for (&(ref name, ref ns), value) in &elem.attributes {
try!(match *ns {
Some(ref ns) => {
let prefix = all_prefixes.get(ns).expect("No namespace prefix bound");
write!(f, " {}:{}='{}'", *prefix, name, escape(&value))
}
None => write!(f, " {}='{}'", name, escape(&value))
});
}
if elem.children.len() == 0 {
write!(f, "/>")
} else {
try!(write!(f, ">"));
for child in &elem.children {
try!(match *child {
Xml::ElementNode(ref child) => fmt_elem(child, Some(elem), &all_prefixes, f),
ref o => fmt::Display::fmt(o, f)
});
}
if elem.ns != elem.default_ns {
let prefix = all_prefixes.get(elem.ns.as_ref().unwrap())
.expect("No namespace prefix bound");
write!(f, "</{}:{}>", *prefix, elem.name)
} else {
write!(f, "</{}>", elem.name)
}
}
}
impl fmt::Display for Element {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_elem(self, None, &HashMap::new(), f)
}
}
pub struct ChildElements<'a, 'b> {
elems: slice::Iter<'a, Xml>,
name: &'b str,
ns: Option<&'b str>
}
impl<'a, 'b> Iterator for ChildElements<'a, 'b> {
type Item = &'a Element;
fn next(&mut self) -> Option<&'a Element> {
let (name, ns) = (self.name, self.ns);
self.elems.by_ref().filter_map(|child| {
if let Xml::ElementNode(ref elem) = *child {
if name == elem.name && ns == elem.ns.as_ref().map(|x| &x[..]) {
return Some(elem);
}
}
None
}).next()
}
}
impl Element {
pub fn new<A>(name: String, ns: Option<String>, attrs: A) -> Element
where A: IntoIterator<Item=(String, Option<String>, String)>
{
let mut prefixes = HashMap::with_capacity(2);
prefixes.insert("http://www.w3.org/XML/1998/namespace".to_string(), "xml".to_string());
prefixes.insert("http://www.w3.org/2000/xmlns/".to_string(), "xmlns".to_string());
let attributes: HashMap<_, _> = attrs.into_iter()
.map(|(name, ns, value)| ((name, ns), value))
.collect();
Element {
name: name,
ns: ns.clone(),
default_ns: ns,
prefixes: prefixes,
attributes: attributes,
children: Vec::new()
}
}
pub fn content_str(&self) -> String {
let mut res = String::new();
for child in &self.children {
match *child {
Xml::ElementNode(ref elem) => res.push_str(&elem.content_str()),
Xml::CharacterNode(ref data)
| Xml::CDATANode(ref data) => res.push_str(&data),
_ => ()
}
}
res
}
pub fn get_attribute<'a>(&'a self, name: &str, ns: Option<&str>) -> Option<&'a str> {
self.attributes.get(&(name.to_string(), ns.map(|x| x.to_string()))).map(|x| &x[..])
}
pub fn set_attribute(&mut self, name: String, ns: Option<String>,
value: String) -> Option<String> {
self.attributes.insert((name, ns), value)
}
pub fn remove_attribute(&mut self, name: &str, ns: Option<&str>) -> Option<String> {
self.attributes.remove(&(name.to_string(), ns.map(|x| x.to_string())))
}
pub fn get_child<'a>(&'a self, name: &str, ns: Option<&str>) -> Option<&'a Element> {
self.get_children(name, ns).next()
}
pub fn get_children<'a, 'b>(&'a self, name: &'b str,
ns: Option<&'b str>) -> ChildElements<'a, 'b> {
ChildElements {
elems: self.children.iter(),
name: name,
ns: ns
}
}
pub fn tag(&mut self, child: Element) -> &mut Element {
self.children.push(Xml::ElementNode(child));
let error = "Internal error: Could not get reference to new element!";
let elem = match self.children.last_mut().expect(error) {
&mut Xml::ElementNode(ref mut elem) => elem,
_ => panic!(error)
};
elem
}
pub fn tag_stay(&mut self, child: Element) -> &mut Element {
self.children.push(Xml::ElementNode(child));
self
}
pub fn text(&mut self, text: String) -> &mut Element {
self.children.push(Xml::CharacterNode(text));
self
}
pub fn cdata(&mut self, text: String) -> &mut Element {
self.children.push(Xml::CDATANode(text));
self
}
pub fn comment(&mut self, text: String) -> &mut Element {
self.children.push(Xml::CommentNode(text));
self
}
pub fn pi(&mut self, text: String) -> &mut Element {
self.children.push(Xml::PINode(text));
self
}
}
impl FromStr for Element {
type Err = BuilderError;
#[inline]
fn from_str(data: &str) -> Result<Element, BuilderError> {
let mut p = Parser::new();
let mut e = ElementBuilder::new();
p.feed_str(data);
for event in p.filter_map(|x| e.handle_event(x)) {
return event;
}
Err(BuilderError::NoElement)
}
}
#[cfg(test)]
mod tests {
use super::Element;
#[test]
fn test_get_children() {
let elem: Element = "<a><b/><c/><b/></a>".parse().unwrap();
assert_eq!(elem.get_children("b", None).collect::<Vec<_>>(),
vec![&Element::new("b".to_string(), None, vec![]),
&Element::new("b".to_string(), None, vec![])]);
}
#[test]
fn test_get_child() {
let elem: Element = "<a><b/><c/><b/></a>".parse().unwrap();
assert_eq!(elem.get_child("b", None),
Some(&Element::new("b".to_string(), None, vec![])));
}
}