mirror of https://github.com/bjc/prosody
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
239 lines
6.2 KiB
239 lines
6.2 KiB
local st = require("util.stanza");
|
|
|
|
local function toboolean(s)
|
|
if s == "true" or s == "1" then
|
|
return true
|
|
elseif s == "false" or s == "0" then
|
|
return false
|
|
elseif s then
|
|
return true
|
|
end
|
|
end
|
|
|
|
local function totype(t, s)
|
|
if t == "string" then
|
|
return s
|
|
elseif t == "boolean" then
|
|
return toboolean(s)
|
|
elseif t == "number" or t == "integer" then
|
|
return tonumber(s)
|
|
end
|
|
end
|
|
|
|
local value_goes = {}
|
|
|
|
local function unpack_propschema(propschema, propname, current_ns)
|
|
|
|
local proptype = "string"
|
|
local value_where = "in_text_tag"
|
|
local name = propname
|
|
local namespace = current_ns
|
|
local prefix
|
|
local single_attribute
|
|
local enums
|
|
|
|
if type(propschema) == "table" then
|
|
proptype = propschema.type
|
|
elseif type(propschema) == "string" then
|
|
proptype = propschema
|
|
end
|
|
|
|
if type(propschema) == "table" then
|
|
local xml = propschema.xml
|
|
if xml then
|
|
if xml.name then
|
|
name = xml.name
|
|
end
|
|
if xml.namespace then
|
|
namespace = xml.namespace
|
|
end
|
|
if xml.prefix then
|
|
prefix = xml.prefix
|
|
end
|
|
|
|
if xml.attribute then
|
|
value_where = "in_attribute"
|
|
elseif xml.text then
|
|
value_where = "in_text"
|
|
elseif xml.x_name_is_value then
|
|
value_where = "in_tag_name"
|
|
elseif xml.x_single_attribute then
|
|
single_attribute = xml.x_single_attribute
|
|
value_where = "in_single_attribute"
|
|
end
|
|
end
|
|
if propschema["const"] then
|
|
enums = {propschema["const"]}
|
|
elseif propschema["enum"] then
|
|
enums = propschema["enum"]
|
|
end
|
|
end
|
|
|
|
if proptype == "object" or proptype == "array" then
|
|
value_where = "in_children"
|
|
end
|
|
|
|
return proptype, value_where, name, namespace, prefix, single_attribute, enums
|
|
end
|
|
|
|
local function parse_object(schema, s)
|
|
local out = {}
|
|
if schema.properties then
|
|
for prop, propschema in pairs(schema.properties) do
|
|
|
|
local proptype, value_where, name, namespace, prefix, single_attribute, enums = unpack_propschema(propschema, prop, s.attr.xmlns)
|
|
|
|
local value
|
|
if value_where == "in_tag_name" then
|
|
local c
|
|
if proptype == "boolean" then
|
|
c = s:get_child(name, namespace);
|
|
elseif enums and proptype == "string" then
|
|
|
|
for i = 1, #enums do
|
|
c = s:get_child(enums[i], namespace);
|
|
if c then
|
|
break
|
|
end
|
|
end
|
|
else
|
|
c = s:get_child(nil, namespace);
|
|
end
|
|
value = c.name;
|
|
elseif value_where == "in_attribute" then
|
|
local attr = name
|
|
if prefix then
|
|
attr = prefix .. ":" .. name
|
|
elseif namespace ~= s.attr.xmlns then
|
|
attr = namespace .. "\1" .. name
|
|
end
|
|
value = s.attr[attr]
|
|
|
|
elseif value_where == "in_text" then
|
|
value = s:get_text()
|
|
|
|
elseif value_where == "in_single_attribute" then
|
|
local c = s:get_child(name, namespace)
|
|
value = c and c.attr[single_attribute]
|
|
elseif value_where == "in_text_tag" then
|
|
value = s:get_child_text(name, namespace)
|
|
elseif value_where == "in_children" and type(propschema) == "table" then
|
|
if proptype == "object" then
|
|
local c = s:get_child(name, namespace)
|
|
if c then
|
|
out[prop] = parse_object(propschema, c);
|
|
end
|
|
|
|
end
|
|
end
|
|
if value_where ~= "in_children" then
|
|
out[prop] = totype(proptype, value)
|
|
end
|
|
end
|
|
end
|
|
|
|
return out
|
|
end
|
|
|
|
local function parse(schema, s)
|
|
if schema.type == "object" then
|
|
return parse_object(schema, s)
|
|
end
|
|
end
|
|
|
|
local function unparse(schema, t, current_name, current_ns)
|
|
if schema.type == "object" then
|
|
|
|
if schema.xml then
|
|
if schema.xml.name then
|
|
current_name = schema.xml.name
|
|
end
|
|
if schema.xml.namespace then
|
|
current_ns = schema.xml.namespace
|
|
end
|
|
|
|
end
|
|
|
|
local out = st.stanza(current_name, {xmlns = current_ns})
|
|
|
|
for prop, propschema in pairs(schema.properties) do
|
|
local v = t[prop]
|
|
|
|
if v ~= nil then
|
|
|
|
local proptype, value_where, name, namespace, prefix, single_attribute = unpack_propschema(propschema, prop, current_ns)
|
|
|
|
if value_where == "in_attribute" then
|
|
local attr = name
|
|
if prefix then
|
|
attr = prefix .. ":" .. name
|
|
elseif namespace ~= current_ns then
|
|
attr = namespace .. "\1" .. name
|
|
end
|
|
|
|
if proptype == "string" and type(v) == "string" then
|
|
out.attr[attr] = v
|
|
elseif proptype == "number" and type(v) == "number" then
|
|
out.attr[attr] = string.format("%g", v)
|
|
elseif proptype == "integer" and type(v) == "number" then
|
|
out.attr[attr] = string.format("%d", v)
|
|
elseif proptype == "boolean" then
|
|
out.attr[attr] = v and "1" or "0"
|
|
end
|
|
elseif value_where == "in_text" then
|
|
if type(v) == "string" then
|
|
out:text(v)
|
|
end
|
|
elseif value_where == "in_single_attribute" then
|
|
local propattr = {}
|
|
|
|
if namespace ~= current_ns then
|
|
propattr.xmlns = namespace
|
|
end
|
|
|
|
if proptype == "string" and type(v) == "string" then
|
|
propattr[single_attribute] = v
|
|
elseif proptype == "number" and type(v) == "number" then
|
|
propattr[single_attribute] = string.format("%g", v)
|
|
elseif proptype == "integer" and type(v) == "number" then
|
|
propattr[single_attribute] = string.format("%d", v)
|
|
elseif proptype == "boolean" and type(v) == "boolean" then
|
|
propattr[single_attribute] = v and "1" or "0"
|
|
end
|
|
out:tag(name, propattr):up();
|
|
|
|
else
|
|
local propattr
|
|
if namespace ~= current_ns then
|
|
propattr = {xmlns = namespace}
|
|
end
|
|
if value_where == "in_tag_name" then
|
|
if proptype == "string" and type(v) == "string" then
|
|
out:tag(v, propattr):up();
|
|
elseif proptype == "boolean" and v == true then
|
|
out:tag(name, propattr):up();
|
|
end
|
|
elseif proptype == "string" and type(v) == "string" then
|
|
out:text_tag(name, v, propattr)
|
|
elseif proptype == "number" and type(v) == "number" then
|
|
out:text_tag(name, string.format("%g", v), propattr)
|
|
elseif proptype == "integer" and type(v) == "number" then
|
|
out:text_tag(name, string.format("%d", v), propattr)
|
|
elseif proptype == "boolean" and type(v) == "boolean" then
|
|
out:text_tag(name, v and "1" or "0", propattr)
|
|
elseif proptype == "object" and type(propschema) == "table" and type(v) == "table" then
|
|
local c = unparse(propschema, v, name, namespace);
|
|
if c then
|
|
out:add_direct_child(c);
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return out
|
|
|
|
end
|
|
end
|
|
|
|
return {parse = parse; unparse = unparse}
|
|
|