Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 107 additions & 4 deletions source/kxml/xml.d
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ class XmlNode
protected string _name;
protected string[string] _attributes;
protected XmlNode[] _children;
protected XmlNode _parent;



Expand All @@ -191,6 +192,37 @@ class XmlNode
_name = name;
}

/// Clone the state of an XmlNode from another
XmlNode cloneNode(XmlNode node)
{
_docroot = node._docroot;
_name = node._name;
_attributes = node._attributes.dup;
removeChildren;
_parent = null;
foreach(child; node._children)
addChild(child.duplicate);
return this;
}

/// Duplicate an XmlNode or derivatives
XmlNode duplicate()
{
if (auto ucdata = cast(UCData)this) {
return (new UCData).cloneUCData(ucdata);
} else if (auto cdata = cast(CData)this) {
return (new CData).cloneCData(cdata);
} else if (auto xmlPi = cast(XmlPI)this) {
return (new XmlPI).cloneXmlPI(xmlPi);
} else if (auto xmlComment = cast(XmlComment)this) {
return (new XmlComment).cloneXmlComment(xmlComment);
} else if (auto xmlDoc = cast(XmlDocument)this) {
return (new XmlDocument).cloneXmlDocument(xmlDoc);
} else {
return (new XmlNode).cloneNode(this);
}
}

/// Get the name of this XmlNode.
string getName() {
return _name;
Expand Down Expand Up @@ -249,8 +281,16 @@ class XmlNode
return this;
}

/// Get the parent node (or null if root).
XmlNode getParent()
{
return _parent;
}

