Skip to content

Commit 250e088

Browse files
committed
feat(c14n): add exec c14n method
1 parent 7dc6b98 commit 250e088

File tree

4 files changed

+182
-2
lines changed

4 files changed

+182
-2
lines changed

xmlua.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ local Searchable = require("xmlua.searchable")
1717
Searchable.lazy_load()
1818
Searchable.lazy_load = nil
1919

20+
local C14n = require("xmlua.c14n")
21+
C14n.lazy_load()
22+
C14n.lazy_load = nil
23+
2024
function xmlua.init()
2125
xmlua.libxml2.xmlInitParser()
2226
end

xmlua/c14n.lua

Lines changed: 153 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,40 @@ local C14n = {}
33
local libxml2 = require("xmlua.libxml2")
44
local ffi = require("ffi")
55

6+
local DEFAULT_MODE = "C14N_EXCLUSIVE_1_0"
7+
8+
local Attribute
9+
local AttributeDeclaration
10+
local CDATASection
11+
local Comment
12+
local Document
13+
local DocumentFragment
14+
local DocumentType
15+
local Element
16+
local ElementDeclaration
17+
local EntityDeclaration
18+
local NamespaceDeclaration
19+
local Notation
20+
local ProcessingInstruction
21+
local Text
22+
23+
function C14n.lazy_load()
24+
Attribute = require("xmlua.attribute")
25+
AttributeDeclaration = require("xmlua.attribute-declaration")
26+
Text = require("xmlua.text")
27+
CDATASection = require("xmlua.cdata-section")
28+
Comment = require("xmlua.comment")
29+
Document = require("xmlua.document")
30+
DocumentFragment = require("xmlua.document-fragment")
31+
DocumentType = require("xmlua.document-type")
32+
Element = require("xmlua.element")
33+
ElementDeclaration = require("xmlua.element-declaration")
34+
EntityDeclaration = require("xmlua.entity-declaration")
35+
NamespaceDeclaration = require("xmlua.namespace-declaration")
36+
Notation = require("xmlua.notation")
37+
ProcessingInstruction = require("xmlua.processing-instruction")
38+
end
39+
640

741

