use std::cell::RefCell;
use ahash::{HashMap, HashMapExt};
use xot::Xot;
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub(crate) struct DocumentOrder(usize, usize);
impl DocumentOrder {
pub(crate) fn generate_id(&self) -> String {
format!("id{}s{}", self.0, self.1)
}
}
pub(crate) struct DocumentOrderAccess<'a> {
pub(crate) xot: &'a Xot,
pub(crate) annotations: &'a DocumentOrderAnnotations,
}
impl<'a> DocumentOrderAccess<'a> {
pub(crate) fn new(xot: &'a Xot, annotations: &'a DocumentOrderAnnotations) -> Self {
Self { xot, annotations }
}
pub(crate) fn get(&self, node: xot::Node) -> DocumentOrder {
self.annotations.get(node, self.xot)
}
}
#[derive(Debug, Clone)]
pub(crate) struct DocumentOrderAnnotations {
document_id: RefCell<usize>,
map: RefCell<HashMap<xot::Node, DocumentOrder>>,
}
impl DocumentOrderAnnotations {
pub(crate) fn new() -> Self {
Self {
map: RefCell::new(HashMap::new()),
document_id: RefCell::new(0),
}
}
pub(crate) fn access<'a>(&'a self, xot: &'a Xot) -> DocumentOrderAccess<'a> {
DocumentOrderAccess::new(xot, self)
}
pub(crate) fn get(&self, node: xot::Node, xot: &xot::Xot) -> DocumentOrder {
let document_order = self.map.borrow().get(&node).cloned();
if let Some(document_order) = document_order {
document_order
} else {
let (document_order, found_node) =
find_node_with_document_order(&self.map.borrow(), node, xot);
if let Some(document_order) = document_order {
annotation_with_document_order(
&mut self.map.borrow_mut(),
document_order,
found_node,
node,
xot,
)
} else {
*self.document_id.borrow_mut() += 1;
let document_order = DocumentOrder(*self.document_id.borrow(), 0);
let mut map = self.map.borrow_mut();
map.insert(found_node, document_order);
annotation_with_document_order(&mut map, document_order, found_node, node, xot)
}
}
}
}
fn find_node_with_document_order(
map: &HashMap<xot::Node, DocumentOrder>,
node: xot::Node,
xot: &xot::Xot,
) -> (Option<DocumentOrder>, xot::Node) {
let mut last_node = node;
for node in xot.all_reverse_preorder(node) {
if let Some(document_order) = map.get(&node) {
return (Some(*document_order), node);
}
last_node = node;
}
(None, last_node)
}
fn annotation_with_document_order(
map: &mut HashMap<xot::Node, DocumentOrder>,
document_order: DocumentOrder,
root_node: xot::Node,
node: xot::Node,
xot: &Xot,
) -> DocumentOrder {
if root_node == node {
return document_order;
}
let document_id = document_order.0;
let mut iter = xot
.all_descendants(root_node)
.chain(xot.all_following(root_node));
iter.next();
let start = document_order.1 + 1;
for (i, descendant) in iter.enumerate() {
let document_order = DocumentOrder(document_id, start + i);
map.insert(descendant, document_order);
if descendant == node {
return document_order;
}
}
unreachable!()
}