Mercurial > prosody-hg
comparison util/template.lua @ 3545:c85f9a4ae1c4
util.template: Initial commit. A template library for XML stanzas.
| author | Waqas Hussain <waqas20@gmail.com> |
|---|---|
| date | Sun, 24 Oct 2010 15:06:13 +0500 |
| parents | |
| children | cb1600dea3ad |
comparison
equal
deleted
inserted
replaced
| 3544:f2aca3e0fe3b | 3545:c85f9a4ae1c4 |
|---|---|
| 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; |
