Skip to content

Commit b7d2f47

Browse files
committed
Add addClone method to XmlElement
Call `xmlElement.addClone(otherElement)` to add a clone of `otherElement` (possibly from a different XmlDocument) to the children of `xlmElement`.
1 parent 01fc960 commit b7d2f47

File tree

4 files changed

+71
-0
lines changed

4 files changed

+71
-0
lines changed

binding/exported-functions.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ _xmlCtxtSetErrorHandler
1111
_xmlCtxtValidateDtd
1212
_xmlDocGetRootElement
1313
_xmlDocSetRootElement
14+
_xmlDOMWrapCloneNode
1415
_xmlFreeDoc
1516
_xmlFreeDtd
1617
_xmlFreeNode

src/libxml2.mts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,31 @@ function allocUTF8Buffer(str: string | null) {
7979
return [buf, len];
8080
}
8181

82+
class PointerRef<T extends Pointer> {
83+
ptr: Pointer;
84+
85+
constructor(ptr: Pointer) {
86+
this.ptr = ptr;
87+
}
88+
89+
dereference(): T {
90+
return libxml2.getValue(this.ptr, '*') as T;
91+
}
92+
}
93+
94+
// TODO: find out actual pointer size
95+
const POINTER_SIZE = 8;
96+
97+
export function withPointerRef<T extends Pointer, R>(process: (pointerRef: PointerRef<T>) => R): R {
98+
const buf = libxml2._malloc(POINTER_SIZE);
99+
100+
try {
101+
return process(new PointerRef<T>(buf));
102+
} finally {
103+
libxml2._free(buf);
104+
}
105+
}
106+
82107
function withStrings<R>(process: (...buf: number[]) => R, ...strings: (string | null)[]): R {
83108
const args = strings.map((str) => {
84109
const [buf] = allocUTF8Buffer(str);
@@ -725,6 +750,7 @@ export const xmlXPathFreeContext = libxml2._xmlXPathFreeContext;
725750
export const xmlXPathFreeObject = libxml2._xmlXPathFreeObject;
726751
export const xmlXPathNewContext = libxml2._xmlXPathNewContext;
727752
export const xmlXPathSetContextNode = libxml2._xmlXPathSetContextNode;
753+
export const xmlDOMWrapCloneNode = libxml2._xmlDOMWrapCloneNode;
728754

729755
/**
730756
* Create an output buffer using I/O callbacks (same pattern as xmlSaveToIO)

src/libxml2raw.d.mts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ type Pointer = number;
22
type CString = Pointer;
33
type XmlAttrPtr = Pointer;
44
type XmlCharEncodingHandlerPtr = Pointer;
5+
type XMLDomWrapCtxtPtr = Pointer;
56
type XmlParserCtxtPtr = Pointer;
67
type XmlParserInputBufferPtr = Pointer;
78
type XmlDocPtr = Pointer;
@@ -62,6 +63,16 @@ export class LibXml2 {
6263
_xmlCtxtValidateDtd(ctxt: XmlParserCtxtPtr, doc: XmlDocPtr, dtd: XmlDtdPtr): number;
6364
_xmlFreeNode(node: XmlNodePtr): void;
6465
_xmlFreeParserCtxt(ctxt: XmlParserCtxtPtr): void;
66+
_xmlDOMWrapCloneNode(
67+
ctxt: XMLDomWrapCtxtPtr,
68+
sourceDoc: XmlDocPtr,
69+
node: XmlNodePtr,
70+
resNode: Pointer,
71+
destDoc: XmlDocPtr,
72+
destParent: XmlDocPtr,
73+
deep: number,
74+
options: number,
75+
): number;
6576
_xmlDocGetRootElement(doc: XmlDocPtr): XmlNodePtr;
6677
_xmlDocSetRootElement(doc: XmlDocPtr, root: XmlNodePtr): XmlNodePtr;
6778
_xmlFreeDoc(Doc: XmlDocPtr): void;

src/nodes.mts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import {
88
XmlNsStruct,
99
XmlOutputBufferHandler,
1010
XmlXPathObjectStruct,
11+
withPointerRef,
1112
xmlAddChild,
1213
xmlAddNextSibling,
1314
xmlAddPrevSibling,
15+
xmlDOMWrapCloneNode,
1416
xmlFreeNode,
1517
xmlGetNsList,
1618
xmlHasNsProp,
@@ -798,6 +800,37 @@ export class XmlElement extends XmlTreeNode {
798800
);
799801
}
800802

803+
/**
804+
* Insert a clone of the given source node to the end of the children list.
805+
*/
806+
addClone(srcNode: XmlElement, deep: boolean = true): XmlElement {
807+
return withPointerRef((resNodePtr) => {
808+
const result = xmlDOMWrapCloneNode(
809+
0,
810+
srcNode.doc._ptr,
811+
srcNode._nodePtr,
812+
resNodePtr.ptr,
813+
this.doc._ptr,
814+
this._nodePtr,
815+
deep ? 1 : 0,
816+
0,
817+
);
818+
819+
switch (result) {
820+
case 0:
821+
{
822+
const resNode = resNodePtr.dereference();
823+
xmlAddChild(this._nodePtr, resNode);
824+
return new XmlElement(resNode);
825+
}
826+
case 1:
827+
throw new Error('Unsupported node type given to addClone');
828+
default:
829+
throw new Error('Internal error in addClone');
830+
}
831+
});
832+
}
833+
801834
/**
802835
* Save the XmlElement to a buffer and invoke the callbacks to process.
803836
*

0 commit comments

Comments
 (0)