use crate::common::{Error, Result};
use crate::odf::elements::namespace::{QualifiedName, NamespaceContext};
use quick_xml::events::Event;
use std::collections::HashMap;
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct PropDef {
pub name: String,
pub attr: String,
pub family: String,
}
#[allow(dead_code)]
impl PropDef {
pub fn new(name: &str, attr: &str) -> Self {
Self {
name: name.to_string(),
attr: attr.to_string(),
family: String::new(),
}
}
pub fn with_family(name: &str, attr: &str, family: &str) -> Self {
Self {
name: name.to_string(),
attr: attr.to_string(),
family: family.to_string(),
}
}
}
#[allow(dead_code)]
pub trait ElementBase {
fn tag_name(&self) -> &str;
fn attributes(&self) -> &HashMap<String, String>;
fn attributes_mut(&mut self) -> &mut HashMap<String, String>;
fn text(&self) -> &str;
fn set_text(&mut self, text: &str);
fn children(&self) -> &[Box<dyn ElementBase>];
fn children_mut(&mut self) -> &mut Vec<Box<dyn ElementBase>>;
fn get_attribute(&self, name: &str) -> Option<&str> {
self.attributes().get(name).map(|s| s.as_str())
}
fn set_attribute(&mut self, name: &str, value: &str) {
self.attributes_mut().insert(name.to_string(), value.to_string());
}
fn remove_attribute(&mut self, name: &str) {
self.attributes_mut().remove(name);
}
fn has_attribute(&self, name: &str) -> bool {
self.attributes().contains_key(name)
}
fn get_bool_attribute(&self, name: &str) -> Option<bool> {
self.get_attribute(name)
.and_then(|s| match s {
"true" | "1" => Some(true),
"false" | "0" => Some(false),
_ => None,
})
}
fn get_numeric_attribute(&self, name: &str) -> Option<f64> {
self.get_attribute(name).and_then(|s| s.parse().ok())
}
fn get_int_attribute(&self, name: &str) -> Option<i64> {
self.get_attribute(name).and_then(|s| s.parse().ok())
}
fn get_elements_by_tag(&self, tag_name: &str) -> Vec<&dyn ElementBase> {
self.children()
.iter()
.filter(|child| child.tag_name() == tag_name)
.map(|child| child.as_ref())
.collect()
}
fn get_element_by_tag(&self, tag_name: &str) -> Option<&dyn ElementBase> {
self.children()
.iter()
.find(|child| child.tag_name() == tag_name)
.map(|child| child.as_ref())
}
fn add_child(&mut self, child: Box<dyn ElementBase>) {
self.children_mut().push(child);
}
fn remove_child(&mut self, index: usize) -> Option<Box<dyn ElementBase>> {
if index < self.children().len() {
Some(self.children_mut().remove(index))
} else {
None
}
}
fn get_text_recursive(&self) -> String {
let mut text = self.text().to_string();
for child in self.children() {
text.push_str(&child.get_text_recursive());
}
text
}
}
#[derive(Debug, Clone)]
pub struct Element {
tag_name: String,
qualified_name: QualifiedName,
attributes: HashMap<String, String>,
namespace_context: NamespaceContext,
text_content: String,
children: Vec<Element>,
}
impl Element {
pub fn new(tag_name: &str) -> Self {
let qualified_name = QualifiedName::from_string(tag_name);
Self {
tag_name: tag_name.to_string(),
qualified_name,
attributes: HashMap::new(),
namespace_context: NamespaceContext::new(),
text_content: String::new(),
children: Vec::new(),
}
}
pub fn new_with_context(tag_name: &str, namespace_context: NamespaceContext) -> Self {
let qualified_name = namespace_context.parse_qualified_name(tag_name);
Self {
tag_name: tag_name.to_string(),
qualified_name,
attributes: HashMap::new(),
namespace_context,
text_content: String::new(),
children: Vec::new(),
}
}
pub fn qualified_name(&self) -> &QualifiedName {
&self.qualified_name
}
pub fn namespace_uri(&self) -> Option<&str> {
self.qualified_name.namespace_uri.as_deref()
}
pub fn local_name(&self) -> &str {
&self.qualified_name.local_name
}
pub fn namespace_context(&self) -> &NamespaceContext {
&self.namespace_context
}
pub fn set_namespace_context(&mut self, context: NamespaceContext) {
self.namespace_context = context;
self.qualified_name = self.namespace_context.parse_qualified_name(&self.tag_name);
}
pub fn add_namespace(&mut self, prefix: &str, uri: &str) {
self.namespace_context.add_namespace(prefix, uri);
self.qualified_name = self.namespace_context.parse_qualified_name(&self.tag_name);
}
pub fn name_matches(&self, name: &str) -> bool {
self.qualified_name.matches_str(name, Some(&self.namespace_context))
}
pub fn get_qualified_attribute(&self, name: &str) -> Option<&str> {
if let Some(value) = self.attributes.get(name) {
return Some(value);
}
let qualified_name = self.namespace_context.parse_qualified_name(name);
for (key, value) in &self.attributes {
let key_qualified = self.namespace_context.parse_qualified_name(key);
if key_qualified.matches(&qualified_name) {
return Some(value);
}
}
None
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
let mut reader = quick_xml::Reader::from_reader(bytes);
let mut buf = Vec::new();
let mut stack = Vec::new();
loop {
match reader.read_event_into(&mut buf) {
Ok(Event::Start(ref e)) => {
let tag_name = String::from_utf8(e.name().as_ref().to_vec())
.map_err(|_| Error::InvalidFormat("Invalid UTF-8 in tag name".to_string()))?;
let mut namespace_context = NamespaceContext::new();
for attr_result in e.attributes() {
let attr = attr_result.map_err(|_| Error::InvalidFormat("Invalid attribute".to_string()))?;
let key = String::from_utf8(attr.key.as_ref().to_vec())
.map_err(|_| Error::InvalidFormat("Invalid UTF-8 in attribute key".to_string()))?;
let value = String::from_utf8(attr.value.to_vec())
.map_err(|_| Error::InvalidFormat("Invalid UTF-8 in attribute value".to_string()))?;
if key == "xmlns" || key.starts_with("xmlns:") {
namespace_context.add_namespace(&key, &value);
}
}
let mut element = Element::new_with_context(&tag_name, namespace_context);
for attr_result in e.attributes() {
let attr = attr_result.map_err(|_| Error::InvalidFormat("Invalid attribute".to_string()))?;
let key = String::from_utf8(attr.key.as_ref().to_vec())
.map_err(|_| Error::InvalidFormat("Invalid UTF-8 in attribute key".to_string()))?;
let value = String::from_utf8(attr.value.to_vec())
.map_err(|_| Error::InvalidFormat("Invalid UTF-8 in attribute value".to_string()))?;
if !(key == "xmlns" || key.starts_with("xmlns:")) {
element.set_attribute(&key, &value);
}
}
stack.push(element);
}
Ok(Event::Text(ref t)) => {
if let Some(current) = stack.last_mut() {
let text = String::from_utf8(t.to_vec())
.map_err(|_| Error::InvalidFormat("Invalid UTF-8 in text content".to_string()))?;
current.text_content.push_str(&text);
}
}
Ok(Event::End(ref e)) => {
let _tag_name = String::from_utf8(e.name().as_ref().to_vec()) .map_err(|_| Error::InvalidFormat("Invalid UTF-8 in tag name".to_string()))?;
if let Some(element) = stack.pop() {
if let Some(parent) = stack.last_mut() {
parent.children.push(element);
} else {
return Ok(element);
}
}
}
Ok(Event::Eof) => break,
Err(e) => return Err(Error::InvalidFormat(format!("XML parsing error: {}", e))),
_ => {}
}
buf.clear();
}
Err(Error::InvalidFormat("No root element found".to_string()))
}
pub fn to_xml_string(&self) -> String {
let mut xml = String::new();
self.write_xml(&mut xml, 0);
xml
}
fn write_xml(&self, output: &mut String, indent: usize) {
let indent_str = " ".repeat(indent);
output.push_str(&indent_str);
output.push('<');
output.push_str(&self.tag_name);
for (key, value) in &self.attributes {
output.push(' ');
output.push_str(key);
output.push_str("=\"");
for ch in value.chars() {
match ch {
'"' => output.push_str("""),
'&' => output.push_str("&"),
'<' => output.push_str("<"),
'>' => output.push_str(">"),
_ => output.push(ch),
}
}
output.push('"');
}
if self.children.is_empty() && self.text_content.is_empty() {
output.push_str(" />");
} else {
output.push('>');
if !self.text_content.is_empty() {
for ch in self.text_content.chars() {
match ch {
'&' => output.push_str("&"),
'<' => output.push_str("<"),
'>' => output.push_str(">"),
_ => output.push(ch),
}
}
}
for child in &self.children {
output.push('\n');
child.write_xml(output, indent + 1);
}
if !self.children.is_empty() {
output.push('\n');
output.push_str(&indent_str);
}
output.push_str("</");
output.push_str(&self.tag_name);
output.push('>');
}
}
}
impl ElementBase for Element {
fn tag_name(&self) -> &str {
&self.tag_name
}
fn attributes(&self) -> &HashMap<String, String> {
&self.attributes
}
fn attributes_mut(&mut self) -> &mut HashMap<String, String> {
&mut self.attributes
}
fn text(&self) -> &str {
&self.text_content
}
fn set_text(&mut self, text: &str) {
self.text_content = text.to_string();
}
fn children(&self) -> &[Box<dyn ElementBase>] {
unsafe { std::mem::transmute(&self.children[..]) }
}
fn children_mut(&mut self) -> &mut Vec<Box<dyn ElementBase>> {
unsafe { std::mem::transmute(&mut self.children) }
}
}
#[allow(dead_code)]
pub struct ElementFactory;
#[allow(dead_code)]
impl ElementFactory {
pub fn paragraph() -> Element {
Element::new("text:p")
}
pub fn span() -> Element {
Element::new("text:span")
}
pub fn heading(level: u8) -> Element {
let mut element = Element::new("text:h");
element.set_attribute("text:outline-level", &level.to_string());
element
}
pub fn table() -> Element {
Element::new("table:table")
}
pub fn table_row() -> Element {
Element::new("table:table-row")
}
pub fn table_cell() -> Element {
Element::new("table:table-cell")
}
}