Software /
code /
prosody
Comparison
util/template.lua @ 3637:bd491def3efb
util.template: Rewritten to be much faster than the util.stanza stanza building API.
author | Waqas Hussain <waqas20@gmail.com> |
---|---|
date | Thu, 25 Nov 2010 08:38:26 +0500 |
parent | 3546:cb1600dea3ad |
child | 3640:4bc88bb748d1 |
comparison
equal
deleted
inserted
replaced
3636:88e4397e39a9 | 3637:bd491def3efb |
---|---|
1 | 1 |
2 local t_insert = table.insert; | |
3 local st = require "util.stanza"; | 2 local st = require "util.stanza"; |
4 local lxp = require "lxp"; | 3 local lxp = require "lxp"; |
5 local setmetatable = setmetatable; | 4 local setmetatable = setmetatable; |
6 local pairs = pairs; | 5 local pairs = pairs; |
6 local ipairs = ipairs; | |
7 local error = error; | 7 local error = error; |
8 local s_gsub = string.gsub; | 8 local loadstring = loadstring; |
9 | 9 local debug = debug; |
10 local print = print; | |
11 | 10 |
12 module("template") | 11 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:match("^{([^}]*)}$"), true}); | |
29 elseif child:match("{[^}]*}") then -- text | |
30 t_insert(ops, {stanza, i, child}); | |
31 end | |
32 i = i + 1; | |
33 end | |
34 end | |
35 | 12 |
36 local parse_xml = (function() | 13 local parse_xml = (function() |
37 local ns_prefixes = { | 14 local ns_prefixes = { |
38 ["http://www.w3.org/XML/1998/namespace"] = "xml"; | 15 ["http://www.w3.org/XML/1998/namespace"] = "xml"; |
39 }; | 16 }; |
81 return ok, err.." (line "..line..", col "..col..")"; | 58 return ok, err.." (line "..line..", col "..col..")"; |
82 end | 59 end |
83 end; | 60 end; |
84 end)(); | 61 end)(); |
85 | 62 |
63 local function create_string_string(str) | |
64 str = ("%q"):format(str); | |
65 str = str:gsub("{([^}]*)}", function(s) | |
66 return '"..(data["'..s..'"]or"").."'; | |
67 end); | |
68 return str; | |
69 end | |
70 local function create_attr_string(attr, xmlns) | |
71 local str = '{'; | |
72 for name,value in pairs(attr) do | |
73 if name ~= "xmlns" or value ~= xmlns then | |
74 str = str..("[%q]=%s;"):format(name, create_string_string(value)); | |
75 end | |
76 end | |
77 return str..'}'; | |
78 end | |
79 local function create_clone_string(stanza, lookup, xmlns) | |
80 if not lookup[stanza] then | |
81 local s = ('setmetatable({name=%q,attr=%s,last_add={},tags={'):format(stanza.name, create_attr_string(stanza.attr, xmlns)); | |
82 -- add tags | |
83 for i,tag in ipairs(stanza.tags) do | |
84 s = s..create_clone_string(tag, lookup, stanza.attr.xmlns)..";"; | |
85 end | |
86 s = s..'};'; | |
87 -- add children | |
88 for i,child in ipairs(stanza) do | |
89 if child.name then | |
90 s = s..create_clone_string(child, lookup, stanza.attr.xmlns)..";"; | |
91 else | |
92 s = s..create_string_string(child)..";" | |
93 end | |
94 end | |
95 s = s..'}, stanza_mt)'; | |
96 s = s:gsub('%.%.""', ""):gsub('([=;])""%.%.', "%1"):gsub(';"";', ";"); -- strip empty strings | |
97 local n = #lookup + 1; | |
98 lookup[n] = s; | |
99 lookup[stanza] = "_"..n; | |
100 end | |
101 return lookup[stanza]; | |
102 end | |
86 local stanza_mt = st.stanza_mt; | 103 local stanza_mt = st.stanza_mt; |
87 local function fast_st_clone(stanza, lookup) | 104 local function create_cloner(stanza, chunkname) |
88 local stanza_attr = stanza.attr; | 105 local lookup = {}; |
89 local stanza_tags = stanza.tags; | 106 local name = create_clone_string(stanza, lookup, ""); |
90 local tags, attr = {}, {}; | 107 local f = "local setmetatable,stanza_mt=...;return function(data)"; |
91 local clone = { name = stanza.name, attr = attr, tags = tags, last_add = {} }; | 108 for i=1,#lookup do |
92 for k,v in pairs(stanza_attr) do attr[k] = v; end | 109 f = f.."local _"..i.."="..lookup[i]..";"; |
93 lookup[stanza_attr] = attr; | |
94 for i=1,#stanza_tags do | |
95 local child = stanza_tags[i]; | |
96 local new = fast_st_clone(child, lookup); | |
97 tags[i] = new; | |
98 lookup[child] = new; | |
99 end | 110 end |
100 for i=1,#stanza do | 111 f = f.."return "..name..";end"; |
101 local child = stanza[i]; | 112 local f,err = loadstring(f, chunkname); |
102 clone[i] = lookup[child] or child; | 113 if not f then error(err); end |
103 end | 114 return f(setmetatable, stanza_mt); |
104 return setmetatable(clone, stanza_mt); | |
105 end | 115 end |
106 | 116 |
107 local function create_template(text) | 117 local template_mt = { __tostring = function(t) return t.name end }; |
118 local function create_template(templates, text) | |
108 local stanza, err = parse_xml(text); | 119 local stanza, err = parse_xml(text); |
109 if not stanza then error(err); end | 120 if not stanza then error(err); end |
110 local ops = {}; | 121 |
111 process_stanza(stanza, ops); | 122 local info = debug.getinfo(3, "Sl"); |
112 | 123 info = info and ("template(%s:%d)"):format(info.short_src:match("[^\\/]*$"), info.currentline) or "template(unknown)"; |
113 local template = {}; | 124 |
114 local lookup = {}; | 125 local template = setmetatable({ apply = create_cloner(stanza, info), name = info, text = text }, template_mt); |
115 function template.apply(data) | 126 templates[text] = template; |
116 local newstanza = fast_st_clone(stanza, lookup); | |
117 for i=1,#ops do | |
118 local op = ops[i]; | |
119 local t, k, v, g = op[1], op[2], op[3], op[4]; | |
120 if g then | |
121 lookup[t][k] = data[v]; | |
122 else | |
123 lookup[t][k] = s_gsub(v, "{([^}]*)}", data); | |
124 end | |
125 end | |
126 return newstanza; | |
127 end | |
128 return template; | 127 return template; |
129 end | 128 end |
130 | 129 |
131 local templates = setmetatable({}, { __mode = 'k' }); | 130 local templates = setmetatable({}, { __mode = 'k', __index = create_template }); |
132 return function(text) | 131 return function(text) |
133 local template = templates[text]; | 132 return templates[text]; |
134 if not template then | |
135 template = create_template(text); | |
136 templates[text] = template; | |
137 end | |
138 return template; | |
139 end; | 133 end; |