capricorn 2.0.8 → 2.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. data/erlang/lib/capricorn/ebin/capricorn.app +2 -1
  2. data/erlang/lib/capricorn/include/capricorn.hrl +1 -0
  3. data/erlang/lib/capricorn/src/cap_cluster_gems.erl +6 -1
  4. data/erlang/lib/capricorn/src/cap_console_dispatcher.erl +214 -0
  5. data/erlang/lib/capricorn/src/cap_machine_apps.erl +25 -1
  6. data/erlang/lib/capricorn/src/cap_sup.erl +14 -0
  7. data/erlang/lib/ejson/Makefile +24 -0
  8. data/erlang/lib/ejson/ebin/ejson.app +9 -0
  9. data/erlang/lib/ejson/include/ejson.hrl +40 -0
  10. data/erlang/lib/ejson/rebar.config +3 -0
  11. data/erlang/lib/ejson/src/ejson.erl +22 -0
  12. data/erlang/lib/ejson/src/ejson_decode.erl +337 -0
  13. data/erlang/lib/ejson/src/ejson_encode.erl +124 -0
  14. data/erlang/lib/ejson/test/arrays.escript +47 -0
  15. data/erlang/lib/ejson/test/compound.escript +56 -0
  16. data/erlang/lib/ejson/test/literals.escript +30 -0
  17. data/erlang/lib/ejson/test/numbers.escript +70 -0
  18. data/erlang/lib/ejson/test/objects.escript +51 -0
  19. data/erlang/lib/ejson/test/strings.escript +49 -0
  20. data/erlang/lib/ejson/test/timing.escript +43 -0
  21. data/erlang/lib/ejson/test/timing.json +382 -0
  22. data/erlang/lib/ejson/vendor/mochijson2.erl +621 -0
  23. data/erlang/lib/ejson/vendor/rfc4627.erl +625 -0
  24. data/erlang/lib/misultin/LICENSE.txt +41 -0
  25. data/erlang/lib/misultin/Makefile +26 -0
  26. data/erlang/lib/misultin/README.txt +120 -0
  27. data/erlang/lib/misultin/ebin/misultin.app +9 -0
  28. data/erlang/lib/misultin/examples/misultin_compress.erl +43 -0
  29. data/erlang/lib/misultin/examples/misultin_echo.erl +58 -0
  30. data/erlang/lib/misultin/examples/misultin_file.erl +43 -0
  31. data/erlang/lib/misultin/examples/misultin_gen_server.erl +158 -0
  32. data/erlang/lib/misultin/examples/misultin_get_variable.erl +55 -0
  33. data/erlang/lib/misultin/examples/misultin_hello_world.erl +43 -0
  34. data/erlang/lib/misultin/examples/misultin_rest.erl +68 -0
  35. data/erlang/lib/misultin/examples/misultin_ssl.erl +51 -0
  36. data/erlang/lib/misultin/examples/misultin_stream.erl +55 -0
  37. data/erlang/lib/misultin/examples/misultin_websocket_event_example.erl +103 -0
  38. data/erlang/lib/misultin/examples/misultin_websocket_example.erl +95 -0
  39. data/erlang/lib/misultin/include/misultin.hrl +95 -0
  40. data/erlang/lib/misultin/make.bat +55 -0
  41. data/erlang/lib/misultin/priv/README.txt +12 -0
  42. data/erlang/lib/misultin/priv/test_certificate.pem +21 -0
  43. data/erlang/lib/misultin/priv/test_privkey.pem +18 -0
  44. data/erlang/lib/misultin/rebar.config +3 -0
  45. data/erlang/lib/misultin/src/misultin.app.src +9 -0
  46. data/erlang/lib/misultin/src/misultin.erl +338 -0
  47. data/erlang/lib/misultin/src/misultin_http.erl +488 -0
  48. data/erlang/lib/misultin/src/misultin_req.erl +280 -0
  49. data/erlang/lib/misultin/src/misultin_socket.erl +193 -0
  50. data/erlang/lib/misultin/src/misultin_utility.erl +357 -0
  51. data/erlang/lib/misultin/src/misultin_websocket.erl +252 -0
  52. data/erlang/lib/misultin/src/misultin_ws.erl +78 -0
  53. data/erlang/rebar.config +2 -0
  54. data/erlang/rel/overlay/etc/capricorn/app.config +4 -0
  55. data/erlang/rel/reltool.config +5 -0
  56. data/lib/capricorn/recipes/apache-debian.rb +1 -1
  57. data/lib/capricorn/recipes/centos-plesk.rb +1 -1
  58. data/lib/capricorn/recipes/debian-plesk95.rb +1 -2
  59. data/lib/capricorn/recipes/macports.rb +1 -1
  60. data/lib/capricorn/version.rb +1 -1
  61. metadata +51 -4
