mod attribute;
mod element;
mod enumeration;
mod notation;
use std::{
any::type_name,
borrow::Cow,
collections::HashMap,
ops::{Deref, DerefMut},
os::raw::c_void,
ptr::{NonNull, null_mut},
};
use crate::{
globals::{get_deregister_node_func, get_register_node_func},
parser::split_qname2,
};
use super::{
InvalidNodePointerCastError, NodeCommon, XmlDocPtr, XmlElementType, XmlEntityPtr,
XmlGenericNodePtr, xml_free_entity, xml_free_node, xml_tree_err_memory,
};
pub use attribute::*;
pub use element::*;
pub use enumeration::*;
pub use notation::*;
#[repr(C)]
pub struct XmlDtd {
pub _private: *mut c_void,
pub(crate) typ: XmlElementType,
pub(crate) name: Option<String>,
pub(crate) children: Option<XmlGenericNodePtr>,
pub(crate) last: Option<XmlGenericNodePtr>,
pub(crate) parent: Option<XmlDocPtr>,
pub(crate) next: Option<XmlGenericNodePtr>,
pub(crate) prev: Option<XmlGenericNodePtr>,
pub(crate) doc: Option<XmlDocPtr>,
pub(crate) notations: HashMap<String, XmlNotation>,
pub(crate) elements: HashMap<(Cow<'static, str>, Option<Cow<'static, str>>), XmlElementPtr>,
#[allow(clippy::type_complexity)]
pub(crate) attributes: HashMap<
(
Cow<'static, str>,
Option<Cow<'static, str>>,
Option<Cow<'static, str>>,
),
XmlAttributePtr,
>,
pub(crate) entities: HashMap<String, XmlEntityPtr>,
pub(crate) external_id: Option<String>,
pub(crate) system_id: Option<String>,
pub(crate) pentities: HashMap<String, XmlEntityPtr>,
}
impl XmlDtd {
#[doc(alias = "xmlGetEntityFromDtd")]
#[cfg(feature = "libxml_tree")]
pub(super) fn get_entity(&self, name: &str) -> Option<XmlEntityPtr> {
self.entities.get(name).copied()
}
#[doc(alias = "xmlGetParameterEntityFromDtd")]
#[cfg(feature = "libxml_tree")]
pub(super) fn get_parameter_entity(&self, name: &str) -> Option<XmlEntityPtr> {
self.pentities.get(name).copied()
}
#[doc(alias = "xmlGetDtdAttrDesc")]
pub fn get_attr_desc(&self, elem: &str, name: &str) -> Option<XmlAttributePtr> {
if let Some((prefix, local)) = split_qname2(name) {
self.attributes
.get(&(
Cow::Borrowed(local),
Some(Cow::Borrowed(prefix)),
Some(Cow::Borrowed(elem)),
))
.copied()
} else {
self.attributes
.get(&(Cow::Borrowed(name), None, Some(Cow::Borrowed(elem))))
.copied()
}
}
#[doc(alias = "xmlGetDtdQAttrDesc")]
pub fn get_qattr_desc(
&self,
elem: &str,
name: &str,
prefix: Option<&str>,
) -> Option<XmlAttributePtr> {
self.attributes
.get(&(
Cow::Borrowed(name),
prefix.map(Cow::Borrowed),
Some(Cow::Borrowed(elem)),
))
.copied()
}
#[doc(alias = "xmlSetTreeDoc")]
pub fn set_doc(&mut self, doc: Option<XmlDocPtr>) {
if self.doc != doc {
if let Some(children) = self.children() {
children.set_doc_all_sibling(doc);
}
self.doc = doc;
}
}
}
impl Default for XmlDtd {
fn default() -> Self {
Self {
_private: null_mut(),
typ: XmlElementType::default(),
name: None,
children: None,
last: None,
parent: None,
next: None,
prev: None,
doc: None,
notations: HashMap::new(),
elements: HashMap::new(),
attributes: HashMap::new(),
entities: HashMap::new(),
external_id: None,
system_id: None,
pentities: HashMap::new(),
}
}
}
impl NodeCommon for XmlDtd {
fn document(&self) -> Option<XmlDocPtr> {
self.doc
}
fn set_document(&mut self, doc: Option<XmlDocPtr>) {
self.doc = doc;
}
fn element_type(&self) -> XmlElementType {
self.typ
}
fn name(&self) -> Option<Cow<'_, str>> {
self.name.as_deref().map(Cow::Borrowed)
}
fn children(&self) -> Option<XmlGenericNodePtr> {
self.children
}
fn set_children(&mut self, children: Option<XmlGenericNodePtr>) {
self.children = children
}
fn last(&self) -> Option<XmlGenericNodePtr> {
self.last
}
fn set_last(&mut self, last: Option<XmlGenericNodePtr>) {
self.last = last
}
fn next(&self) -> Option<XmlGenericNodePtr> {
self.next
}
fn set_next(&mut self, next: Option<XmlGenericNodePtr>) {
self.next = next
}
fn prev(&self) -> Option<XmlGenericNodePtr> {
self.prev
}
fn set_prev(&mut self, prev: Option<XmlGenericNodePtr>) {
self.prev = prev
}
fn parent(&self) -> Option<XmlGenericNodePtr> {
self.parent.map(|node| node.into())
}
fn set_parent(&mut self, parent: Option<XmlGenericNodePtr>) {
self.parent = parent.map(|ptr| XmlDocPtr::try_from(ptr).unwrap());
}
fn unlink(&mut self) {
let dtd = unsafe {
XmlDtdPtr::from_raw(self).unwrap()
};
if let Some(mut doc) = self.document() {
if doc.int_subset == dtd {
doc.int_subset = None;
}
if doc.ext_subset == dtd {
doc.ext_subset = None;
}
}
if let Some(mut parent) = self.parent() {
if parent.children() == XmlGenericNodePtr::from_raw(self) {
parent.set_children(self.next());
}
if parent.last() == XmlGenericNodePtr::from_raw(self) {
parent.set_last(self.prev());
}
self.set_parent(None);
}
if let Some(mut next) = self.next() {
next.set_prev(self.prev());
}
if let Some(mut prev) = self.prev() {
prev.set_next(self.next());
}
self.set_next(None);
self.set_prev(None);
}
}
#[doc(alias = "xmlCreateIntSubset")]
pub fn xml_create_int_subset(
doc: Option<XmlDocPtr>,
name: Option<&str>,
external_id: Option<&str>,
system_id: Option<&str>,
) -> Option<XmlDtdPtr> {
if doc.is_some_and(|doc| doc.get_int_subset().is_some()) {
return None;
}
let Some(mut cur) = XmlDtdPtr::new(XmlDtd {
typ: XmlElementType::XmlDTDNode,
external_id: external_id.map(|e| e.to_owned()),
system_id: system_id.map(|e| e.to_owned()),
..Default::default()
}) else {
xml_tree_err_memory("building internal subset");
return None;
};
cur.name = name.map(|name| name.to_owned());
if let Some(mut doc) = doc {
doc.int_subset = Some(cur);
cur.parent = Some(doc);
cur.doc = Some(doc);
if let Some(children) = doc.children() {
if matches!(doc.typ, XmlElementType::XmlHTMLDocumentNode) {
let mut prev = children;
prev.set_prev(Some(cur.into()));
cur.set_next(Some(prev));
doc.set_children(Some(cur.into()));
} else {
let mut next = Some(children);
while let Some(now) =
next.filter(|n| !matches!(n.element_type(), XmlElementType::XmlElementNode))
{
next = now.next();
}
if let Some(mut next) = next {
cur.set_next(Some(next));
cur.set_prev(next.prev());
if let Some(mut prev) = cur.prev() {
prev.set_next(Some(cur.into()));
} else {
doc.set_children(Some(cur.into()));
}
next.set_prev(Some(cur.into()));
} else {
cur.set_prev(doc.last());
cur.prev().unwrap().set_next(Some(cur.into()));
cur.set_next(None);
doc.set_last(Some(cur.into()));
}
}
} else {
doc.children = Some(cur.into());
doc.last = Some(cur.into());
}
}
if let Some(register) = get_register_node_func() {
register(cur.into());
}
Some(cur)
}
#[doc(alias = "xmlNewDtd")]
pub fn xml_new_dtd(
doc: Option<XmlDocPtr>,
name: Option<&str>,
external_id: Option<&str>,
system_id: Option<&str>,
) -> Option<XmlDtdPtr> {
if doc.is_some_and(|doc| doc.ext_subset.is_some()) {
return None;
}
let Some(mut cur) = XmlDtdPtr::new(XmlDtd {
typ: XmlElementType::XmlDTDNode,
external_id: external_id.map(|e| e.to_owned()),
system_id: system_id.map(|s| s.to_owned()),
doc,
..Default::default()
}) else {
xml_tree_err_memory("building DTD");
return None;
};
cur.name = name.map(|name| name.to_owned());
if let Some(mut doc) = doc {
doc.ext_subset = Some(cur);
}
if let Some(register) = get_register_node_func() {
register(cur.into());
}
Some(cur)
}
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct XmlDtdPtr(NonNull<XmlDtd>);
impl XmlDtdPtr {
pub(crate) fn new(node: XmlDtd) -> Option<Self> {
let boxed = Box::new(node);
NonNull::new(Box::leak(boxed)).map(Self)
}
pub(crate) unsafe fn from_raw(
ptr: *mut XmlDtd,
) -> Result<Option<Self>, InvalidNodePointerCastError> {
unsafe {
if ptr.is_null() {
return Ok(None);
}
match (*ptr).element_type() {
XmlElementType::XmlDTDNode | XmlElementType::XmlDocumentTypeNode => {
Ok(Some(Self(NonNull::new_unchecked(ptr))))
}
_ => Err(InvalidNodePointerCastError {
from: (*ptr).element_type(),
to: type_name::<Self>(),
}),
}
}
}
pub(crate) unsafe fn free(self) {
unsafe {
let _ = *Box::from_raw(self.0.as_ptr());
}
}
}
impl Clone for XmlDtdPtr {
fn clone(&self) -> Self {
*self
}
}
impl Copy for XmlDtdPtr {}
impl Deref for XmlDtdPtr {
type Target = XmlDtd;
fn deref(&self) -> &Self::Target {
unsafe { self.0.as_ref() }
}
}
impl DerefMut for XmlDtdPtr {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.0.as_mut() }
}
}
impl TryFrom<XmlGenericNodePtr> for XmlDtdPtr {
type Error = InvalidNodePointerCastError;
fn try_from(value: XmlGenericNodePtr) -> Result<Self, Self::Error> {
match value.element_type() {
XmlElementType::XmlDTDNode | XmlElementType::XmlDocumentTypeNode => {
Ok(Self(value.0.cast()))
}
_ => Err(InvalidNodePointerCastError {
from: value.element_type(),
to: type_name::<Self>(),
}),
}
}
}
impl From<XmlDtdPtr> for XmlGenericNodePtr {
fn from(value: XmlDtdPtr) -> Self {
Self(value.0 as NonNull<dyn NodeCommon>)
}
}
impl From<XmlDtdPtr> for *mut XmlDtd {
fn from(value: XmlDtdPtr) -> Self {
value.0.as_ptr()
}
}
#[doc(alias = "xmlFreeDtd")]
pub unsafe fn xml_free_dtd(mut cur: XmlDtdPtr) {
unsafe {
if let Some(deregister) = get_deregister_node_func() {
deregister(cur.into());
}
if let Some(children) = cur.children() {
let mut c = Some(children);
while let Some(mut now) = c {
let next = now.next();
if !matches!(
now.element_type(),
XmlElementType::XmlNotationNode
| XmlElementType::XmlElementDecl
| XmlElementType::XmlAttributeDecl
| XmlElementType::XmlEntityDecl
) {
now.unlink();
xml_free_node(now);
}
c = next;
}
}
cur.system_id = None;
cur.external_id = None;
for (_, element) in cur.elements.drain() {
xml_free_element(Some(element));
}
for (_, attr) in cur.attributes.drain() {
xml_free_attribute(attr);
}
for (_, entity) in cur.entities.drain() {
xml_free_entity(entity);
}
for (_, entity) in cur.pentities.drain() {
xml_free_entity(entity);
}
cur.free();
}
}