842
local C14N_MODES = {
@@ -81,8 +115,8 @@ end
81115
-- @param with_comments if truthy, comments will be included (default: false)
82116
-- @return string containing canonicalized xml
83117
function C14n:c14n_save(nodes, mode, inclusive_ns_prefixes, with_comments)
84-
if mode == nil then -- default is exclusive 1.0
85-
mode = "C14N_EXCLUSIVE_1_0"
118+
if mode == nil then
119+
mode = DEFAULT_MODE
86120
end
87121
with_comments = with_comments and 1 or 0 -- default = not including comments
88122

@@ -103,4 +137,121 @@ end
103137

104138

105139

140+
local wrap_raw_node do
141+
-- order is according to the constant value of xmlElementType enum in libxml2
142+
local type_generators = setmetatable({
143+
[ffi.C.XML_ELEMENT_NODE] = function(document, raw_node)
144+
return Element.new(document, raw_node)
145+
end,
146+
[ffi.C.XML_ATTRIBUTE_NODE] = function(document, raw_node)
147+
return Attribute.new(document, raw_node)
148+
end,
149+
[ffi.C.XML_TEXT_NODE] = function(document, raw_node)
150+
return Text.new(document, raw_node)
151+
end,
152+
[ffi.C.XML_CDATA_SECTION_NODE] = function(document, raw_node)
153+
return CDATASection.new(document, raw_node)
154+
end,
155+
[ffi.C.XML_ENTITY_REF_NODE] = function(document, raw_node)
156+
error("XML_ENTITY_REF_NODE not implemented") -- TODO: implement
157+
end,
158+
[ffi.C.XML_ENTITY_NODE] = function(document, raw_node)
159+
error("XML_ENTITY_NODE not implemented") -- TODO: implement
160+
end,
161+
[ffi.C.XML_PI_NODE] = function(document, raw_node)
162+
return ProcessingInstruction.new(document, raw_node)
163+
end,
164+
[ffi.C.XML_COMMENT_NODE] = function(document, raw_node)
165+
return Comment.new(document, raw_node)
166+
end,
167+
[ffi.C.XML_DOCUMENT_NODE] = function(document, raw_node)
168+
return Document.new(raw_node)
169+
end,
170+
[ffi.C.XML_DOCUMENT_TYPE_NODE] = function(document, raw_node)
171+
return DocumentType.new(document, raw_node)
172+
end,
173+
[ffi.C.XML_DOCUMENT_FRAG_NODE] = function(document, raw_node)
174+
return DocumentFragment.new(document, raw_node)
175+
end,
176+
[ffi.C.XML_NOTATION_NODE] = function(document, raw_node)
177+
return Notation.new(document, raw_node)
178+
end,
179+
[ffi.C.XML_HTML_DOCUMENT_NODE] = function(document, raw_node)
180+
error("XML_HTML_DOCUMENT_NODE not implemented") -- TODO: implement
181+
end,
182+
[ffi.C.XML_DTD_NODE] = function(document, raw_node)
183+
error("XML_DTD_NODE not implemented") -- TODO: implement
184+
end,
185+
[ffi.C.XML_ELEMENT_DECL] = function(document, raw_node)
186+
return ElementDeclaration.new(document, raw_node)
187+
end,
188+
[ffi.C.XML_ATTRIBUTE_DECL] = function(document, raw_node)
189+
return AttributeDeclaration.new(document, raw_node)
190+
end,
191+
[ffi.C.XML_ENTITY_DECL] = function(document, raw_node)
192+
return EntityDeclaration.new(document, raw_node)
193+
end,
194+
[ffi.C.XML_NAMESPACE_DECL] = function(document, raw_node)
195+
return NamespaceDeclaration.new(document, raw_node)
196+
end,
197+
[ffi.C.XML_XINCLUDE_START] = function(document, raw_node)
198+
error("XML_XINCLUDE_START not implemented") -- TODO: implement
199+
end,
200+
[ffi.C.XML_XINCLUDE_END] = function(document, raw_node)
201+
error("XML_XINCLUDE_END not implemented") -- TODO: implement
202+
end,
203+
[ffi.C.XML_DOCB_DOCUMENT_NODE] = function(document, raw_node)
204+
error("XML_DOCB_DOCUMENT_NODE not implemented") -- TODO: implement
205+
end,
206+
}, {
207+
__index = function(self, key)
208+
error("Unknown node type: " .. tostring(key))
209+
end
210+
})
211+
212+
function wrap_raw_node(document, raw_node)
213+
if raw_node == ffi.NULL then
214+
return nil
215+
end
216+
return type_generators[tonumber(raw_node.type)](document, raw_node)
217+
end
218+
end
219+
220+
221+
222+
--- Canonicalise an xmlDocument or set of elements.
223+
-- @param self xmlDoc from which to canonicalize elements
224+
-- @param callback function to determine if a node should be included in the canonicalized output.
225+
-- Signature: `boolean = function(node, parent)`
226+
-- @param mode any of C14N_1_0, C14N_EXCLUSIVE_1_0 (default), C14N_1_1
227+
-- @param inclusive_ns_prefixes array, or space-separated string, of namespace prefixes to include
228+
-- @param with_comments if truthy, comments will be included (default: false)
229+
-- @return string containing canonicalized xml
230+
function C14n:c14n_execute(callback, mode, inclusive_ns_prefixes, with_comments)
231+
if mode == nil then
232+
mode = DEFAULT_MODE
233+
end
234+
with_comments = with_comments and 1 or 0 -- default = not including comments
235+
assert(type(callback) == "function", "callback must be a function")
236+
237+
-- wrap the callback to pass wrapped objects, and return 1 or 0
238+
local cbwrapper = function(_, nodePtr, parentPtr)
239+
return callback(wrap_raw_node(self, nodePtr), wrap_raw_node(self, parentPtr)) and 1 or 0
240+
end
241+
242+
mode = assert(C14N_MODES_LOOKUP[mode], "mode must be a valid C14N mode constant")
243+
local prefixes = getNamespacePrefixArray(inclusive_ns_prefixes)
244+
local buffer = libxml2.xmlBufferCreate()
245+
local output_buffer = libxml2.xmlOutputBufferCreate(buffer)
246+
247+
local success = libxml2.xmlC14NExecute(self.document, cbwrapper, nil, mode,
248+
prefixes, with_comments, output_buffer)
249+
250+
if success < 0 then
251+
return nil, "failed to generate C14N string"
252+
end
253+
return libxml2.xmlBufferGetContent(buffer)
254+
end
255+
256+
106257
return C14n

xmlua/libxml2.lua

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,17 @@ if __xmlC14NDocSaveToIsAvailable() then
8181
libxml2.xmlC14NDocSaveTo = xml2.xmlC14NDocSaveTo
8282
end
8383

84+
local function __xmlC14NExecuteIsAvailable()
85+
local success, err = pcall(function()
86+
local func = xml2.xmlC14NExecute
87+
end)
88+
return success, err
89+
end
90+
91+
if __xmlC14NExecuteIsAvailable() then
92+
libxml2.xmlC14NExecute = xml2.xmlC14NExecute
93+
end
94+
8495
libxml2.xmlInitParser = xml2.xmlInitParser
8596
libxml2.xmlCleanupParser = xml2.xmlCleanupParser
8697

xmlua/libxml2/c14n.lua

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,18 @@ int
2020
xmlChar **inclusive_ns_prefixes,
2121
int with_comments,
2222
xmlOutputBufferPtr buf);
23+
24+
typedef int (*xmlC14NIsVisibleCallback) (void* user_data,
25+
xmlNodePtr node,
26+
xmlNodePtr parent);
27+
28+
int
29+
xmlC14NExecute (xmlDocPtr doc,
30+
xmlC14NIsVisibleCallback is_visible_callback,
31+
void* user_data,
32+
int mode, /* a xmlC14NMode */
33+
xmlChar **inclusive_ns_prefixes,
34+
int with_comments,
35+
xmlOutputBufferPtr buf);
36+
2337
]]

0 commit comments

Comments
 (0)