1% -*- mode: slang; mode: fold -*-
2
3import("json");
4
5%{{{ Type Handlers for json_encode
6
7private variable Type_Map = Ref_Type[0];
8
9private define add_type_handler ()
10{
11   variable func = ();
12   loop (_NARGS-1)
13     {
14	variable type = ();
15	variable idx = __class_id (type);
16	variable n = length (Type_Map);
17	if (idx >= n)
18	  {
19	     variable new_map = Ref_Type[idx+1];
20	     new_map[[0:n-1]] = Type_Map;
21	     Type_Map = new_map;
22	  }
23	Type_Map[idx] = func;
24     }
25}
26
27private define get_encode_func (type)
28{
29   try
30     {
31	variable func = Type_Map[__class_id (type)];
32	if (func == NULL) throw IndexError;
33	return func;
34     }
35   catch IndexError:
36     throw Json_Invalid_Json_Error, "$type does not represent a JSON data structure"$;
37}
38%}}}
39
40private define json_encode_string (indent, q, data) %{{{
41{
42   return _json_encode_string (data);
43}
44add_type_handler (String_Type, BString_Type, &json_encode_string);
45%}}}
46
47private define json_encode_boolean (indent, q, data) %{{{
48{
49   if (data == 1) return "true"B;
50   if (data == 0) return "false"B;
51   throw Json_Invalid_Json_Error, sprintf (`invalid boolean value '\%03o'; only '\000' and '\001' are allowed`, data);
52}
53add_type_handler (UChar_Type, &json_encode_boolean);
54%}}}
55
56private define json_encode_null (indent, q, data) %{{{
57{
58   return "null"B;
59}
60add_type_handler (Null_Type, &json_encode_null);
61%}}}
62
63private define json_encode_number (indent, q, data) %{{{
64{
65   return string (data);
66}
67add_type_handler (
68	Char_Type, % UChar_Type,
69	Short_Type, UShort_Type,
70	Int_Type, UInt_Type,
71	Long_Type, ULong_Type,
72#ifexists LLong_Type
73	LLong_Type, ULLong_Type,
74#endif
75	Float_Type, Double_Type,
76	&json_encode_number);
77%}}}
78
79private define json_encode_object (indent, q, object) %{{{
80{
81   variable json = "{"B;
82   variable keys = get_struct_field_names (object);
83   variable n_values = length (keys);
84   if (n_values)
85     {
86	% pvs indent KEY nsep VAL vsep pvs indent KEY nsep VAL vsep
87	% ... pvs indent KEY nsep VAL pvs
88
89	variable new_indent = bstrcat (indent, q.indent);
90	variable sep = bstrcat (q.vsep, q.post_vsep, new_indent);
91	variable nsep = q.nsep;
92
93	variable key = keys[0];
94	variable val = get_struct_field(object, key);
95	variable type = typeof (val);
96	variable func = get_encode_func (type);
97	json = bstrcat (__tmp(json), q.post_vsep, new_indent,
98			_json_encode_string (key), nsep, (@func)(new_indent, q, val));
99
100	variable i;
101	_for i (1, n_values-1)
102	  {
103	     key = keys[i];
104	     val = get_struct_field(object, key);
105	     variable next_type = typeof (val);
106	     if (next_type == String_Type)
107	       {
108		  json = bstrcat (__tmp(json), sep, _json_encode_string (key),
109				  nsep, _json_encode_string (val));
110		  continue;
111	       }
112
113	     if (next_type != type)
114	       (type, func) = (next_type, get_encode_func (next_type));
115	     json = bstrcat (__tmp(json), sep, _json_encode_string (key),
116			     nsep, (@func)(new_indent, q, val));
117          }
118	json = bstrcat (__tmp(json), q.post_vsep);
119     }
120   return bstrcat (__tmp(json), indent, "}");
121}
122add_type_handler (Struct_Type, &json_encode_object);
123%}}}
124
125private define json_encode_array (indent, q, array) %{{{
126{
127   variable json = "["B;
128   variable n_values = length (array);
129   if (n_values)
130     {
131	%  pvs, new_indent, VALUE, vsep, pvs, new_indent, VALUE, vsep, pvs, ..., new_indent, VALUE, pvs
132
133	variable new_indent = bstrcat (indent, q.indent);
134	variable sep = bstrcat (q.vsep, q.post_vsep, new_indent);
135
136	variable i = 0;
137	variable a = array[i];
138	variable type = typeof (a);
139	variable func = get_encode_func (type);
140	json = bstrcat (__tmp(json), q.post_vsep,
141			new_indent, (@func)(new_indent, q, a));
142
143	if ((typeof (array) == Array_Type) && not any (_isnull (array)))
144	  {
145	     if (type == String_Type)
146	       _for i (1, n_values-1)
147		 json = bstrcat (__tmp(json), sep, _json_encode_string (array[i]));
148	     else
149	       _for i (1, n_values-1)
150		 json = bstrcat (__tmp(json), sep, (@func)(new_indent, q, array[i]));
151	  }
152	else _for i (1, n_values-1)
153	  {
154	     a = array[i];
155	     variable next_type = typeof (a);
156	     if (next_type == String_Type)
157	       {
158		  json = bstrcat (__tmp(json), sep, _json_encode_string (a));
159		  continue;
160	       }
161
162	     if (next_type != type)
163	       (type, func) = (next_type, get_encode_func (next_type));
164	     json = bstrcat (__tmp(json), sep, (@func)(new_indent, q, a));
165	  }
166
167	json = bstrcat (__tmp(json), q.post_vsep);
168     }
169   return bstrcat (__tmp(json), indent, "]");
170}
171add_type_handler (List_Type, Array_Type, &json_encode_array);
172%}}}
173
174private define default_handler (indent, q, data) %{{{
175{
176   if (0 < __is_numeric (data) < 3)
177     return json_encode_number (data);
178
179   if (is_struct_type (data))
180     return json_encode_object (data);
181
182   variable type = _typeof (data);
183   throw Json_Invalid_Json_Error, "$type does not represent a JSON data structure"$;
184}
185Type_Map[where (_isnull (Type_Map))] = &default_handler;
186%}}}
187
188% process_qualifiers %{{{
189
190private define split_post_vsep (post_vsep) %{{{
191{
192   variable indent = "";
193   variable parts = strchop (post_vsep, '\n', 0);
194   if (length (parts) > 1)
195     {
196	indent = parts[-1];
197	parts[-1] = "";
198	post_vsep = strjoin (parts, "\n");
199     }
200  return (indent, post_vsep);
201}
202%}}}
203
204private define only_whitespace (s)
205{
206   return ""B + str_delete_chars (s, "^ \t\n\r");
207}
208
209private define process_qualifiers ()
210{
211   variable post_vsep = split_post_vsep (qualifier ("post_vsep", ""));
212   variable indent = ();  % other return value from split_post_vsep
213   variable q = struct {
214      pre_nsep  = only_whitespace (qualifier ("pre_nsep", "")),
215      post_nsep = only_whitespace (qualifier ("post_nsep", "")),
216      pre_vsep  = only_whitespace (qualifier ("pre_vsep", "")),
217      post_vsep = only_whitespace (post_vsep),
218      indent    = only_whitespace (indent),
219   };
220   return struct {
221      nsep = q.pre_nsep + ":" + q.post_nsep,
222      vsep = q.pre_vsep + ",",
223      @q
224   };
225}
226%}}}
227
228define json_encode (data)
229{
230   variable q = process_qualifiers (;; __qualifiers);
231   variable func = get_encode_func (typeof (data));
232   variable json = (@func)(""B, q, data);
233   return typecast (json, String_Type);
234}
235