use std::{cell::RefCell, collections::HashMap, rc::Rc};
use anyxml_uri::uri::URIStr;
use crate::tree::{
AttlistDecl, Document, ElementDecl, EntityDecl, NodeType, NotationDecl, XMLTreeError,
attlist_decl::AttlistDeclSpec,
convert::NodeKind,
element_decl::ElementDeclSpec,
entity_decl::EntityDeclSpec,
node::{InternalNodeSpec, Node, NodeCore, NodeSpec},
notation_decl::NotationDeclSpec,
};
pub struct DocumentTypeSpec {
first_child: Option<Rc<RefCell<NodeCore<dyn NodeSpec>>>>,
last_child: Option<Rc<RefCell<NodeCore<dyn NodeSpec>>>>,
element_decl: HashMap<Rc<str>, Rc<RefCell<NodeCore<ElementDeclSpec>>>>,
attlist_decl: HashMap<Rc<str>, HashMap<Rc<str>, Rc<RefCell<NodeCore<AttlistDeclSpec>>>>>,
entity_decl: HashMap<Rc<str>, Rc<RefCell<NodeCore<EntityDeclSpec>>>>,
notation_decl: HashMap<Rc<str>, Rc<RefCell<NodeCore<NotationDeclSpec>>>>,
name: Rc<str>,
system_id: Option<Rc<URIStr>>,
public_id: Option<Rc<str>>,
}
impl NodeSpec for DocumentTypeSpec {
fn node_type(&self) -> NodeType {
NodeType::DocumentType
}
fn first_child(&self) -> Option<Rc<RefCell<NodeCore<dyn NodeSpec>>>> {
self.first_child.clone()
}
fn last_child(&self) -> Option<Rc<RefCell<NodeCore<dyn NodeSpec>>>> {
self.last_child.clone()
}
}
impl InternalNodeSpec for DocumentTypeSpec {
fn set_first_child(&mut self, new: Rc<RefCell<NodeCore<dyn NodeSpec>>>) {
self.first_child = Some(new);
}
fn unset_first_child(&mut self) {
self.first_child = None;
}
fn set_last_child(&mut self, new: Rc<RefCell<NodeCore<dyn NodeSpec>>>) {
self.last_child = Some(new);
}
fn unset_last_child(&mut self) {
self.last_child = None;
}
fn pre_child_removal(&mut self, removed_child: Node<dyn NodeSpec>) -> Result<(), XMLTreeError> {
match removed_child.downcast() {
NodeKind::ElementDecl(elementdecl) => {
self.element_decl.remove(&*elementdecl.name());
}
NodeKind::AttlistDecl(attlistdecl) => {
let registered = self
.attlist_decl
.get_mut(&*attlistdecl.elem_name())
.unwrap()
.get_mut(&*attlistdecl.attr_name())
.unwrap();
if Rc::ptr_eq(registered, &attlistdecl.core) {
let mut next = attlistdecl.next_sibling();
while let Some(now) = next {
if let Some(att) = now.as_attlist_decl()
&& *attlistdecl.elem_name() == *att.elem_name()
&& *attlistdecl.attr_name() == *att.attr_name()
{
*registered = att.core;
return Ok(());
}
next = now.next_sibling();
}
let map = self
.attlist_decl
.get_mut(&*attlistdecl.elem_name())
.unwrap();
map.remove(&*attlistdecl.attr_name());
if map.is_empty() {
self.attlist_decl.remove(&*attlistdecl.elem_name());
}
}
}
NodeKind::EntityDecl(entity) => {
let registered = self.entity_decl.get_mut(&*entity.name()).unwrap();
if Rc::ptr_eq(registered, &entity.core) {
let mut next = entity.next_sibling();
while let Some(now) = next {
if let Some(ent) = now.as_entity_decl()
&& *entity.name() == *ent.name()
{
*registered = ent.core;
return Ok(());
}
next = now.next_sibling();
}
self.entity_decl.remove(&*entity.name());
}
}
NodeKind::NotationDecl(notation) => {
self.notation_decl.remove(&*notation.name());
}
_ => {}
}
Ok(())
}
fn pre_child_insertion(
&self,
inserted_child: Node<dyn NodeSpec>,
_preceding_node: Option<Node<dyn NodeSpec>>,
) -> Result<(), XMLTreeError> {
match inserted_child.downcast() {
NodeKind::ElementDecl(element) => {
if self.element_decl.contains_key(&element.name()) {
return Err(XMLTreeError::DuplicateElementDecl);
}
}
NodeKind::NotationDecl(notation) => {
if self.notation_decl.contains_key(¬ation.name()) {
return Err(XMLTreeError::DuplicateNotationDecl);
}
}
NodeKind::AttlistDecl(_)
| NodeKind::EntityDecl(_)
| NodeKind::Comment(_)
| NodeKind::ProcessingInstruction(_) => {}
NodeKind::EntityReference(_) => {
return Err(XMLTreeError::Unsupported);
}
_ => return Err(XMLTreeError::UnacceptableHierarchy),
}
Ok(())
}
fn post_child_insertion(&mut self, inserted_child: Node<dyn NodeSpec>) {
use std::collections::hash_map::Entry::*;
match inserted_child.downcast() {
NodeKind::AttlistDecl(attlist) => match self.attlist_decl.entry(attlist.elem_name()) {
Vacant(entry) => {
entry.insert(From::from([(attlist.attr_name(), attlist.core.clone())]));
}
Occupied(mut entry) => match entry.get_mut().entry((*attlist.attr_name()).into()) {
Vacant(entry) => {
entry.insert(attlist.core.clone());
}
Occupied(mut entry) => {
let core = entry.get();
let mut preceding_node = attlist.previous_sibling();
while let Some(now) = preceding_node {
preceding_node = now.previous_sibling();
if let Some(now) = now.as_attlist_decl()
&& Rc::ptr_eq(&now.core, core)
{
return;
}
}
entry.insert(attlist.core.clone());
}
},
},
NodeKind::ElementDecl(element) => {
self.element_decl.insert(element.name(), element.core);
}
NodeKind::EntityDecl(entity) => match self.entity_decl.entry(entity.name()) {
Vacant(entry) => {
entry.insert(entity.core.clone());
}
Occupied(mut entry) => {
let core = entry.get();
let mut preceding_node = entity.previous_sibling();
while let Some(now) = preceding_node {
preceding_node = now.previous_sibling();
if let Some(now) = now.as_entity_decl()
&& Rc::ptr_eq(&now.core, core)
{
return;
}
}
entry.insert(entity.core.clone());
}
},
NodeKind::NotationDecl(notation) => {
self.notation_decl.insert(notation.name(), notation.core);
}
_ => {}
}
}
}
pub type DocumentType = Node<DocumentTypeSpec>;
impl DocumentType {
pub(crate) fn new(
name: Rc<str>,
system_id: Option<Rc<URIStr>>,
public_id: Option<Rc<str>>,
owner_document: Document,
) -> Self {
Node::create_node(
DocumentTypeSpec {
first_child: None,
last_child: None,
element_decl: Default::default(),
attlist_decl: Default::default(),
entity_decl: Default::default(),
notation_decl: Default::default(),
name,
system_id,
public_id,
},
owner_document,
)
}
pub fn get_element_decl(&self, name: &str) -> Option<ElementDecl> {
self.core
.borrow()
.spec
.element_decl
.get(name)
.map(|core| ElementDecl {
core: core.clone(),
owner_document: self.owner_document.clone(),
})
}
pub fn get_attlist_decl(&self, elem_name: &str, attr_name: &str) -> Option<AttlistDecl> {
self.core
.borrow()
.spec
.attlist_decl
.get(elem_name)?
.get(attr_name)
.map(|core| AttlistDecl {
core: core.clone(),
owner_document: self.owner_document.clone(),
})
}
pub fn get_entity_decl(&self, name: &str) -> Option<EntityDecl> {
self.core
.borrow()
.spec
.entity_decl
.get(name)
.map(|core| EntityDecl {
core: core.clone(),
owner_document: self.owner_document.clone(),
})
}
pub fn get_notation_decl(&self, name: &str) -> Option<NotationDecl> {
self.core
.borrow()
.spec
.notation_decl
.get(name)
.map(|core| NotationDecl {
core: core.clone(),
owner_document: self.owner_document.clone(),
})
}
pub fn name(&self) -> Rc<str> {
self.core.borrow().spec.name.clone()
}
pub fn system_id(&self) -> Option<Rc<URIStr>> {
self.core.borrow().spec.system_id.clone()
}
pub fn public_id(&self) -> Option<Rc<str>> {
self.core.borrow().spec.public_id.clone()
}
}