use super::{
AttributeId,
Document,
ElementId,
Node,
NodeEdge,
NodeType,
TagName,
Traverse,
WriteBuffer,
WriteOptions,
};
struct Depth {
value: u32,
block: Vec<u8>,
}
impl Depth {
fn new(indent: i8) -> Depth {
Depth {
value: 0,
block: Depth::gen_indent(indent),
}
}
fn gen_indent(len: i8) -> Vec<u8> {
match len {
-1...0 => Vec::new(),
_ => {
let mut v = Vec::with_capacity(len as usize);
for _ in 0..len {
v.push(b' ');
}
v
}
}
}
fn write_indent(&self, s: &mut Vec<u8>) {
for _ in 0..self.value {
s.extend_from_slice(&self.block);
}
}
fn write_indent_with_step(&self, step: i8, s: &mut Vec<u8>) {
let v = (self.value as i32 + step as i32) as u32;
for _ in 0..v {
s.extend_from_slice(&self.block);
}
}
}
pub fn write_dom(doc: &Document, opt: &WriteOptions, out: &mut Vec<u8>) {
let mut depth = Depth::new(opt.indent);
let mut iter = doc.root().traverse();
while let Some(edge) = iter.next() {
match edge {
NodeEdge::Start(node) => {
match node.node_type() {
NodeType::Root => continue,
NodeType::Element => {
depth.write_indent(out);
if node.is_tag_id(ElementId::Text) && node.has_children_nodes() {
write_element_start(&node, opt, out);
process_text(&mut iter, opt, &node, &depth, out);
write_newline(opt.indent, out);
continue;
}
write_element_start(&node, opt, out);
if node.has_children_nodes() {
depth.value += 1;
write_newline(opt.indent, out);
}
},
NodeType::Declaration => {
depth.write_indent(out);
out.extend_from_slice(b"<?xml ");
out.extend_from_slice(node.text().unwrap().as_bytes());
out.extend_from_slice(b"?>");
write_newline(opt.indent, out);
},
NodeType::Comment => {
depth.write_indent(out);
out.extend_from_slice(b"<!--");
out.extend_from_slice(node.text().unwrap().as_bytes());
out.extend_from_slice(b"-->");
write_newline(opt.indent, out);
},
NodeType::Cdata => {
depth.write_indent_with_step(-1, out);
out.extend_from_slice(b"<![CDATA[");
out.extend_from_slice(node.text().unwrap().as_bytes());
out.extend_from_slice(b"]]>");
write_newline(opt.indent, out);
},
NodeType::Text => {
depth.write_indent(out);
out.extend_from_slice(node.text().unwrap().trim().as_bytes());
write_newline(opt.indent, out);
},
}
},
NodeEdge::End(node) => {
match node.node_type() {
NodeType::Root => continue,
NodeType::Element => {
if node.has_children_nodes() {
if depth.value > 0 {
depth.value -= 1;
}
depth.write_indent(out);
}
write_element_end(&node, out);
write_newline(opt.indent, out);
},
_ => {},
}
},
}
}
}
fn write_quote(opt: &WriteOptions, out: &mut Vec<u8>) {
if opt.use_single_quote {
out.push(b'\'');
} else {
out.push(b'"');
}
}
fn write_attribute_name(name: &[u8], opt: &WriteOptions, out: &mut Vec<u8>) {
out.extend_from_slice(name);
out.push(b'=');
write_quote(opt, out);
}
fn write_attribute(name: &[u8], value: &[u8], opt: &WriteOptions, out: &mut Vec<u8>) {
write_attribute_name(name, opt, out);
out.extend_from_slice(value);
write_quote(opt, out);
}
fn write_tag_name(tag_name: &TagName, out: &mut Vec<u8>) {
match *tag_name {
TagName::Id(ref id) => {
out.extend_from_slice(id.name().as_bytes());
}
TagName::Name(ref name) => {
let n = name.clone();
out.extend_from_slice(n.as_bytes());
}
}
}
fn write_newline(indent: i8, out: &mut Vec<u8>) {
if indent >= 0 {
out.push(b'\n');
}
}
fn write_attributes(node: &Node, opt: &WriteOptions, out: &mut Vec<u8>) {
let id = node.id();
if !id.is_empty() {
let idvec = &id[..];
out.push(b' ');
write_attribute(b"id", idvec.as_bytes(), opt, out);
}
let attrs = node.attributes();
let mut ids: Vec<AttributeId> = attrs.iter().map(|a| a.id).collect();
ids.sort_by_key(|x| *x as usize);
for aid in ids {
let attr = attrs.get(aid).unwrap();
if !opt.write_hidden_attributes && !attr.visible {
continue;
}
out.push(b' ');
attr.write_buf_opt(opt, out);
}
let ext_hash = node.unknown_attributes();
for (name, value) in ext_hash.iter() {
out.push(b' ');
write_attribute(name.as_bytes(), value.as_bytes(), opt, out);
}
}
fn write_element_start(node: &Node, opt: &WriteOptions, out: &mut Vec<u8>) {
out.push(b'<');
write_tag_name(&node.tag_name().unwrap(), out);
write_attributes(node, opt, out);
if node.has_children_nodes() {
out.push(b'>');
}
}
fn write_element_end(node: &Node, out: &mut Vec<u8>) {
if node.has_children_nodes() {
out.extend_from_slice(b"</");
write_tag_name(&node.tag_name().unwrap(), out);
out.push(b'>');
} else {
out.extend_from_slice(b"/>");
}
}
fn process_text(iter: &mut Traverse, opt: &WriteOptions, root: &Node, depth: &Depth,
out: &mut Vec<u8>) {
let is_root_preserve = root.has_attribute_with_value(AttributeId::XmlSpace, "preserve");
if !is_root_preserve {
write_newline(opt.indent, out);
depth.write_indent_with_step(1, out);
}
let is_simple_text = root.descendant_nodes().count() == 2;
let mut is_first_text = true;
for edge in iter {
match edge {
NodeEdge::Start(node) => {
match node.node_type() {
NodeType::Element => {
write_element_start(&node, opt, out);
}
NodeType::Text => {
if is_root_preserve {
out.extend_from_slice(node.text().unwrap().as_bytes());
} else if is_simple_text {
out.extend_from_slice(node.text().unwrap().trim().as_bytes());
} else if is_first_text {
out.extend_from_slice(node.text().unwrap().trim_left().as_bytes());
} else if root.last_child().unwrap() == node {
out.extend_from_slice(node.text().unwrap().trim_right().as_bytes());
} else {
out.extend_from_slice(node.text().unwrap().as_bytes());
}
is_first_text = false;
}
_ => {}
}
}
NodeEdge::End(node) => {
if let NodeType::Element = node.node_type() {
if node == *root {
if !is_root_preserve {
write_newline(opt.indent, out);
depth.write_indent(out);
}
write_element_end(&node, out);
break;
} else {
write_element_end(&node, out);
}
}
}
}
}
}