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