c143a216ee2009b0445b5a03d855088238984891
[prosody.git] / util / template.lua
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;