/// Add a child node.
XmlNode addChild(XmlNode newNode) {
if (newNode._parent) throw new Exception("Child already has a parent");
newNode._parent = this;
// let's bump things by increments of 10 to make them more efficient
if (_children.length+1%10==0) {
_children.length = _children.length + 10;
Expand All @@ -268,15 +308,16 @@ class XmlNode
}

/// Remove the child with the same reference as what was given.
/// Returns: The number of children removed.
/// Returns: The number of children removed (can only be 1 or 0).
size_t removeChild(XmlNode remove) {
size_t len = _children.length;
for (size_t i = 0;i<_children.length;i++) if (_children[i] is remove) {
// we matched it, so remove it
// don't return true yet, since we're removing all references to it, not just the first one
_children = _children[0..i]~_children[i+1..$];
remove._parent = null;
return 1;
}
return len - _children.length;
return 0;
}

/// Add a child Node of cdata (text).
Expand Down Expand Up @@ -322,6 +363,7 @@ class XmlNode
_children.length = 0;
_attributes = null;
_name = null;
_parent = null;
// put back in the pool of available XmlNode nodes if possible
if (_docroot) {
_docroot.xmlNodes.length = _docroot.xmlNodes.length + 1;
Expand All @@ -331,6 +373,9 @@ class XmlNode

/// This function removes all child nodes from the current node
XmlNode removeChildren() {
foreach(child;_children) {
child._parent = null;
}
_children.length = 0;
return this;
}
Expand Down Expand Up @@ -438,6 +483,9 @@ class XmlNode

/// Add array of nodes directly into this node as children.
void addChildren(XmlNode[]newChildren) {
foreach(child; newChildren) {
child._parent = this;
}
// let's bump things by increments of 10 to make them more efficient
if (_children.length+newChildren.length%10 < newChildren.length) {
_children.length = _children.length + 10;
Expand Down Expand Up @@ -1005,6 +1053,7 @@ class XmlNode
/// Index override for replacing children.
XmlNode opIndexAssign(XmlNode x,int childnum) {
if (childnum > _children.length) throw new Exception("Child element assignment is outside of array bounds");
if (x._parent) throw new Exception("Child already has a parent");
_children[childnum] = x;
return this;
}
Expand All @@ -1022,6 +1071,15 @@ class CData : XmlNode

this(){}

/// Clone the state of a CData from another
CData cloneCData(CData cdata)
{
_docroot = cdata._docroot;
_parent = null;
_cdata = cdata._cdata;
return this;
}

/// Get CData string associated with this object.
/// Returns: Parsed Character Data with decoded XML entities
override string getCData() {
Expand All @@ -1042,6 +1100,7 @@ class CData : XmlNode
_docroot.cdataNodes[$-1] = this;
}
_cdata = null;
_parent = null;
}

/// This outputs escaped XML entities for use on the network or in a document.
Expand Down Expand Up @@ -1114,6 +1173,12 @@ class CData : XmlNode

/// A specialization of CData for <![CDATA[]]> nodes
class UCData : CData {
/// Clone the state of a UCData from another
UCData cloneUCData(UCData ucdata)
{
return cast(UCData)(cast(CData)this).cloneCData(cast(CData)ucdata);
}

/// Get CData string associated with this object.
/// Returns: Unparsed Character Data
override string getCData() {
Expand All @@ -1134,6 +1199,7 @@ class UCData : CData {
_docroot.ucdataNodes[$-1] = this;
}
_cdata = null;
_parent = null;
}

/// This outputs escaped XML entities for use on the network or in a document.
Expand All @@ -1151,6 +1217,16 @@ class XmlPI : XmlNode {
super(name);
}

/// Clone the state of an XmlPI from another
XmlPI cloneXmlPI(XmlPI xmlPi)
{
_name = xmlPi._name;
_attributes = xmlPi._attributes.dup;
_parent = null;
_docroot = xmlPi._docroot;
return this;
}

/// This node can't have children, and so can't have CData.
/// Should this throw an exception?
override string getCData() {
Expand All @@ -1164,9 +1240,10 @@ class XmlPI : XmlNode {

/// This function resets the node to a default state
override void reset() {
// put back in the pool of available CData nodes if possible
// put back in the pool of available xmlPINodes if possible
_name = null;
_attributes = null;
_parent = null;
if (_docroot) {
_docroot.xmlPINodes.length = _docroot.xmlPINodes.length + 1;
_docroot.xmlPINodes[$-1] = this;
Expand Down Expand Up @@ -1222,10 +1299,20 @@ class XmlComment : XmlNode {
return null;
}

/// Clone the state of an XmlComment from another
XmlComment cloneXmlComment(XmlComment comment)
{
_comment = comment._comment;
_parent = null;
_docroot = comment._docroot;
return this;
}

/// This function resets the node to a default state
override void reset() {
// put back in the pool of available XmlComment nodes if possible
_comment = null;
_parent = null;
if (_docroot) {
_docroot.xmlCommentNodes.length = _docroot.xmlCommentNodes.length + 1;
_docroot.xmlCommentNodes[$-1] = this;
Expand Down Expand Up @@ -1351,6 +1438,14 @@ class XmlDocument:XmlNode {
super();
}

/// Clone the state of an XmlDocument from another
XmlDocument cloneXmlDocument(XmlDocument document)
{
reset;
foreach(child; document._children)
addChild(child.duplicate);
return this;
}

/// This static opCall should be used when creating new XmlDocuments for use
static XmlDocument opCall(string constring,bool preserveWS = false) {
Expand Down Expand Up @@ -1630,6 +1725,14 @@ unittest {
searchlist = xml.parseXPath(`//td[.="Text 2.3"]`);
assert(searchlist.length == 1);

xmlstring = `<?xml version="1.0" encoding="UTF-8"?><root><empty/><elem val="42">Text <child prop="test">with child </child>` ~
` and more text.</elem><cdata><![CDATA[Some <CDATA> content.]]></cdata><!-- Comment within XML --></root>`;

logline("kxml.xml XmlNode.duplicate test\n");
xml = readDocument(xmlstring, true);
assert(xml.toString == xmlstring);
auto dupXml = xml.duplicate;
assert(dupXml.toString == xmlstring);
}

version(XML_main) {
Expand Down