lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On Wednesday 07 March 2007, Rici Lake wrote:
> On 7-Mar-07, at 4:14 PM, Javier Guerra wrote:
> > if anybody is interested, i might try to document it
>
> I'd be really interested.

attached is my XML module. it depends on lxp.

basically, you call xlp.new_page() with input and output functions.  the 
returned object is a table with this fields:

page.cb  : initally empty, put here your callback functions
page.node : main node of XML
page.glob : table of all nodes with IDs, keyed by id, of course
page.outf : your output function.


on your XML template, just add a function="your_funcname" attribute whenever 
you want to be called.  your callback should be like this:

function page.cb.you_funcname (node, extraparams)
	-- node is the node you tagged with the 'function' attribute
	-- extraparams are usually nil, might be anything if you nest calls
end

in the callback you can modify the node, return a new node, or call 
some 'output' functions.

page:out (node, ...)  : renders the node and any content.
page:out_content (node, ...) : renders only the content of the node
page:out_tagstart (node, empty) : renders only the starting tag of the node. 
if empty is true, then the tag is a non-content one (like <tag />)
page:out_tagend (node) : renders only the finish tag of the node

page:out() and page:out_content() render the main node if none is specified.  
also, any extra parameters given here would be relayed to any nested callback 
on inner nodes.

you can access (and modify) any attribute of a node like node.attr.attrname 
(or attr.attr["attrname"] if the attribute name isn't very lua friendly).

to create a new node, call
xlp.new_node (tagname, attribute_table, content nodes ...)
or
xlp.new_node (oldnode, content nodes ...)
in the second case, the new node would copy the tagname and all attributes 
from the oldnode.

if you need the XML as a string, call
page:render (node, ...)
or
page:render_content (node, ...)
these functions call out(node, ...) and out_content(node,...) respectively, 
but replace the output function of the page with an accumulation and return 
the result as a string.

hum.... i think that's all...


-- 
Javier
local assert, type, print, setmetatable = assert, type, print, setmetatable
local ipairs, pairs = ipairs, pairs
local table, string = table, string
local tinsert, tremove = table.insert, table.remove

local lxp = require "lxp"

module ("xlp")

local function top (t)
	return t[#t]
end

local function attrstostr (a)
	local out = {}
	for i,attr in ipairs (a) do
		if a[attr] then
			tinsert (out, string.format ('%s="%s"', attr,  a[attr]))
		end
	end
	return table.concat (out, " ")
end

local function _tagstart (name, attr, empty)
	local t = "<"..name
	if attr and attr[1] then
		t = t .. " "..attrstostr (attr)
	end
	if empty then
		t = t .. " /"
	end
	t = t .. ">"
	return t
end

function tagstart (node, empty)
	assert (type (node) == "table")
	if empty == nil then empty = not node[1] end
	return _tagstart (node.tag, node.attr, empty)
end

function tagend (node)
	assert (type (node) == "table")
	return "</"..node.tag..">"
end

local function wholetag (name, attr, content)
	local empty = not content or content==""
	local t = _tagstart (name, attr, empty)
	if not empty then
		t = t .. content .. "</"..name..">"
	end
	return t
end


local function pack_node (node)
	local empty, purestring = true, true
	local acc, pack = {}, {}

	for i,v in ipairs (node) do
		empty = false
		local t = type (v)
		if t == "string" then
			tinsert (acc, v)
		else
			purestring = false
			tinsert (pack, table.concat (acc))
			acc = {}
			tinsert (pack, v)
		end
	end
	tinsert (pack, table.concat (acc))
	
	if purestring then
		return wholetag (node.tag, node.attr, table.concat (pack))
	else
		pack.tag = node.tag
		pack.attr = node.attr
		return pack
	end
end

local function simple_parser (cb, idattr)
	cb = cb or {}
	idattr = idattr or "id"
	
	local stack = {{}}
	local glob = {}
	
	local function get_id (id)
		return glob [id]
	end
	local function get_fun (node, ...)
	--	print ("get_fun", node.tag, attrstostr (node.attr), cb [node.attr["function"]])
		local fun = cb [node.attr["function"]] or pack_node
		return fun (node, ...)
	end
	
	return lxp.new {
		StartElement = function(parser, elementName, attributes)
				tinsert (stack, {tag=elementName, attr=attributes})
			end,
			
		EndElement = function(parser, elementName)
				local node = tremove (stack)
				local parent = top (stack)
				
-- 				local hand = cb [elementName] or pack_node
-- 				local result = hand (node)
				
				if node.attr["function"] then
					tinsert (parent, {_f = get_fun, _p =node})
				elseif node.attr[idattr] then
					local id = node.attr[idattr]
					glob [id] = pack_node (node)
					tinsert (parent, {_f = get_id, _p=id})
				else
					tinsert (parent, pack_node (node))
				end
			end,
			
-- 		CharacterData = function (parser, string)
-- 				tinsert (stack [getn (stack)], string)
-- 			end,
			
		Default = function (parser, string)
				tinsert (top (stack), string)
			end,
			
	}, stack[1], glob
end

local function do_out (node, outf, contonly, ...)
	local t = type (node)
	if t == "string" then
		outf (node)
	else
		if t == "table" then
			if node._f then
				do_out (node._f(node._p, ...), outf, contonly, ...)
			else
				if not contonly and node.tag then
					outf (tagstart (node))
				end
				for _,v in ipairs (node) do
					do_out (v, outf, nil, ...)
				end
				if not contonly and node.tag then
					outf ("</"..node.tag..">")
				end
			end
		end
	end
end

function new_node (a, b, ...)
	if type (a) == "table" then
		return { tag = a.tag, attr=a.attr, b, ...}
	else
		return { tag = a, attr = b, ...}
	end
end


local page_mt = {__index={}}

function page_mt.__index:out (node, ...)
	return do_out (node or self.node, self.outf, nil, ...)
end
function page_mt.__index:out_content (node, ...)
	return do_out (node or self.node, self.outf, true, ...)
end
function page_mt.__index:out_tagstart (node, empty)
	return self.outf (tagstart (node, empty))
end
function page_mt.__index:out_tagend (node)
	return self.outf (tagend (node))
end

function page_mt.__index:render (node, ...)
	local outtab = {}
	do_out (node or self.node, function (s) tinsert (outtab, s) end, nil, ...)
	return table.concat (outtab)
end
function page_mt.__index:render_content (node, ...)
	local outtab = {}
	do_out (node or self.node, function (s) tinsert (outtab, s) end, true, ...)
	return table.concat (outtab)
end



function new_page (input, output)
	local cb = {}
	local p,node,glob = simple_parser (cb)
	
	local typ = type (input)
	if typ == "string" then
		p:parse (input)
		
	elseif typ == "table" then
		for _,v in ipairs (input) do
			p:parse (v)
		end
		
	elseif typ == "function" then
		local l = input()
		while l ~= nil do
			p:parse (l)
			l = input ()
		end
	end
	p:close()
	
	return setmetatable ({
		cb = cb,
		node=node,
		glob=glob,
		outf = output
	}, page_mt)
end


function make_handler (page)
	return function (req, res)
		page.outf = function (s) res:send_data (s) end
		res.headers ["Content-Type"] = "text/xml"
		page:out ()
	end
end

Attachment: pgpbez4GB3JSe.pgp
Description: PGP signature