@@ -0,0 +1,625 @@
1
+ %% JSON - RFC 4627 - for Erlang
2
+ %%---------------------------------------------------------------------------
3
+ %% @author Tony Garnock-Jones <tonyg@kcbbs.gen.nz>
4
+ %% @author LShift Ltd. <query@lshift.net>
5
+ %% @copyright 2007, 2008 Tony Garnock-Jones and LShift Ltd.
6
+ %% @license
7
+ %%
8
+ %% Permission is hereby granted, free of charge, to any person
9
+ %% obtaining a copy of this software and associated documentation
10
+ %% files (the "Software"), to deal in the Software without
11
+ %% restriction, including without limitation the rights to use, copy,
12
+ %% modify, merge, publish, distribute, sublicense, and/or sell copies
13
+ %% of the Software, and to permit persons to whom the Software is
14
+ %% furnished to do so, subject to the following conditions:
15
+ %%
16
+ %% The above copyright notice and this permission notice shall be
17
+ %% included in all copies or substantial portions of the Software.
18
+ %%
19
+ %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20
+ %% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
+ %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22
+ %% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
23
+ %% BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
24
+ %% ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25
+ %% CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ %% SOFTWARE.
27
+ %%---------------------------------------------------------------------------
28
+ %%
29
+ %% @reference <a href="http://www.ietf.org/rfc/rfc4627.txt">RFC
30
+ %% 4627</a>, the JSON RFC
31
+ %%
32
+ %% @reference <a href="http://www.json.org/">JSON in general</a>
33
+ %%
34
+ %% @reference Joe Armstrong's <a
35
+ %% href="http://www.erlang.org/ml-archive/erlang-questions/200511/msg00193.html">
36
+ %% message</a> describing the basis of the JSON data type mapping that
37
+ %% this module uses
38
+ %%
39
+ %% @doc An implementation of RFC 4627 (JSON, the JavaScript Object Notation) for Erlang.
40
+ %%
41
+ %% The basic API is comprised of the {@link encode/1} and {@link decode/1} functions.
42
+ %%
43
+ %% == Data Type Mapping ==
44
+ %%
45
+ %% The data type mapping I've implemented is as per Joe Armstrong's
46
+ %% message [http://www.erlang.org/ml-archive/erlang-questions/200511/msg00193.html] - see {@link json()}.
47
+ %%
48
+ %% == Unicode ==
49
+ %%
50
+ %% When serializing a string, if characters are found with codepoint
51
+ %% >127, we rely on the unicode encoder to build the proper byte
52
+ %% sequence for transmission. We still use the \uXXXX escape for
53
+ %% control characters (other than the RFC-specified specially
54
+ %% recognised ones).
55
+ %%
56
+ %% {@link decode/1} will autodetect the unicode encoding used, and any
57
+ %% strings returned in the result as binaries will contain UTF-8
58
+ %% encoded byte sequences for codepoints >127. Object keys containing
59
+ %% codepoints >127 will be returned as lists of codepoints, rather
60
+ %% than being UTF-8 encoded. If you have already transformed the text
61
+ %% to parse into a list of unicode codepoints, perhaps by your own use
62
+ %% of {@link unicode_decode/1}, then use {@link decode_noauto/1} to
63
+ %% avoid redundant and erroneous double-unicode-decoding.
64
+ %%
65
+ %% Similarly, {@link encode/1} produces text that is already UTF-8
66
+ %% encoded. To get raw codepoints, use {@link encode_noauto/1} and
67
+ %% {@link encode_noauto/2}. You can use {@link unicode_encode/1} to
68
+ %% UTF-encode the results, if that's appropriate for your application.
69
+ %%
70
+ %% == Differences to the specification ==
71
+ %%
72
+ %% I'm lenient in the following ways during parsing:
73
+ %%
74
+ %% <ul>
75
+ %% <li>repeated commas in arrays and objects collapse to a single comma</li>
76
+ %% <li>any character =&lt;32 is considered whitespace</li>
77
+ %% <li>leading zeros for numbers are accepted</li>
78
+ %% <li>we don't restrict the toplevel token to only object or array -
79
+ %% any JSON value can be used at toplevel</li>
80
+ %% </ul>
81
+
82
+ %% @type json() = jsonobj() | jsonarray() | jsonnum() | jsonstr() | true | false | null. An Erlang representation of a general JSON value.
83
+ %% @type jsonobj() = {obj, [{jsonkey(), json()}]}. A JSON "object" or "struct".
84
+ %% @type jsonkey() = string(). A field-name within a JSON "object".
85
+ %% @type jsonarray() = [json()]. A JSON array value.
86
+ %% @type jsonnum() = integer() | float(). A JSON numeric value.
87
+ %% @type jsonstr() = binary(). A JSON string value.
88
+ %% @type byte() = integer(). An integer >=0 and =&lt;255.
89
+
90
+ -module(rfc4627).
91
+
92
+ -export([mime_type/0, encode/1, decode/1]).
93
+ -export([encode_noauto/1, encode_noauto/2, decode_noauto/1]).
94
+ -export([unicode_decode/1, unicode_encode/1]).
95
+ -export([from_record/3, to_record/3]).
96
+ -export([hex_digit/1, digit_hex/1]).
97
+ -export([get_field/2, get_field/3, set_field/3]).
98
+ -export([equiv/2]).
99
+
100
+ %% @spec () -> string()
101
+ %% @doc Returns the IANA-registered MIME type for JSON data.
102
+ mime_type() ->
103
+ "application/json".
104
+
105
+ %% @spec (json()) -> [byte()]
106
+ %%
107
+ %% @doc Encodes the JSON value supplied, first into Unicode
108
+ %% codepoints, and then into UTF-8.
109
+ %%
110
+ %% The resulting string is a list of byte values that should be
111
+ %% interpreted as UTF-8 encoded text.
112
+ %%
113
+ %% During encoding, atoms and binaries are accepted as keys of JSON
114
+ %% objects (type {@link jsonkey()}) as well as the usual strings
115
+ %% (lists of character codepoints).
116
+ encode(X) ->
117
+ unicode_encode({'utf-8', encode_noauto(X)}).
118
+
119
+ %% @spec (json()) -> string()
120
+ %%
121
+ %% @doc Encodes the JSON value supplied into raw Unicode codepoints.
122
+ %%
123
+ %% The resulting string may contain codepoints with value >=128. You
124
+ %% can use {@link unicode_encode/1} to UTF-encode the results, if
125
+ %% that's appropriate for your application.
126
+ %%
127
+ %% During encoding, atoms and binaries are accepted as keys of JSON
128
+ %% objects (type {@link jsonkey()}) as well as the usual strings
129
+ %% (lists of character codepoints).
130
+ encode_noauto(X) ->
131
+ lists:reverse(encode_noauto(X, [])).
132
+
133
+ %% @spec (json(), string()) -> string()
134
+ %%
135
+ %% @doc As {@link encode_noauto/1}, but prepends <i>reversed</i> text
136
+ %% to the supplied accumulator string.
137
+ encode_noauto(true, Acc) ->
138
+ "eurt" ++ Acc;
139
+ encode_noauto(false, Acc) ->
140
+ "eslaf" ++ Acc;
141
+ encode_noauto(null, Acc) ->
142
+ "llun" ++ Acc;
143
+ encode_noauto(Str, Acc) when is_binary(Str) ->
144
+ Codepoints = xmerl_ucs:from_utf8(Str),
145
+ quote_and_encode_string(Codepoints, Acc);
146
+ encode_noauto(Str, Acc) when is_atom(Str) ->
147
+ quote_and_encode_string(atom_to_list(Str), Acc);
148
+ encode_noauto(Num, Acc) when is_number(Num) ->
149
+ encode_number(Num, Acc);
150
+ encode_noauto({obj, Fields}, Acc) ->
151
+ "}" ++ encode_object(Fields, "{" ++ Acc);
152
+ encode_noauto(Dict, Acc) when element(1, Dict) =:= dict ->
153
+ "}" ++ encode_object(dict:to_list(Dict), "{" ++ Acc);
154
+ encode_noauto(Arr, Acc) when is_list(Arr) ->
155
+ "]" ++ encode_array(Arr, "[" ++ Acc).
156
+
157
+ encode_object([], Acc) ->
158
+ Acc;
159
+ encode_object([{Key, Value}], Acc) ->
160
+ encode_field(Key, Value, Acc);
161
+ encode_object([{Key, Value} | Rest], Acc) ->
162
+ encode_object(Rest, "," ++ encode_field(Key, Value, Acc)).
163
+
164
+ encode_field(Key, Value, Acc) when is_binary(Key) ->
165
+ Codepoints = xmerl_ucs:from_utf8(Key),
166
+ encode_noauto(Value, ":" ++ quote_and_encode_string(Codepoints, Acc));
167
+ encode_field(Key, Value, Acc) when is_atom(Key) ->
168
+ encode_noauto(Value, ":" ++ quote_and_encode_string(atom_to_list(Key), Acc));
169
+ encode_field(Key, Value, Acc) when is_list(Key) ->
170
+ encode_noauto(Value, ":" ++ quote_and_encode_string(Key, Acc)).
171
+
172
+ encode_array([], Acc) ->
173
+ Acc;
174
+ encode_array([X], Acc) ->
175
+ encode_noauto(X, Acc);
176
+ encode_array([X | Rest], Acc) ->
177
+ encode_array(Rest, "," ++ encode_noauto(X, Acc)).
178
+
179
+ quote_and_encode_string(Str, Acc) ->
180
+ "\"" ++ encode_string(Str, "\"" ++ Acc).
181
+
182
+ encode_string([], Acc) ->
183
+ Acc;
184
+ encode_string([$" | Rest], Acc) ->
185
+ encode_string(Rest, [$", $\\ | Acc]);
186
+ encode_string([$\\ | Rest], Acc) ->
187
+ encode_string(Rest, [$\\, $\\ | Acc]);
188
+ encode_string([X | Rest], Acc) when X < 32 orelse X > 127 ->
189
+ encode_string(Rest, encode_general_char(X, Acc));
190
+ encode_string([X | Rest], Acc) ->
191
+ encode_string(Rest, [X | Acc]).
192
+
193
+ encode_general_char(8, Acc) -> [$b, $\\ | Acc];
194
+ encode_general_char(9, Acc) -> [$t, $\\ | Acc];
195
+ encode_general_char(10, Acc) -> [$n, $\\ | Acc];
196
+ encode_general_char(12, Acc) -> [$f, $\\ | Acc];
197
+ encode_general_char(13, Acc) -> [$r, $\\ | Acc];
198
+ encode_general_char(X, Acc) when X > 127 -> [X | Acc];
199
+ encode_general_char(X, Acc) ->
200
+ %% FIXME currently this branch never runs.
201
+ %% We could make it configurable, maybe?
202
+ Utf16Bytes = xmerl_ucs:to_utf16be(X),
203
+ encode_utf16be_chars(Utf16Bytes, Acc).
204
+
205
+ encode_utf16be_chars([], Acc) ->
206
+ Acc;
207
+ encode_utf16be_chars([B1, B2 | Rest], Acc) ->
208
+ encode_utf16be_chars(Rest, [hex_digit((B2) band 16#F),
209
+ hex_digit((B2 bsr 4) band 16#F),
210
+ hex_digit((B1) band 16#F),
211
+ hex_digit((B1 bsr 4) band 16#F),
212
+ $u,
213
+ $\\ | Acc]).
214
+
215
+ %% @spec (Nibble::integer()) -> char()
216
+ %% @doc Returns the character code corresponding to Nibble.
217
+ %%
218
+ %% Nibble must be >=0 and =&lt;16.
219
+ hex_digit(0) -> $0;
220
+ hex_digit(1) -> $1;
221
+ hex_digit(2) -> $2;
222
+ hex_digit(3) -> $3;
223
+ hex_digit(4) -> $4;
224
+ hex_digit(5) -> $5;
225
+ hex_digit(6) -> $6;
226
+ hex_digit(7) -> $7;
227
+ hex_digit(8) -> $8;
228
+ hex_digit(9) -> $9;
229
+ hex_digit(10) -> $A;
230
+ hex_digit(11) -> $B;
231
+ hex_digit(12) -> $C;
232
+ hex_digit(13) -> $D;
233
+ hex_digit(14) -> $E;
234
+ hex_digit(15) -> $F.
235
+
236
+ encode_number(Num, Acc) when is_integer(Num) ->
237
+ lists:reverse(integer_to_list(Num), Acc);
238
+ encode_number(Num, Acc) when is_float(Num) ->
239
+ lists:reverse(float_to_list(Num), Acc).
240
+
241
+ %% @spec (Input::(binary() | [byte()])) -> ({ok, json(), Remainder} | {error, Reason})
242
+ %% where Remainder = string()
243
+ %% Reason = any()
244
+ %%
245
+ %% @doc Decodes a JSON value from an input binary or string of
246
+ %% Unicode-encoded text.
247
+ %%
248
+ %% Given a binary, converts it to a list of bytes. Given a
249
+ %% list/string, interprets it as a list of bytes.
250
+ %%
251
+ %% Uses {@link unicode_decode/1} on its input, which results in a list
252
+ %% of codepoints, and then decodes a JSON value from that list of
253
+ %% codepoints.
254
+ %%
255
+ %% Returns either `{ok, Result, Remainder}', where Remainder is the
256
+ %% remaining portion of the input that was not consumed in the process
257
+ %% of decoding Result, or `{error, Reason}'.
258
+ decode(Bin) when is_binary(Bin) ->
259
+ decode(binary_to_list(Bin));
260
+ decode(Bytes) ->
261
+ {_Charset, Codepoints} = unicode_decode(Bytes),
262
+ decode_noauto(Codepoints).
263
+
264
+ %% @spec (Input::string()) -> ({ok, json(), string()} | {error, any()})
265
+ %%
266
+ %% @doc As {@link decode/1}, but does not perform Unicode decoding on its input.
267
+ %%
268
+ %% Expects a list of codepoints - an ordinary Erlang string - rather
269
+ %% than a list of Unicode-encoded bytes.
270
+ decode_noauto(Bin) when is_binary(Bin) ->
271
+ decode_noauto(binary_to_list(Bin));
272
+ decode_noauto(Chars) ->
273
+ case catch parse(skipws(Chars)) of
274
+ {'EXIT', Reason} ->
275
+ %% Reason is usually far too much information, but helps
276
+ %% if needing to debug this module.
277
+ {error, Reason};
278
+ {Value, Remaining} ->
279
+ {ok, Value, skipws(Remaining)}
280
+ end.
281
+
282
+ %% @spec ([byte()]) -> [char()]
283
+ %%
284
+ %% @doc Autodetects and decodes using the Unicode encoding of its input.
285
+ %%
286
+ %% From RFC4627, section 3, "Encoding":
287
+ %%
288
+ %% <blockquote>
289
+ %% JSON text SHALL be encoded in Unicode. The default encoding is
290
+ %% UTF-8.
291
+ %%
292
+ %% Since the first two characters of a JSON text will always be ASCII
293
+ %% characters [RFC0020], it is possible to determine whether an octet
294
+ %% stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
295
+ %% at the pattern of nulls in the first four octets.
296
+ %%
297
+ %% 00 00 00 xx UTF-32BE
298
+ %% 00 xx 00 xx UTF-16BE
299
+ %% xx 00 00 00 UTF-32LE
300
+ %% xx 00 xx 00 UTF-16LE
301
+ %% xx xx xx xx UTF-8
302
+ %% </blockquote>
303
+ %%
304
+ %% Interestingly, the BOM (byte-order mark) is not mentioned. We
305
+ %% support it here by using it to detect our encoding, discarding it
306
+ %% if present, even though RFC4627 explicitly notes that the first two
307
+ %% characters of a JSON text will be ASCII.
308
+ %%
309
+ %% If a BOM ([http://unicode.org/faq/utf_bom.html]) is present, we use
310
+ %% that; if not, we use RFC4627's rules (as above). Note that UTF-32
311
+ %% is the same as UCS-4 for our purposes (but see also
312
+ %% [http://unicode.org/reports/tr19/tr19-9.html]). Note that UTF-16 is
313
+ %% not the same as UCS-2!
314
+ %%
315
+ %% Note that I'm using xmerl's UCS/UTF support here. There's another
316
+ %% UTF-8 codec in asn1rt, which works on binaries instead of lists.
317
+ %%
318
+ unicode_decode([0,0,254,255|C]) -> {'utf-32', xmerl_ucs:from_ucs4be(C)};
319
+ unicode_decode([255,254,0,0|C]) -> {'utf-32', xmerl_ucs:from_ucs4le(C)};
320
+ unicode_decode([254,255|C]) -> {'utf-16', xmerl_ucs:from_utf16be(C)};
321
+ unicode_decode([239,187,191|C]) -> {'utf-8', xmerl_ucs:from_utf8(C)};
322
+ unicode_decode(C=[0,0,_,_|_]) -> {'utf-32be', xmerl_ucs:from_ucs4be(C)};
323
+ unicode_decode(C=[_,_,0,0|_]) -> {'utf-32le', xmerl_ucs:from_ucs4le(C)};
324
+ unicode_decode(C=[0,_|_]) -> {'utf-16be', xmerl_ucs:from_utf16be(C)};
325
+ unicode_decode(C=[_,0|_]) -> {'utf-16le', xmerl_ucs:from_utf16le(C)};
326
+ unicode_decode(C=_) -> {'utf-8', xmerl_ucs:from_utf8(C)}.
327
+
328
+ %% @spec (EncodingAndCharacters::{Encoding, [char()]}) -> [byte()]
329
+ %% where Encoding = 'utf-32' | 'utf-32be' | 'utf-32le' | 'utf-16' |
330
+ %% 'utf-16be' | 'utf-16le' | 'utf-8'
331
+ %%
332
+ %% @doc Encodes the given characters to bytes, using the given Unicode encoding.
333
+ %%
334
+ %% For convenience, we supply a partial inverse of unicode_decode; If
335
+ %% a BOM is requested, we more-or-less arbitrarily pick the big-endian
336
+ %% variant of the encoding, since big-endian is network-order. We
337
+ %% don't support UTF-8 with BOM here.
338
+ unicode_encode({'utf-32', C}) -> [0,0,254,255|xmerl_ucs:to_ucs4be(C)];
339
+ unicode_encode({'utf-32be', C}) -> xmerl_ucs:to_ucs4be(C);
340
+ unicode_encode({'utf-32le', C}) -> xmerl_ucs:to_ucs4le(C);
341
+ unicode_encode({'utf-16', C}) -> [254,255|xmerl_ucs:to_utf16be(C)];
342
+ unicode_encode({'utf-16be', C}) -> xmerl_ucs:to_utf16be(C);
343
+ unicode_encode({'utf-16le', C}) -> xmerl_ucs:to_utf16le(C);
344
+ unicode_encode({'utf-8', C}) -> xmerl_ucs:to_utf8(C).
345
+
346
+ parse([$" | Rest]) -> %% " emacs balancing
347
+ {Codepoints, Rest1} = parse_string(Rest, []),
348
+ {list_to_binary(xmerl_ucs:to_utf8(Codepoints)), Rest1};
349
+ parse("true" ++ Rest) -> {true, Rest};
350
+ parse("false" ++ Rest) -> {false, Rest};
351
+ parse("null" ++ Rest) -> {null, Rest};
352
+ parse([${ | Rest]) -> parse_object(skipws(Rest), []);
353
+ parse([$[ | Rest]) -> parse_array(skipws(Rest), []);
354
+ parse([]) -> exit(unexpected_end_of_input);
355
+ parse(Chars) -> parse_number(Chars, []).
356
+
357
+ skipws([X | Rest]) when X =< 32 ->
358
+ skipws(Rest);
359
+ skipws(Chars) ->
360
+ Chars.
361
+
362
+ parse_string(Chars, Acc) ->
363
+ case parse_codepoint(Chars) of
364
+ {done, Rest} ->
365
+ {lists:reverse(Acc), Rest};
366
+ {ok, Codepoint, Rest} ->
367
+ parse_string(Rest, [Codepoint | Acc])
368
+ end.
369
+
370
+ parse_codepoint([$" | Rest]) -> %% " emacs balancing
371
+ {done, Rest};
372
+ parse_codepoint([$\\, Key | Rest]) ->
373
+ parse_general_char(Key, Rest);
374
+ parse_codepoint([X | Rest]) ->
375
+ {ok, X, Rest}.
376
+
377
+ parse_general_char($b, Rest) -> {ok, 8, Rest};
378
+ parse_general_char($t, Rest) -> {ok, 9, Rest};
379
+ parse_general_char($n, Rest) -> {ok, 10, Rest};
380
+ parse_general_char($f, Rest) -> {ok, 12, Rest};
381
+ parse_general_char($r, Rest) -> {ok, 13, Rest};
382
+ parse_general_char($/, Rest) -> {ok, $/, Rest};
383
+ parse_general_char($\\, Rest) -> {ok, $\\, Rest};
384
+ parse_general_char($", Rest) -> {ok, $", Rest};
385
+ parse_general_char($u, [D0, D1, D2, D3 | Rest]) ->
386
+ Codepoint =
387
+ (digit_hex(D0) bsl 12) +
388
+ (digit_hex(D1) bsl 8) +
389
+ (digit_hex(D2) bsl 4) +
390
+ (digit_hex(D3)),
391
+ if
392
+ Codepoint >= 16#D800 andalso Codepoint < 16#DC00 ->
393
+ % High half of surrogate pair
394
+ case parse_codepoint(Rest) of
395
+ {low_surrogate_pair, Codepoint2, Rest1} ->
396
+ [FinalCodepoint] =
397
+ xmerl_ucs:from_utf16be(<<Codepoint:16/big-unsigned-integer,
398
+ Codepoint2:16/big-unsigned-integer>>),
399
+ {ok, FinalCodepoint, Rest1};
400
+ _ ->
401
+ exit(incorrect_usage_of_surrogate_pair)
402
+ end;
403
+ Codepoint >= 16#DC00 andalso Codepoint < 16#E000 ->
404
+ {low_surrogate_pair, Codepoint, Rest};
405
+ true ->
406
+ {ok, Codepoint, Rest}
407
+ end.
408
+
409
+ %% @spec (Hexchar::char()) -> integer()
410
+ %% @doc Returns the number corresponding to Hexchar.
411
+ %%
412
+ %% Hexchar must be one of the characters `$0' through `$9', `$A'
413
+ %% through `$F' or `$a' through `$f'.
414
+ digit_hex($0) -> 0;
415
+ digit_hex($1) -> 1;
416
+ digit_hex($2) -> 2;
417
+ digit_hex($3) -> 3;
418
+ digit_hex($4) -> 4;
419
+ digit_hex($5) -> 5;
420
+ digit_hex($6) -> 6;
421
+ digit_hex($7) -> 7;
422
+ digit_hex($8) -> 8;
423
+ digit_hex($9) -> 9;
424
+
425
+ digit_hex($A) -> 10;
426
+ digit_hex($B) -> 11;
427
+ digit_hex($C) -> 12;
428
+ digit_hex($D) -> 13;
429
+ digit_hex($E) -> 14;
430
+ digit_hex($F) -> 15;
431
+
432
+ digit_hex($a) -> 10;
433
+ digit_hex($b) -> 11;
434
+ digit_hex($c) -> 12;
435
+ digit_hex($d) -> 13;
436
+ digit_hex($e) -> 14;
437
+ digit_hex($f) -> 15.
438
+
439
+ finish_number(Acc, Rest) ->
440
+ Str = lists:reverse(Acc),
441
+ {case catch list_to_integer(Str) of
442
+ {'EXIT', _} -> list_to_float(Str);
443
+ Value -> Value
444
+ end, Rest}.
445
+
446
+ parse_number([$- | Rest], Acc) ->
447
+ parse_number1(Rest, [$- | Acc]);
448
+ parse_number(Rest = [C | _], Acc) ->
449
+ case is_digit(C) of
450
+ true -> parse_number1(Rest, Acc);
451
+ false -> exit(syntax_error)
452
+ end.
453
+
454
+ parse_number1(Rest, Acc) ->
455
+ {Acc1, Rest1} = parse_int_part(Rest, Acc),
456
+ case Rest1 of
457
+ [] -> finish_number(Acc1, []);
458
+ [$. | More] ->
459
+ {Acc2, Rest2} = parse_int_part(More, [$. | Acc1]),
460
+ parse_exp(Rest2, Acc2, false);
461
+ _ ->
462
+ parse_exp(Rest1, Acc1, true)
463
+ end.
464
+
465
+ parse_int_part(Chars = [_Ch | _Rest], Acc) ->
466
+ parse_int_part0(Chars, Acc).
467
+
468
+ parse_int_part0([], Acc) ->
469
+ {Acc, []};
470
+ parse_int_part0([Ch | Rest], Acc) ->
471
+ case is_digit(Ch) of
472
+ true -> parse_int_part0(Rest, [Ch | Acc]);
473
+ false -> {Acc, [Ch | Rest]}
474
+ end.
475
+
476
+ parse_exp([$e | Rest], Acc, NeedFrac) ->
477
+ parse_exp1(Rest, Acc, NeedFrac);
478
+ parse_exp([$E | Rest], Acc, NeedFrac) ->
479
+ parse_exp1(Rest, Acc, NeedFrac);
480
+ parse_exp(Rest, Acc, _NeedFrac) ->
481
+ finish_number(Acc, Rest).
482
+
483
+ parse_exp1(Rest, Acc, NeedFrac) ->
484
+ {Acc1, Rest1} = parse_signed_int_part(Rest, if
485
+ NeedFrac -> [$e, $0, $. | Acc];
486
+ true -> [$e | Acc]
487
+ end),
488
+ finish_number(Acc1, Rest1).
489
+
490
+ parse_signed_int_part([$+ | Rest], Acc) ->
491
+ parse_int_part(Rest, [$+ | Acc]);
492
+ parse_signed_int_part([$- | Rest], Acc) ->
493
+ parse_int_part(Rest, [$- | Acc]);
494
+ parse_signed_int_part(Rest, Acc) ->
495
+ parse_int_part(Rest, Acc).
496
+
497
+ is_digit($0) -> true;
498
+ is_digit($1) -> true;
499
+ is_digit($2) -> true;
500
+ is_digit($3) -> true;
501
+ is_digit($4) -> true;
502
+ is_digit($5) -> true;
503
+ is_digit($6) -> true;
504
+ is_digit($7) -> true;
505
+ is_digit($8) -> true;
506
+ is_digit($9) -> true;
507
+ is_digit(_) -> false.
508
+
509
+ parse_object([$} | Rest], Acc) ->
510
+ {{obj, lists:reverse(Acc)}, Rest};
511
+ parse_object([$, | Rest], Acc) ->
512
+ parse_object(skipws(Rest), Acc);
513
+ parse_object([$" | Rest], Acc) -> %% " emacs balancing
514
+ {KeyCodepoints, Rest1} = parse_string(Rest, []),
515
+ [$: | Rest2] = skipws(Rest1),
516
+ {Value, Rest3} = parse(skipws(Rest2)),
517
+ parse_object(skipws(Rest3), [{KeyCodepoints, Value} | Acc]).
518
+
519
+ parse_array([$] | Rest], Acc) ->
520
+ {lists:reverse(Acc), Rest};
521
+ parse_array([$, | Rest], Acc) ->
522
+ parse_array(skipws(Rest), Acc);
523
+ parse_array(Chars, Acc) ->
524
+ {Value, Rest} = parse(Chars),
525
+ parse_array(skipws(Rest), [Value | Acc]).
526
+
527
+ %% @spec (Record, atom(), [any()]) -> jsonobj()
528
+ %% where Record = tuple()
529
+ %%
530
+ %% @doc Used by the `?RFC4627_FROM_RECORD' macro in `rfc4627.hrl'.
531
+ %%
532
+ %% Given a record type definiton of ``-record(myrecord, {field1,
533
+ %% field})'', and a value ``V = #myrecord{}'', the code
534
+ %% ``?RFC4627_FROM_RECORD(myrecord, V)'' will return a JSON "object"
535
+ %% with fields corresponding to the fields of the record. The macro
536
+ %% expands to a call to the `from_record' function.
537
+ from_record(R, _RecordName, Fields) ->
538
+ {obj, encode_record_fields(R, 2, Fields)}.
539
+
540
+ encode_record_fields(_R, _Index, []) ->
541
+ [];
542
+ encode_record_fields(R, Index, [Field | Rest]) ->
543
+ case element(Index, R) of
544
+ undefined ->
545
+ encode_record_fields(R, Index + 1, Rest);
546
+ Value ->
547
+ [{atom_to_list(Field), Value} | encode_record_fields(R, Index + 1, Rest)]
548
+ end.
549
+
550
+ %% @spec (JsonObject::jsonobj(), DefaultValue::Record, [atom()]) -> Record
551
+ %% where Record = tuple()
552
+ %%
553
+ %% @doc Used by the `?RFC4627_TO_RECORD' macro in `rfc4627.hrl'.
554
+ %%
555
+ %% Given a record type definiton of ``-record(myrecord, {field1,
556
+ %% field})'', and a JSON "object" ``J = {obj, [{"field1", 123},
557
+ %% {"field2", 234}]}'', the code ``?RFC4627_TO_RECORD(myrecord, J)''
558
+ %% will return a record ``#myrecord{field1 = 123, field2 = 234}''.
559
+ %% The macro expands to a call to the `to_record' function.
560
+ to_record({obj, Values}, Fallback, Fields) ->
561
+ list_to_tuple([element(1, Fallback) | decode_record_fields(Values, Fallback, 2, Fields)]).
562
+
563
+ decode_record_fields(_Values, _Fallback, _Index, []) ->
564
+ [];
565
+ decode_record_fields(Values, Fallback, Index, [Field | Rest]) ->
566
+ [case lists:keysearch(atom_to_list(Field), 1, Values) of
567
+ {value, {_, Value}} ->
568
+ Value;
569
+ false ->
570
+ element(Index, Fallback)
571
+ end | decode_record_fields(Values, Fallback, Index + 1, Rest)].
572
+
573
+ %% @spec (JsonObject::jsonobj(), atom()) -> {ok, json()} | not_found
574
+ %% @doc Retrieves the value of a named field of a JSON "object".
575
+ get_field({obj, Props}, Key) ->
576
+ case lists:keysearch(Key, 1, Props) of
577
+ {value, {_K, Val}} ->
578
+ {ok, Val};
579
+ false ->
580
+ not_found
581
+ end.
582
+
583
+ %% @spec (jsonobj(), atom(), json()) -> json()
584
+ %% @doc Retrieves the value of a named field of a JSON "object", or a
585
+ %% default value if no such field is present.
586
+ get_field(Obj, Key, DefaultValue) ->
587
+ case get_field(Obj, Key) of
588
+ {ok, Val} ->
589
+ Val;
590
+ not_found ->
591
+ DefaultValue
592
+ end.
593
+
594
+ %% @spec (JsonObject::jsonobj(), atom(), json()) -> jsonobj()
595
+ %% @doc Adds or replaces a named field with the given value.
596
+ %%
597
+ %% Returns a JSON "object" that contains the new field value as well
598
+ %% as all the unmodified fields from the first argument.
599
+ set_field({obj, Props}, Key, NewValue) ->
600
+ {obj, [{Key, NewValue} | lists:keydelete(Key, 1, Props)]}.
601
+
602
+ %% @spec (A::json(), B::json()) -> bool()
603
+ %% @doc Tests equivalence of JSON terms.
604
+ %%
605
+ %% After Bob Ippolito's `equiv' predicate in mochijson.
606
+ equiv({obj, Props1}, {obj, Props2}) ->
607
+ L1 = lists:keysort(1, Props1),
608
+ L2 = lists:keysort(1, Props2),
609
+ equiv_sorted_plists(L1, L2);
610
+ equiv(A, B) when is_list(A) andalso is_list(B) ->
611
+ equiv_arrays(A, B);
612
+ equiv(A, B) ->
613
+ A == B.
614
+
615
+ equiv_sorted_plists([], []) -> true;
616
+ equiv_sorted_plists([], _) -> false;
617
+ equiv_sorted_plists(_, []) -> false;
618
+ equiv_sorted_plists([{K1, V1} | R1], [{K2, V2} | R2]) ->
619
+ K1 == K2 andalso equiv(V1, V2) andalso equiv_sorted_plists(R1, R2).
620
+
621
+ equiv_arrays([], []) -> true;
622
+ equiv_arrays([], _) -> false;
623
+ equiv_arrays(_, []) -> false;
624
+ equiv_arrays([V1 | R1], [V2 | R2]) ->
625
+ equiv(V1, V2) andalso equiv_arrays(R1, R2).