Merge

Mon, 03 Jan 2011 22:20:49 +0000

author
Matthew Wild <mwild1@gmail.com>
date
Mon, 03 Jan 2011 22:20:49 +0000
changeset 3994
b3dffb6bc1aa
parent 3993
cbfa0ea6668b (current diff)
parent 3992
de77ec2b49bc (diff)
child 3995
7214dc7a5642

Merge

--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/util/xmllex.lua	Mon Jan 03 22:20:49 2011 +0000
@@ -0,0 +1,187 @@
+local assert, ipairs , pairs , setmetatable , rawget , rawset , tostring =
+	assert, ipairs , pairs , setmetatable , rawget , rawset , tostring
+local strsub = string.sub
+local tblconcat = table.concat
+local tblinsert = table.insert
+
+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 )
+			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 == "/" 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" }
+	
+	if not r[#r] then
+		r[1] = setmetatable ( {
+			msgs = { str } ;
+			type = "text" ;
+			start = 1 ;
+		} , m_mt )
+	else
+		tblinsert ( r[#r].msgs , str )
+	end
+	
+	repeat
+		if r.state == "outside" then
+			if not handleoutside ( str , r , r[#r].start ) then
+				break
+			end
+		else
+			if not handleinside ( str , r , r[#r].start ) then
+				break
+			end
+		end
+	until false
+
+	return r
+end
+
+local function process_starttag ( starttag )
+	local str = tostring ( starttag )
+	local attr = { }
+	
+	local elem = str:match ( "[^%s=></]+" )
+	for name , quote, attvalue in str:gmatch ( [=[([^%s=<]+)%s*=%s*(["'])([^"]*)%2]=] ) do
+		attr [ name ] = attvalue
+	end
+	return elem , attr
+end
+
+local stanza_mt = {
+	__index = function ( t , k )
+		if k == "name" or k == "attr" then
+			local elem , attr = process_starttag ( t.opentag )
+			rawset ( t , "name" , elem )
+			rawset ( t , "attr" , attr )
+			return rawget ( t , k )
+		else
+			print("METHOD",k)
+			return stanza_methods [ k ]
+		end
+	end ;
+	__tostring = function ( t )
+		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 ;
+}
+
+local function new_stanza ( )
+	return setmetatable ( { tags = { } } , stanza_mt )
+end
+
+local function tagindex_to_tree(indices)
+	local root = { tags = { } }
+	local leaf = root
+	
+	for k ,v in ipairs ( indices ) do
+		if v.type == "selfclosing" then
+			local newleaf = new_stanza ( )
+			newleaf.opentag = v
+			newleaf.selfclosing = true
+			newleaf.parent = leaf
+			
+			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 ( )
+			newleaf.opentag = v
+			newleaf.parent = leaf
+			
+			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;
+};
--- a/util/xmppstream.lua	Mon Jan 03 22:20:37 2011 +0000
+++ b/util/xmppstream.lua	Mon Jan 03 22:20:49 2011 +0000
@@ -8,6 +8,7 @@
 
 
 local lxp = require "lxp";
+local lex = require "util.xmllex";
 local st = require "util.stanza";
 local stanza_mt = st.stanza_mt;
 
@@ -23,158 +24,14 @@
 
 module "xmppstream"
 
-local new_parser = lxp.new;
-
-local ns_prefixes = {
-	["http://www.w3.org/XML/1998/namespace"] = "xml";
-};
-
 local xmlns_streams = "http://etherx.jabber.org/streams";
 
-local ns_separator = "\1";
-local ns_pattern = "^([^"..ns_separator.."]*)"..ns_separator.."?(.*)$";
-
-_M.ns_separator = ns_separator;
-_M.ns_pattern = ns_pattern;
-
-function new_sax_handlers(session, stream_callbacks)
-	local xml_handlers = {};
-	
-	local log = session.log or default_log;
-	
-	local cb_streamopened = stream_callbacks.streamopened;
-	local cb_streamclosed = stream_callbacks.streamclosed;
-	local cb_error = stream_callbacks.error or function(session, e) error("XML stream error: "..tostring(e)); end;
-	local cb_handlestanza = stream_callbacks.handlestanza;
-	
-	local stream_ns = stream_callbacks.stream_ns or xmlns_streams;
-	local stream_tag = stream_callbacks.stream_tag or "stream";
-	if stream_ns ~= "" then
-		stream_tag = stream_ns..ns_separator..stream_tag;
-	end
-	local stream_error_tag = stream_ns..ns_separator..(stream_callbacks.error_tag or "error");
-	
-	local stream_default_ns = stream_callbacks.default_ns;
+function new(session, stream_callbacks)
+	local partial;
 	
-	local stack = {};
-	local chardata, stanza = {};
-	local non_streamns_depth = 0;
-	function xml_handlers:StartElement(tagname, attr)
-		if stanza and #chardata > 0 then
-			-- We have some character data in the buffer
-			t_insert(stanza, t_concat(chardata));
-			chardata = {};
-		end
-		local curr_ns,name = tagname:match(ns_pattern);
-		if name == "" then
-			curr_ns, name = "", curr_ns;
-		end
-
-		if curr_ns ~= stream_default_ns or non_streamns_depth > 0 then
-			attr.xmlns = curr_ns;
-			non_streamns_depth = non_streamns_depth + 1;
-		end
-		
-		-- FIXME !!!!!
-		for i=1,#attr do
-			local k = attr[i];
-			attr[i] = nil;
-			local ns, nm = k:match(ns_pattern);
-			if nm ~= "" then
-				ns = ns_prefixes[ns];
-				if ns then
-					attr[ns..":"..nm] = attr[k];
-					attr[k] = nil;
-				end
-			end
-		end
-		
-		if not stanza then --if we are not currently inside a stanza
-			if session.notopen then
-				if tagname == stream_tag then
-					non_streamns_depth = 0;
-					if cb_streamopened then
-						cb_streamopened(session, attr);
-					end
-				else
-					-- Garbage before stream?
-					cb_error(session, "no-stream");
-				end
-				return;
-			end
-			if curr_ns == "jabber:client" and name ~= "iq" and name ~= "presence" and name ~= "message" then
-				cb_error(session, "invalid-top-level-element");
-			end
-			
-			stanza = setmetatable({ name = name, attr = attr, tags = {} }, stanza_mt);
-		else -- we are inside a stanza, so add a tag
-			t_insert(stack, stanza);
-			local oldstanza = stanza;
-			stanza = setmetatable({ name = name, attr = attr, tags = {} }, stanza_mt);
-			t_insert(oldstanza, stanza);
-			t_insert(oldstanza.tags, stanza);
-		end
+	local function feed(stream, data)
+		index(data, 
 	end
-	function xml_handlers:CharacterData(data)
-		if stanza then
-			t_insert(chardata, data);
-		end
-	end
-	function xml_handlers:EndElement(tagname)
-		if non_streamns_depth > 0 then
-			non_streamns_depth = non_streamns_depth - 1;
-		end
-		if stanza then
-			if #chardata > 0 then
-				-- We have some character data in the buffer
-				t_insert(stanza, t_concat(chardata));
-				chardata = {};
-			end
-			-- Complete stanza
-			if #stack == 0 then
-				if tagname ~= stream_error_tag then
-					cb_handlestanza(session, stanza);
-				else
-					cb_error(session, "stream-error", stanza);
-				end
-				stanza = nil;
-			else
-				stanza = t_remove(stack);
-			end
-		else
-			if tagname == stream_tag then
-				if cb_streamclosed then
-					cb_streamclosed(session);
-				end
-			else
-				local curr_ns,name = tagname:match(ns_pattern);
-				if name == "" then
-					curr_ns, name = "", curr_ns;
-				end
-				cb_error(session, "parse-error", "unexpected-element-close", name);
-			end
-			stanza, chardata = nil, {};
-			stack = {};
-		end
-	end
-	
-	local function reset()
-		stanza, chardata = nil, {};
-		stack = {};
-	end
-	
-	local function set_session(stream, new_session)
-		session = new_session;
-		log = new_session.log or default_log;
-	end
-	
-	return xml_handlers, { reset = reset, set_session = set_session };
-end
-
-function new(session, stream_callbacks)
-	local handlers, meta = new_sax_handlers(session, stream_callbacks);
-	local parser = new_parser(handlers, ns_separator);
-	local parse = parser.parse;
 
 	return {
 		reset = function ()

mercurial