1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
local t_insert = table.insert;
local st = require "util.stanza";
local lxp = require "lxp";
local setmetatable = setmetatable;
local pairs = pairs;
local error = error;
local s_gsub = string.gsub;
local print = print;
module("template")
local function process_stanza(stanza, ops)
-- process attrs
for key, val in pairs(stanza.attr) do
if val:match("{[^}]*}") then
t_insert(ops, {stanza.attr, key, val});
end
end
-- process children
local i = 1;
while i <= #stanza do
local child = stanza[i];
if child.name then
process_stanza(child, ops);
elseif child:match("{[^}]*}") then -- text
t_insert(ops, {stanza, i, child});
end
i = i + 1;
end
end
local parse_xml = (function()
local ns_prefixes = {
["http://www.w3.org/XML/1998/namespace"] = "xml";
};
local ns_separator = "\1";
local ns_pattern = "^([^"..ns_separator.."]*)"..ns_separator.."?(.*)$";
return function(xml)
local handler = {};
local stanza = st.stanza("root");
function handler:StartElement(tagname, attr)
local curr_ns,name = tagname:match(ns_pattern);
if name == "" then
curr_ns, name = "", curr_ns;
end
if curr_ns ~= "" then
attr.xmlns = curr_ns;
end
for i=1,#attr do
local k = attr[i];
attr[i] = nil;
local ns, nm = k:match(ns_pattern);
if nm ~= "" then
ns = ns_prefixes[ns];
if ns then
attr[ns..":"..nm] = attr[k];
attr[k] = nil;
end
end
end
stanza:tag(name, attr);
end
function handler:CharacterData(data)
data = data:gsub("^%s*", ""):gsub("%s*$", "");
stanza:text(data);
end
function handler:EndElement(tagname)
stanza:up();
end
local parser = lxp.new(handler, "\1");
local ok, err, line, col = parser:parse(xml);
if ok then ok, err, line, col = parser:parse(); end
--parser:close();
if ok then
return stanza.tags[1];
else
return ok, err.." (line "..line..", col "..col..")";
end
end;
end)();
local function create_template(text)
local stanza, err = parse_xml(text);
if not stanza then error(err); end
local ops = {};
process_stanza(stanza, ops);
ops.stanza = stanza;
local template = {};
function template.apply(data)
local newops = st.clone(ops);
for i=1,#newops do
local op = newops[i];
local t, k, v = op[1], op[2], op[3];
t[k] = s_gsub(v, "{([^}]*)}", data);
end
return newops.stanza;
end
return template;
end
local templates = setmetatable({}, { __mode = 'k' });
return function(text)
local template = templates[text];
if not template then
template = create_template(text);
templates[text] = template;
end
return template;
end;
|