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 |
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); |