capricorn 2.0.8 → 2.0.9

Sign up to get free protection for your applications and to get access to all the features.
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).