Wed, 05 Jan 2011 18:01:27 +0000
xmppcomponent_listener: Remove useless require of lxp
local assert , ipairs , pairs , setmetatable , rawget , rawset , tostring = assert , ipairs , pairs , setmetatable , rawget , rawset , tostring local strchar , strgmatch , strgsub , strsub , strmatch = string.char , string.gmatch , string.gsub , string.sub , string.match local tblconcat = table.concat local tblinsert = table.insert local stanza_methods = require "util.stanza".stanza_mt; local entities = setmetatable ( { amp = "&" ; lt = "<" ; gt = ">" ; apos = "'" ; quot = '"' ; } , { __index = function ( entity ) return strchar ( tonumber ( entity:match ( "^#%d+" ) ) or error ( "invalid entity " .. entity ) ) end } ) local function xml_unescape ( str ) return ( strgsub ( str , "&([^;]*);" , entities ) ) end local function getstring ( msgs , startpos , finishpos ) if #msgs == 1 then --All originated in same string return strsub ( msgs[1] , startpos , finishpos ) else -- Over multiple source strings return strsub ( msgs[1] , startpos , -1 ) .. tblconcat ( msgs , "" , 2 , #msgs - 1 ) .. strsub ( msgs[#msgs] , 1 , finishpos ) end end local m_mt = { __tostring = function ( v ) local str = v.stringform if str then return str else str = getstring ( v.msgs , v.start , v.finish ) if v.type == "text" then str = xml_unescape ( str ) end v.stringform = str return str end end } local function handleoutside ( str , r , initial ) local a , b , close = str:find ( "<(/?)" , initial ) if not a then r.state = "outside" return false end --Finalise text object local m = r[#r] m.finish = a - 1 m.type = "text" local m = setmetatable ( { msgs = { str } ; start = a ; starte = b + 1 ; } , m_mt ) if close ~= "/" then r.depth = r.depth + 1 m.type = "open" else r.depth = r.depth - 1 m.type = "close" end tblinsert ( r , m ) r.state = "inside" return true end local function handleinside ( str, r , initial ) local c , d , selfclosing = str:find ( "([/?]?)>" , initial ) if not c then r.state = "inside" return false end local m = r[#r] m.finish = d m.finishs = c - 1 if selfclosing == "/" or selfclosing == "?" then m.type = "selfclosing" r.depth = r.depth - 1 end local m = setmetatable ( { msgs = { str } ; start = d + 1 ; type = "text" ; } , m_mt ) tblinsert ( r , m ) r.state = "outside" return true end local function index ( str , r ) r = r or { depth = 0, state = "outside" } local initial = 1 if not r[#r] then r[1] = setmetatable ( { msgs = { str } ; type = "text" ; start = 1 ; } , m_mt ) else tblinsert ( r[#r].msgs , str ) end while true do if r.state == "outside" then if not handleoutside ( str , r , initial ) then break end else if not handleinside ( str , r , initial ) then break end end initial = r[#r].start end return r end local function get_name ( str ) return strmatch ( str , "^<([^%s>/]+)" ) end local function get_attr ( str ) local attr = { } for name , quote, attvalue in strgmatch ( str , [=[([^%s=/<]+)%s*=%s*(["'])([^'"]*)%2]=] ) do attr [ name ] = xml_unescape ( attvalue ) end return attr end function resolve_attr_namespaces ( attr ) local namespace = { } local prefixattr = { } for k , attr_value in pairs ( attr ) do local attr_prefix , attr_name = k:match ( "^([^:\1]+):?([^\1]-)$" ) if attr_prefix == nil then error ( "already resolved" ) elseif attr_prefix == "xmlns" then namespace [ attr_name ] = attr_value elseif #attr_name ~= 0 and attr_prefix ~= "xml" then local t = prefixattr [ attr_prefix ] if not t then t = { } prefixattr [ attr_prefix ] = t end t [ attr_name ] = attr_value end end for k , v in pairs ( prefixattr ) do for name , value in pairs ( v ) do attr [ namespace [ k ] .. "\1" .. name ] = value end end return namespace end local currentindex = 1 local function resolve_namespace ( element ) local parent = element.parent local prefix = get_name ( element.str ):match ( "^([^:]+):" ) local namespace = setmetatable ( resolve_attr_namespaces ( element.attr ) , { __index = parent.namespace } ) local current_namespace = prefix and ( namespace [ prefix ] or error ("unbound prefix: "..prefix) ) or rawget(element.attr, "xmlns") or parent.attr.xmlns or parent.namespace [ currentindex ] namespace [ currentindex ] = current_namespace return namespace end local dynamic_properties = { name = function ( t ) return get_name ( t.str ):match("[^:]+$") end; attr = function ( t , k ) return setmetatable ( get_attr ( t.str ) , { __index = function ( attr_table , attr ) local _ = t.namespace -- DO NOT OPTIMISE AWAY WAQAS setmetatable ( attr_table , { __index = { xmlns = t.namespace[currentindex] } } ) return attr_table [ attr ] end } ) end; str = function ( t , k ) return tostring ( t.opentag ) end; namespace = resolve_namespace ; } local stanza_mt = { __index = function ( t , k ) local f = dynamic_properties [ k ] if f then local v = f ( t ) rawset ( t , k , v ) return v else return stanza_methods[k] end end ; __tostring = function ( t ) if t.modified then return stanza_methods.__tostring ( t ) end local opentag = t.opentag local endtag = assert ( rawget ( t , "endtag" ) or rawget ( t , "selfclosing" ) and t.opentag ) return getstring ( opentag.msgs , opentag.start , endtag.finish ) end ; __newindex = function ( t , k , v ) rawset ( t , "modified", true ) rawset ( t , k , v ) end ; } local function new_stanza ( parent ) return setmetatable ( { tags = { } , parent = parent } , stanza_mt ) end local function tagindex_to_tree(indices, start, finish,root) if not start then start = 1 finish = #indices end root = root or { attr = { } } root.namespace = resolve_attr_namespaces ( root.attr ) root.root = true root.tags = { } local leaf = root for i = start , finish do local v = indices [ i ] if v.type == "selfclosing" then local newleaf = new_stanza ( leaf ) newleaf.opentag = v newleaf.selfclosing = true tblinsert ( leaf , newleaf ) tblinsert ( leaf.tags , newleaf ) elseif v.type == "close" then -- Close tag leaf.endtag = v leaf = leaf.parent elseif v.type == "text" then tblinsert ( leaf, v ) else -- Open tag local newleaf = new_stanza ( leaf ) newleaf.opentag = v tblinsert ( leaf , newleaf ) tblinsert ( leaf.tags , newleaf ) leaf = newleaf end end assert ( leaf == root , "Mismatched opening/closing tags" ) return root; end return { index = index ; tagindex_to_tree = tagindex_to_tree ; get_name = get_name ; get_attr = get_attr ; resolve_attr_namespaces = resolve_attr_namespaces ; };