teal-src/util/datamapper.tl
changeset 11465 766b0eddd12c
parent 11463 86904555bffc
child 11466 d1982b7eb00d
equal deleted inserted replaced
11464:a8b4e04bc044 11465:766b0eddd12c
    19 -- TODO cleanup / refactor
    19 -- TODO cleanup / refactor
    20 -- TODO s/number/integer/ once we have appropriate math.type() compat
    20 -- TODO s/number/integer/ once we have appropriate math.type() compat
    21 --
    21 --
    22 
    22 
    23 local st = require "util.stanza";
    23 local st = require "util.stanza";
    24 local js = require "util.jsonschema"
    24 local json = require"util.json"
       
    25 
       
    26 local json_type_name = json.json_type_name;
       
    27 local json_schema_object = require "util.jsonschema"
       
    28 local type schema_t = boolean | json_type_name | json_schema_object
    25 
    29 
    26 local function toboolean ( s : string ) : boolean
    30 local function toboolean ( s : string ) : boolean
    27 	if s == "true" or s == "1" then
    31 	if s == "true" or s == "1" then
    28 		return true
    32 		return true
    29 	elseif s == "false" or s == "0" then
    33 	elseif s == "false" or s == "0" then
    31 	elseif s then
    35 	elseif s then
    32 		return true
    36 		return true
    33 	end
    37 	end
    34 end
    38 end
    35 
    39 
    36 local function totype(t : js.schema_t.type_e, s : string) : any
    40 local function totype(t : json_type_name, s : string) : any
    37 	if t == "string" then
    41 	if t == "string" then
    38 		return s;
    42 		return s;
    39 	elseif t == "boolean" then
    43 	elseif t == "boolean" then
    40 		return toboolean(s)
    44 		return toboolean(s)
    41 	elseif t == "number" or t == "integer" then
    45 	elseif t == "number" or t == "integer" then
    51 	"in_single_attribute"
    55 	"in_single_attribute"
    52 	"in_children"
    56 	"in_children"
    53 	"in_wrapper"
    57 	"in_wrapper"
    54 end
    58 end
    55 
    59 
    56 local function unpack_propschema( propschema : js.schema_t | js.schema_t.type_e, propname : string, current_ns : string )
    60 local function unpack_propschema( propschema : schema_t, propname : string, current_ns : string )
    57 		: js.schema_t.type_e, value_goes, string, string, string, string, { any }
    61 		: json_type_name, value_goes, string, string, string, string, { any }
    58 	local proptype : js.schema_t.type_e = "string"
    62 	local proptype : json_type_name = "string"
    59 	local value_where : value_goes = "in_text_tag"
    63 	local value_where : value_goes = "in_text_tag"
    60 	local name = propname
    64 	local name = propname
    61 	local namespace = current_ns
    65 	local namespace = current_ns
    62 	local prefix : string
    66 	local prefix : string
    63 	local single_attribute : string
    67 	local single_attribute : string
    64 	local enums : { any }
    68 	local enums : { any }
    65 
    69 
    66 	if propschema is js.schema_t then
    70 	if propschema is json_schema_object then
    67 		proptype = propschema.type
    71 		proptype = propschema.type
    68 	elseif propschema is js.schema_t.type_e then
    72 	elseif propschema is json_type_name then
    69 		proptype = propschema
    73 		proptype = propschema
    70 	end
    74 	end
    71 
    75 
    72 	if proptype == "object" or proptype == "array" then
    76 	if proptype == "object" or proptype == "array" then
    73 		value_where = "in_children"
    77 		value_where = "in_children"
    74 	end
    78 	end
    75 
    79 
    76 	if propschema is js.schema_t then
    80 	if propschema is json_schema_object then
    77 		local xml = propschema.xml
    81 		local xml = propschema.xml
    78 		if xml then
    82 		if xml then
    79 			if xml.name then
    83 			if xml.name then
    80 				name = xml.name
    84 				name = xml.name
    81 			end
    85 			end
   106 	end
   110 	end
   107 
   111 
   108 	return proptype, value_where, name, namespace, prefix, single_attribute, enums
   112 	return proptype, value_where, name, namespace, prefix, single_attribute, enums
   109 end
   113 end
   110 
   114 
   111 local parse_object : function (schema : js.schema_t, s : st.stanza_t) : { string : any }
   115 local parse_object : function (schema : schema_t, s : st.stanza_t) : { string : any }
   112 local parse_array : function (schema : js.schema_t, s : st.stanza_t) : { any }
   116 local parse_array : function (schema : schema_t, s : st.stanza_t) : { any }
   113 
   117 
   114 function parse_object (schema : js.schema_t, s : st.stanza_t) : { string : any }
   118 function parse_object (schema : schema_t, s : st.stanza_t) : { string : any }
   115 	local out : { string : any } = {}
   119 	local out : { string : any } = {}
   116 	if schema.properties then
   120 	if schema is json_schema_object and schema.properties then
   117 		for prop, propschema in pairs(schema.properties) do
   121 		for prop, propschema in pairs(schema.properties) do
   118 
   122 
   119 			local proptype, value_where, name, namespace, prefix, single_attribute, enums = unpack_propschema(propschema, prop, s.attr.xmlns)
   123 			local proptype, value_where, name, namespace, prefix, single_attribute, enums = unpack_propschema(propschema, prop, s.attr.xmlns)
   120 
   124 
   121 			local value : string
   125 			local value : string
   150 			elseif value_where == "in_single_attribute" then
   154 			elseif value_where == "in_single_attribute" then
   151 				local c = s:get_child(name, namespace)
   155 				local c = s:get_child(name, namespace)
   152 				value = c and c.attr[single_attribute]
   156 				value = c and c.attr[single_attribute]
   153 			elseif value_where == "in_text_tag" then
   157 			elseif value_where == "in_text_tag" then
   154 				value = s:get_child_text(name, namespace)
   158 				value = s:get_child_text(name, namespace)
   155 			elseif value_where == "in_children" and propschema is js.schema_t then
   159 			elseif value_where == "in_children" and propschema is json_schema_object then
   156 				if proptype == "object" then
   160 				if proptype == "object" then
   157 					local c = s:get_child(name, namespace)
   161 					local c = s:get_child(name, namespace)
   158 					if c then
   162 					if c then
   159 						out[prop] = parse_object(propschema, c);
   163 						out[prop] = parse_object(propschema, c);
   160 					end
   164 					end
   161 				elseif proptype == "array" then
   165 				elseif proptype == "array" then
   162 					out[prop] = parse_array(propschema, s);
   166 					out[prop] = parse_array(propschema, s);
   163 				else
   167 				else
   164 					error "unreachable"
   168 					error "unreachable"
   165 				end
   169 				end
   166 			elseif value_where == "in_wrapper" and propschema is js.schema_t and proptype == "array" then
   170 			elseif value_where == "in_wrapper" and propschema is json_schema_object and proptype == "array" then
   167 				local wrapper = s:get_child(name, namespace);
   171 				local wrapper = s:get_child(name, namespace);
   168 				if wrapper then
   172 				if wrapper then
   169 					out[prop] = parse_array(propschema, wrapper);
   173 					out[prop] = parse_array(propschema, wrapper);
   170 				else
   174 				else
   171 					error "unreachable"
   175 					error "unreachable"
   172 			end
   176 				end
   173 			else
   177 			else
   174 				error "unreachable"
   178 				error "unreachable"
   175 			end
   179 			end
   176 			if value_where ~= "in_children" and value_where ~= "in_wrapper" then
   180 			if value_where ~= "in_children" and value_where ~= "in_wrapper" then
   177 				out[prop] = totype(proptype, value)
   181 				out[prop] = totype(proptype, value)
   180 	end
   184 	end
   181 
   185 
   182 	return out
   186 	return out
   183 end
   187 end
   184 
   188 
   185 function parse_array (schema : js.schema_t, s : st.stanza_t) : { any }
   189 function parse_array (schema : json_schema_object, s : st.stanza_t) : { any }
   186 	local proptype, value_where, child_name, namespace = unpack_propschema(schema.items, nil, s.attr.xmlns)
   190 	local proptype, value_where, child_name, namespace = unpack_propschema(schema.items, nil, s.attr.xmlns)
   187 	local out : { any } = {}
   191 	local out : { any } = {}
   188 	for c in s:childtags(child_name, namespace) do
   192 	for c in s:childtags(child_name, namespace) do
   189 		local value : string;
   193 		local value : string;
   190 		if value_where == "in_text_tag" then
   194 		if value_where == "in_text_tag" then
   200 		end
   204 		end
   201 	end
   205 	end
   202 	return out;
   206 	return out;
   203 end
   207 end
   204 
   208 
   205 local function parse (schema : js.schema_t, s : st.stanza_t) : table
   209 local function parse (schema : json_schema_object, s : st.stanza_t) : table
   206 	if schema.type == "object" then
   210 	if schema.type == "object" then
   207 		return parse_object(schema, s)
   211 		return parse_object(schema, s)
   208 	elseif schema.type == "array" then
   212 	elseif schema.type == "array" then
   209 		return parse_array(schema, s)
   213 		return parse_array(schema, s)
   210 	else
   214 	else
   211 		error "top-level scalars unsupported"
   215 		error "top-level scalars unsupported"
   212 	end
   216 	end
   213 end
   217 end
   214 
   218 
   215 local function unparse ( schema : js.schema_t, t : table, current_name : string, current_ns : string ) : st.stanza_t
   219 local function unparse ( schema : json_schema_object, t : table, current_name : string, current_ns : string ) : st.stanza_t
   216 
   220 
   217 		if schema.xml then
   221 	if schema.xml then
   218 			if schema.xml.name then
   222 		if schema.xml.name then
   219 				current_name = schema.xml.name
   223 			current_name = schema.xml.name
   220 			end
   224 		end
   221 			if schema.xml.namespace then
   225 		if schema.xml.namespace then
   222 				current_ns = schema.xml.namespace
   226 			current_ns = schema.xml.namespace
   223 			end
   227 		end
   224 			-- TODO prefix?
   228 		-- TODO prefix?
   225 		end
   229 	end
   226 
   230 
   227 		local out = st.stanza(current_name, { xmlns = current_ns })
   231 	local out = st.stanza(current_name, { xmlns = current_ns })
   228 
   232 
   229 	if schema.type == "object" then
   233 	if schema.type == "object" then
   230 
   234 
   231 		for prop, propschema in pairs(schema.properties) do
   235 		for prop, propschema in pairs(schema.properties) do
   232 			local v = t[prop]
   236 			local v = t[prop]
   291 						out:text_tag(name, string.format("%g", v), propattr)
   295 						out:text_tag(name, string.format("%g", v), propattr)
   292 					elseif proptype == "integer" and v is number then -- TODO is integer
   296 					elseif proptype == "integer" and v is number then -- TODO is integer
   293 						out:text_tag(name, string.format("%d", v), propattr)
   297 						out:text_tag(name, string.format("%d", v), propattr)
   294 					elseif proptype == "boolean" and v is boolean then
   298 					elseif proptype == "boolean" and v is boolean then
   295 						out:text_tag(name, v and "1" or "0", propattr)
   299 						out:text_tag(name, v and "1" or "0", propattr)
   296 					elseif proptype == "object" and propschema is js.schema_t and v is table then
   300 					elseif proptype == "object" and propschema is json_schema_object and v is table then
   297 						local c = unparse(propschema, v, name, namespace);
   301 						local c = unparse(propschema, v, name, namespace);
   298 						if c then
   302 						if c then
   299 							out:add_direct_child(c);
   303 							out:add_direct_child(c);
   300 						end
   304 						end
   301 					elseif proptype == "array" and propschema is js.schema_t and v is table then
   305 					elseif proptype == "array" and propschema is json_schema_object and v is table then
   302 						local c = unparse(propschema, v, name, namespace);
   306 						local c = unparse(propschema, v, name, namespace);
   303 						if c then
   307 						if c then
   304 							if value_where == "in_wrapper" then
   308 							if value_where == "in_wrapper" then
   305 								local w = st.stanza(propschema.xml.name or name, { xmlns = propschema.xml.namespace or namespace })
   309 								local w = st.stanza(propschema.xml.name or name, { xmlns = propschema.xml.namespace or namespace })
   306 								w:add_direct_child(c);
   310 								w:add_direct_child(c);