Add sibling links to nodes.
This commit is contained in:
parent
7e45dee8cf
commit
08c0f273a6
|
@ -30,6 +30,12 @@ pub enum XMLNodeData<'gc> {
|
||||||
/// The parent node of this one.
|
/// The parent node of this one.
|
||||||
parent: Option<XMLNode<'gc>>,
|
parent: Option<XMLNode<'gc>>,
|
||||||
|
|
||||||
|
/// The previous sibling node to this one.
|
||||||
|
prev_sibling: Option<XMLNode<'gc>>,
|
||||||
|
|
||||||
|
/// The next sibling node to this one.
|
||||||
|
next_sibling: Option<XMLNode<'gc>>,
|
||||||
|
|
||||||
/// The string representation of the text.
|
/// The string representation of the text.
|
||||||
contents: String,
|
contents: String,
|
||||||
},
|
},
|
||||||
|
@ -45,6 +51,12 @@ pub enum XMLNodeData<'gc> {
|
||||||
/// The parent node of this one.
|
/// The parent node of this one.
|
||||||
parent: Option<XMLNode<'gc>>,
|
parent: Option<XMLNode<'gc>>,
|
||||||
|
|
||||||
|
/// The previous sibling node to this one.
|
||||||
|
prev_sibling: Option<XMLNode<'gc>>,
|
||||||
|
|
||||||
|
/// The next sibling node to this one.
|
||||||
|
next_sibling: Option<XMLNode<'gc>>,
|
||||||
|
|
||||||
/// The string representation of the comment.
|
/// The string representation of the comment.
|
||||||
contents: String,
|
contents: String,
|
||||||
},
|
},
|
||||||
|
@ -64,6 +76,12 @@ pub enum XMLNodeData<'gc> {
|
||||||
/// The parent node of this one.
|
/// The parent node of this one.
|
||||||
parent: Option<XMLNode<'gc>>,
|
parent: Option<XMLNode<'gc>>,
|
||||||
|
|
||||||
|
/// The previous sibling node to this one.
|
||||||
|
prev_sibling: Option<XMLNode<'gc>>,
|
||||||
|
|
||||||
|
/// The next sibling node to this one.
|
||||||
|
next_sibling: Option<XMLNode<'gc>>,
|
||||||
|
|
||||||
/// The tag name of this element.
|
/// The tag name of this element.
|
||||||
tag_name: XMLName,
|
tag_name: XMLName,
|
||||||
|
|
||||||
|
@ -100,6 +118,8 @@ impl<'gc> XMLNode<'gc> {
|
||||||
script_object: None,
|
script_object: None,
|
||||||
document,
|
document,
|
||||||
parent: None,
|
parent: None,
|
||||||
|
prev_sibling: None,
|
||||||
|
next_sibling: None,
|
||||||
contents: contents.to_string(),
|
contents: contents.to_string(),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
|
@ -117,6 +137,8 @@ impl<'gc> XMLNode<'gc> {
|
||||||
script_object: None,
|
script_object: None,
|
||||||
document,
|
document,
|
||||||
parent: None,
|
parent: None,
|
||||||
|
prev_sibling: None,
|
||||||
|
next_sibling: None,
|
||||||
tag_name: XMLName::from_str(element_name)?,
|
tag_name: XMLName::from_str(element_name)?,
|
||||||
attributes: BTreeMap::new(),
|
attributes: BTreeMap::new(),
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
|
@ -164,6 +186,8 @@ impl<'gc> XMLNode<'gc> {
|
||||||
script_object: None,
|
script_object: None,
|
||||||
document,
|
document,
|
||||||
parent: None,
|
parent: None,
|
||||||
|
prev_sibling: None,
|
||||||
|
next_sibling: None,
|
||||||
tag_name,
|
tag_name,
|
||||||
attributes,
|
attributes,
|
||||||
children,
|
children,
|
||||||
|
@ -186,6 +210,8 @@ impl<'gc> XMLNode<'gc> {
|
||||||
script_object: None,
|
script_object: None,
|
||||||
document,
|
document,
|
||||||
parent: None,
|
parent: None,
|
||||||
|
prev_sibling: None,
|
||||||
|
next_sibling: None,
|
||||||
contents: match bt.unescaped()? {
|
contents: match bt.unescaped()? {
|
||||||
Cow::Borrowed(ln) => Cow::Borrowed(std::str::from_utf8(ln)?),
|
Cow::Borrowed(ln) => Cow::Borrowed(std::str::from_utf8(ln)?),
|
||||||
Cow::Owned(ln) => Cow::Owned(String::from_utf8(ln)?),
|
Cow::Owned(ln) => Cow::Owned(String::from_utf8(ln)?),
|
||||||
|
@ -211,6 +237,8 @@ impl<'gc> XMLNode<'gc> {
|
||||||
script_object: None,
|
script_object: None,
|
||||||
document,
|
document,
|
||||||
parent: None,
|
parent: None,
|
||||||
|
prev_sibling: None,
|
||||||
|
next_sibling: None,
|
||||||
contents: match bt.unescaped()? {
|
contents: match bt.unescaped()? {
|
||||||
Cow::Borrowed(ln) => Cow::Borrowed(std::str::from_utf8(ln)?),
|
Cow::Borrowed(ln) => Cow::Borrowed(std::str::from_utf8(ln)?),
|
||||||
Cow::Owned(ln) => Cow::Owned(String::from_utf8(ln)?),
|
Cow::Owned(ln) => Cow::Owned(String::from_utf8(ln)?),
|
||||||
|
@ -234,32 +262,140 @@ impl<'gc> XMLNode<'gc> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adopt a child element into the current node.
|
/// Adopt a new child node into the current node.
|
||||||
///
|
///
|
||||||
/// This does not add the node to any internal lists; it merely updates the
|
/// This does not add the node to any internal lists; it merely updates the
|
||||||
/// child to ensure that it considers this node it's parent. This function
|
/// child to ensure that it considers this node it's parent. This function
|
||||||
/// should always be called after a child node is added to this one.
|
/// should always be called after a child node is added to this one. If
|
||||||
pub fn adopt(
|
/// you adopt a node that is NOT already added to the children list, bad
|
||||||
|
/// things may happen.
|
||||||
|
pub fn adopt_child(
|
||||||
|
&mut self,
|
||||||
|
mc: MutationContext<'gc, '_>,
|
||||||
|
mut child: XMLNode<'gc>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
{
|
||||||
|
let mut write = child.0.write(mc);
|
||||||
|
let (child_document, child_parent) = match &mut *write {
|
||||||
|
XMLNodeData::Element {
|
||||||
|
document, parent, ..
|
||||||
|
} => Ok((document, parent)),
|
||||||
|
XMLNodeData::Text {
|
||||||
|
document, parent, ..
|
||||||
|
} => Ok((document, parent)),
|
||||||
|
XMLNodeData::Comment {
|
||||||
|
document, parent, ..
|
||||||
|
} => Ok((document, parent)),
|
||||||
|
XMLNodeData::DocumentRoot { .. } => Err("Cannot adopt other document roots"),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
if let Some(parent) = child_parent {
|
||||||
|
parent.orphan_child(mc, child)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
*child_document = self.document();
|
||||||
|
*child_parent = Some(*self);
|
||||||
|
}
|
||||||
|
child.disown_siblings(mc)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the previous sibling
|
||||||
|
pub fn prev_sibling(self) -> Result<Option<XMLNode<'gc>>, Error> {
|
||||||
|
match *self.0.read() {
|
||||||
|
XMLNodeData::Element { prev_sibling, .. } => Ok(prev_sibling),
|
||||||
|
XMLNodeData::Text { prev_sibling, .. } => Ok(prev_sibling),
|
||||||
|
XMLNodeData::Comment { prev_sibling, .. } => Ok(prev_sibling),
|
||||||
|
XMLNodeData::DocumentRoot { .. } => Err("Document roots cannot have siblings".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Establish a new, previous sibling node.
|
||||||
|
fn establish_prev_sibling(
|
||||||
|
&mut self,
|
||||||
|
mc: MutationContext<'gc, '_>,
|
||||||
|
new_prev: Option<XMLNode<'gc>>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
match &mut *self.0.write(mc) {
|
||||||
|
XMLNodeData::Element { prev_sibling, .. } => *prev_sibling = new_prev,
|
||||||
|
XMLNodeData::Text { prev_sibling, .. } => *prev_sibling = new_prev,
|
||||||
|
XMLNodeData::Comment { prev_sibling, .. } => *prev_sibling = new_prev,
|
||||||
|
XMLNodeData::DocumentRoot { .. } => {
|
||||||
|
return Err("Document roots cannot have siblings".into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the next sibling
|
||||||
|
pub fn next_sibling(self) -> Result<Option<XMLNode<'gc>>, Error> {
|
||||||
|
match *self.0.read() {
|
||||||
|
XMLNodeData::Element { next_sibling, .. } => Ok(next_sibling),
|
||||||
|
XMLNodeData::Text { next_sibling, .. } => Ok(next_sibling),
|
||||||
|
XMLNodeData::Comment { next_sibling, .. } => Ok(next_sibling),
|
||||||
|
XMLNodeData::DocumentRoot { .. } => Err("Document roots cannot have siblings".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Establish a new, next sibling node.
|
||||||
|
fn establish_next_sibling(
|
||||||
|
&mut self,
|
||||||
|
mc: MutationContext<'gc, '_>,
|
||||||
|
new_next: Option<XMLNode<'gc>>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
match &mut *self.0.write(mc) {
|
||||||
|
XMLNodeData::Element { next_sibling, .. } => *next_sibling = new_next,
|
||||||
|
XMLNodeData::Text { next_sibling, .. } => *next_sibling = new_next,
|
||||||
|
XMLNodeData::Comment { next_sibling, .. } => *next_sibling = new_next,
|
||||||
|
XMLNodeData::DocumentRoot { .. } => {
|
||||||
|
return Err("Document roots cannot have siblings".into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove node from it's current siblings list.
|
||||||
|
fn disown_siblings(&mut self, mc: MutationContext<'gc, '_>) -> Result<(), Error> {
|
||||||
|
let old_prev = self.prev_sibling()?;
|
||||||
|
let old_next = self.next_sibling()?;
|
||||||
|
|
||||||
|
if let Some(mut prev) = old_prev {
|
||||||
|
prev.establish_next_sibling(mc, old_next)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mut next) = old_next {
|
||||||
|
next.establish_prev_sibling(mc, old_prev)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove node from this node's child list.
|
||||||
|
fn orphan_child(
|
||||||
&mut self,
|
&mut self,
|
||||||
mc: MutationContext<'gc, '_>,
|
mc: MutationContext<'gc, '_>,
|
||||||
child: XMLNode<'gc>,
|
child: XMLNode<'gc>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let mut write = child.0.write(mc);
|
for (i, other_child) in self
|
||||||
let (child_document, child_parent) = match &mut *write {
|
.children()
|
||||||
XMLNodeData::Element {
|
.ok_or("Cannot orphan child if I have no children")?
|
||||||
document, parent, ..
|
.enumerate()
|
||||||
} => Ok((document, parent)),
|
{
|
||||||
XMLNodeData::Text {
|
if GcCell::ptr_eq(child.0, other_child.0) {
|
||||||
document, parent, ..
|
match &mut *self.0.write(mc) {
|
||||||
} => Ok((document, parent)),
|
XMLNodeData::Element { children, .. } => children.remove(i),
|
||||||
XMLNodeData::Comment {
|
XMLNodeData::DocumentRoot { children, .. } => children.remove(i),
|
||||||
document, parent, ..
|
XMLNodeData::Text { .. } => return Err("Text node has no child nodes!".into()),
|
||||||
} => Ok((document, parent)),
|
XMLNodeData::Comment { .. } => {
|
||||||
XMLNodeData::DocumentRoot { .. } => Err("Cannot adopt other document roots"),
|
return Err("Comment node has no child nodes!".into())
|
||||||
}?;
|
}
|
||||||
|
};
|
||||||
|
|
||||||
*child_document = self.document();
|
break;
|
||||||
*child_parent = Some(*self);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -290,7 +426,7 @@ impl<'gc> XMLNode<'gc> {
|
||||||
_ => return Err("Not an Element".into()),
|
_ => return Err("Not an Element".into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.adopt(mc, child)?;
|
self.adopt_child(mc, child)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue