///|
/// 文档 → 规范 XML 串:声明/DOCTYPE 在前,根元素无子时自闭合,
/// 文本与属性值转义,CDATA/注释/PI 原形发。
/// 合同:对 parse 产物往返等价(字符串定点);手搭树的非法值不在此校验。
pub fn Document::to_xml(self : Document) -> String {
  let buffer = StringBuilder()
  match self.decl {
    Some(declaration) => buffer.write_string(decl_to_string(declaration))
    None => ()
  }
  match self.doctype {
    Some(name) => {
      buffer.write_string("")
    }
    None => ()
  }
  for node in self.prologue {
    emit_node(buffer, node)
  }
  emit_element(buffer, self.root)
  for node in self.epilogue {
    emit_node(buffer, node)
  }
  buffer.to_string()
}

///|
/// 声明行(属性序 version → encoding → standalone,与底座解析形对位)。
fn decl_to_string(declaration : XmlDecl) -> String {
  let buffer = StringBuilder()
  buffer.write_string(" {
      buffer.write_string(" encoding=\"")
      buffer.write_string(encoding)
      buffer.write_string("\"")
    }
    None => ()
  }
  match declaration.standalone {
    Some(standalone) => {
      buffer.write_string(" standalone=\"")
      buffer.write_string(standalone)
      buffer.write_string("\"")
    }
    None => ()
  }
  buffer.write_string("?>")
  buffer.to_string()
}

///|
fn emit_element(buffer : StringBuilder, element : Element) -> Unit {
  buffer.write_string("<")
  buffer.write_string(element.name.qualified_name)
  for attribute in element.attributes {
    buffer.write_string(" ")
    buffer.write_string(attribute.name.qualified_name)
    buffer.write_string("=\"")
    buffer.write_string(@xml.escape(attribute.value))
    buffer.write_string("\"")
  }
  if element.children.length() == 0 {
    buffer.write_string("/>")
  } else {
    buffer.write_string(">")
    for child in element.children {
      emit_node(buffer, child)
    }
    buffer.write_string("")
  }
}

///|
fn emit_node(buffer : StringBuilder, node : Node) -> Unit {
  match node {
    Node::Element(element) => emit_element(buffer, element)
    Node::Text(content) => buffer.write_string(@xml.escape(content))
    Node::CData(content) => {
      buffer.write_string("")
    }
    Node::Comment(content) => {
      buffer.write_string("")
    }
    Node::PI(target~, data~) => {
      buffer.write_string("")
    }
  }
}