json 1.8.6 → 2.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +5 -5
  2. data/{CHANGES → CHANGES.md} +292 -96
  3. data/LICENSE +56 -0
  4. data/README.md +185 -114
  5. data/ext/json/ext/fbuffer/fbuffer.h +0 -3
  6. data/ext/json/ext/generator/generator.c +328 -117
  7. data/ext/json/ext/generator/generator.h +8 -8
  8. data/ext/json/ext/parser/extconf.rb +29 -0
  9. data/ext/json/ext/parser/parser.c +540 -569
  10. data/ext/json/ext/parser/parser.h +10 -6
  11. data/ext/json/ext/parser/parser.rl +269 -261
  12. data/ext/json/extconf.rb +1 -1
  13. data/json.gemspec +0 -0
  14. data/lib/json/add/bigdecimal.rb +40 -10
  15. data/lib/json/add/complex.rb +32 -9
  16. data/lib/json/add/core.rb +1 -0
  17. data/lib/json/add/date.rb +27 -7
  18. data/lib/json/add/date_time.rb +26 -9
  19. data/lib/json/add/exception.rb +25 -7
  20. data/lib/json/add/ostruct.rb +32 -9
  21. data/lib/json/add/range.rb +33 -8
  22. data/lib/json/add/rational.rb +30 -8
  23. data/lib/json/add/regexp.rb +28 -10
  24. data/lib/json/add/set.rb +48 -0
  25. data/lib/json/add/struct.rb +29 -7
  26. data/lib/json/add/symbol.rb +28 -5
  27. data/lib/json/add/time.rb +27 -6
  28. data/lib/json/common.rb +402 -188
  29. data/lib/json/ext.rb +0 -6
  30. data/lib/json/generic_object.rb +11 -6
  31. data/lib/json/pure/generator.rb +120 -137
  32. data/lib/json/pure/parser.rb +64 -86
  33. data/lib/json/pure.rb +2 -8
  34. data/lib/json/version.rb +2 -1
  35. data/lib/json.rb +559 -29
  36. metadata +18 -129
  37. data/.gitignore +0 -17
  38. data/.travis.yml +0 -18
  39. data/Gemfile +0 -7
  40. data/README-json-jruby.markdown +0 -33
  41. data/Rakefile +0 -402
  42. data/TODO +0 -1
  43. data/VERSION +0 -1
  44. data/data/example.json +0 -1
  45. data/data/index.html +0 -38
  46. data/data/prototype.js +0 -4184
  47. data/diagrams/.keep +0 -0
  48. data/install.rb +0 -23
  49. data/java/src/json/ext/ByteListTranscoder.java +0 -166
  50. data/java/src/json/ext/Generator.java +0 -446
  51. data/java/src/json/ext/GeneratorMethods.java +0 -231
  52. data/java/src/json/ext/GeneratorService.java +0 -42
  53. data/java/src/json/ext/GeneratorState.java +0 -542
  54. data/java/src/json/ext/OptionsReader.java +0 -113
  55. data/java/src/json/ext/Parser.java +0 -2644
  56. data/java/src/json/ext/Parser.rl +0 -968
  57. data/java/src/json/ext/ParserService.java +0 -34
  58. data/java/src/json/ext/RuntimeInfo.java +0 -120
  59. data/java/src/json/ext/StringDecoder.java +0 -166
  60. data/java/src/json/ext/StringEncoder.java +0 -111
  61. data/java/src/json/ext/Utils.java +0 -88
  62. data/json-java.gemspec +0 -38
  63. data/json_pure.gemspec +0 -37
  64. data/lib/json/ext/.keep +0 -0
  65. data/tests/fixtures/fail1.json +0 -1
  66. data/tests/fixtures/fail10.json +0 -1
  67. data/tests/fixtures/fail11.json +0 -1
  68. data/tests/fixtures/fail12.json +0 -1
  69. data/tests/fixtures/fail13.json +0 -1
  70. data/tests/fixtures/fail14.json +0 -1
  71. data/tests/fixtures/fail18.json +0 -1
  72. data/tests/fixtures/fail19.json +0 -1
  73. data/tests/fixtures/fail2.json +0 -1
  74. data/tests/fixtures/fail20.json +0 -1
  75. data/tests/fixtures/fail21.json +0 -1
  76. data/tests/fixtures/fail22.json +0 -1
  77. data/tests/fixtures/fail23.json +0 -1
  78. data/tests/fixtures/fail24.json +0 -1
  79. data/tests/fixtures/fail25.json +0 -1
  80. data/tests/fixtures/fail27.json +0 -2
  81. data/tests/fixtures/fail28.json +0 -2
  82. data/tests/fixtures/fail3.json +0 -1
  83. data/tests/fixtures/fail4.json +0 -1
  84. data/tests/fixtures/fail5.json +0 -1
  85. data/tests/fixtures/fail6.json +0 -1
  86. data/tests/fixtures/fail7.json +0 -1
  87. data/tests/fixtures/fail8.json +0 -1
  88. data/tests/fixtures/fail9.json +0 -1
  89. data/tests/fixtures/pass1.json +0 -56
  90. data/tests/fixtures/pass15.json +0 -1
  91. data/tests/fixtures/pass16.json +0 -1
  92. data/tests/fixtures/pass17.json +0 -1
  93. data/tests/fixtures/pass2.json +0 -1
  94. data/tests/fixtures/pass26.json +0 -1
  95. data/tests/fixtures/pass3.json +0 -6
  96. data/tests/setup_variant.rb +0 -11
  97. data/tests/test_json.rb +0 -519
  98. data/tests/test_json_addition.rb +0 -196
  99. data/tests/test_json_encoding.rb +0 -65
  100. data/tests/test_json_fixtures.rb +0 -35
  101. data/tests/test_json_generate.rb +0 -348
  102. data/tests/test_json_generic_object.rb +0 -75
  103. data/tests/test_json_string_matching.rb +0 -39
  104. data/tests/test_json_unicode.rb +0 -72
  105. data/tools/diff.sh +0 -18
  106. data/tools/fuzz.rb +0 -139
  107. data/tools/server.rb +0 -62
data/diagrams/.keep DELETED
File without changes
data/install.rb DELETED
@@ -1,23 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'fileutils'
4
- include FileUtils::Verbose
5
- require 'rbconfig'
6
- include\
7
- begin
8
- RbConfig
9
- rescue NameError
10
- Config
11
- end
12
-
13
- sitelibdir = CONFIG["sitelibdir"]
14
- cd 'lib' do
15
- install('json.rb', sitelibdir)
16
- mkdir_p File.join(sitelibdir, 'json')
17
- for file in Dir['json/**/*}']
18
- d = File.join(sitelibdir, file)
19
- mkdir_p File.dirname(d)
20
- install(file, d)
21
- end
22
- end
23
- warn " *** Installed PURE ruby library."
@@ -1,166 +0,0 @@
1
- /*
2
- * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
3
- *
4
- * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
5
- */
6
- package json.ext;
7
-
8
- import org.jruby.exceptions.RaiseException;
9
- import org.jruby.runtime.ThreadContext;
10
- import org.jruby.util.ByteList;
11
-
12
- /**
13
- * A class specialized in transcoding a certain String format into another,
14
- * using UTF-8 ByteLists as both input and output.
15
- */
16
- abstract class ByteListTranscoder {
17
- protected final ThreadContext context;
18
-
19
- protected ByteList src;
20
- protected int srcEnd;
21
- /** Position where the last read character started */
22
- protected int charStart;
23
- /** Position of the next character to read */
24
- protected int pos;
25
-
26
- private ByteList out;
27
- /**
28
- * When a character that can be copied straight into the output is found,
29
- * its index is stored on this variable, and copying is delayed until
30
- * the sequence of characters that can be copied ends.
31
- *
32
- * <p>The variable stores -1 when not in a plain sequence.
33
- */
34
- private int quoteStart = -1;
35
-
36
- protected ByteListTranscoder(ThreadContext context) {
37
- this.context = context;
38
- }
39
-
40
- protected void init(ByteList src, ByteList out) {
41
- this.init(src, 0, src.length(), out);
42
- }
43
-
44
- protected void init(ByteList src, int start, int end, ByteList out) {
45
- this.src = src;
46
- this.pos = start;
47
- this.charStart = start;
48
- this.srcEnd = end;
49
- this.out = out;
50
- }
51
-
52
- /**
53
- * Returns whether there are any characters left to be read.
54
- */
55
- protected boolean hasNext() {
56
- return pos < srcEnd;
57
- }
58
-
59
- /**
60
- * Returns the next character in the buffer.
61
- */
62
- private char next() {
63
- return src.charAt(pos++);
64
- }
65
-
66
- /**
67
- * Reads an UTF-8 character from the input and returns its code point,
68
- * while advancing the input position.
69
- *
70
- * <p>Raises an {@link #invalidUtf8()} exception if an invalid byte
71
- * is found.
72
- */
73
- protected int readUtf8Char() {
74
- charStart = pos;
75
- char head = next();
76
- if (head <= 0x7f) { // 0b0xxxxxxx (ASCII)
77
- return head;
78
- }
79
- if (head <= 0xbf) { // 0b10xxxxxx
80
- throw invalidUtf8(); // tail byte with no head
81
- }
82
- if (head <= 0xdf) { // 0b110xxxxx
83
- ensureMin(1);
84
- int cp = ((head & 0x1f) << 6)
85
- | nextPart();
86
- if (cp < 0x0080) throw invalidUtf8();
87
- return cp;
88
- }
89
- if (head <= 0xef) { // 0b1110xxxx
90
- ensureMin(2);
91
- int cp = ((head & 0x0f) << 12)
92
- | (nextPart() << 6)
93
- | nextPart();
94
- if (cp < 0x0800) throw invalidUtf8();
95
- return cp;
96
- }
97
- if (head <= 0xf7) { // 0b11110xxx
98
- ensureMin(3);
99
- int cp = ((head & 0x07) << 18)
100
- | (nextPart() << 12)
101
- | (nextPart() << 6)
102
- | nextPart();
103
- if (!Character.isValidCodePoint(cp)) throw invalidUtf8();
104
- return cp;
105
- }
106
- // 0b11111xxx?
107
- throw invalidUtf8();
108
- }
109
-
110
- /**
111
- * Throws a GeneratorError if the input list doesn't have at least this
112
- * many bytes left.
113
- */
114
- protected void ensureMin(int n) {
115
- if (pos + n > srcEnd) throw incompleteUtf8();
116
- }
117
-
118
- /**
119
- * Reads the next byte of a multi-byte UTF-8 character and returns its
120
- * contents (lower 6 bits).
121
- *
122
- * <p>Throws a GeneratorError if the byte is not a valid tail.
123
- */
124
- private int nextPart() {
125
- char c = next();
126
- // tail bytes must be 0b10xxxxxx
127
- if ((c & 0xc0) != 0x80) throw invalidUtf8();
128
- return c & 0x3f;
129
- }
130
-
131
-
132
- protected void quoteStart() {
133
- if (quoteStart == -1) quoteStart = charStart;
134
- }
135
-
136
- /**
137
- * When in a sequence of characters that can be copied directly,
138
- * interrupts the sequence and copies it to the output buffer.
139
- *
140
- * @param endPos The offset until which the direct character quoting should
141
- * occur. You may pass {@link #pos} to quote until the most
142
- * recently read character, or {@link #charStart} to quote
143
- * until the character before it.
144
- */
145
- protected void quoteStop(int endPos) {
146
- if (quoteStart != -1) {
147
- out.append(src, quoteStart, endPos - quoteStart);
148
- quoteStart = -1;
149
- }
150
- }
151
-
152
- protected void append(int b) {
153
- out.append(b);
154
- }
155
-
156
- protected void append(byte[] origin, int start, int length) {
157
- out.append(origin, start, length);
158
- }
159
-
160
-
161
- protected abstract RaiseException invalidUtf8();
162
-
163
- protected RaiseException incompleteUtf8() {
164
- return invalidUtf8();
165
- }
166
- }
@@ -1,446 +0,0 @@
1
- /*
2
- * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
3
- *
4
- * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
5
- */
6
- package json.ext;
7
-
8
- import org.jruby.Ruby;
9
- import org.jruby.RubyArray;
10
- import org.jruby.RubyBignum;
11
- import org.jruby.RubyBoolean;
12
- import org.jruby.RubyClass;
13
- import org.jruby.RubyFixnum;
14
- import org.jruby.RubyFloat;
15
- import org.jruby.RubyHash;
16
- import org.jruby.RubyNumeric;
17
- import org.jruby.RubyString;
18
- import org.jruby.runtime.ThreadContext;
19
- import org.jruby.runtime.builtin.IRubyObject;
20
- import org.jruby.util.ByteList;
21
-
22
- public final class Generator {
23
- private Generator() {
24
- throw new RuntimeException();
25
- }
26
-
27
- /**
28
- * Encodes the given object as a JSON string, using the given handler.
29
- */
30
- static <T extends IRubyObject> RubyString
31
- generateJson(ThreadContext context, T object,
32
- Handler<? super T> handler, IRubyObject[] args) {
33
- Session session = new Session(context, args.length > 0 ? args[0]
34
- : null);
35
- return session.infect(handler.generateNew(session, object));
36
- }
37
-
38
- /**
39
- * Encodes the given object as a JSON string, detecting the appropriate handler
40
- * for the given object.
41
- */
42
- static <T extends IRubyObject> RubyString
43
- generateJson(ThreadContext context, T object, IRubyObject[] args) {
44
- Handler<? super T> handler = getHandlerFor(context.getRuntime(), object);
45
- return generateJson(context, object, handler, args);
46
- }
47
-
48
- /**
49
- * Encodes the given object as a JSON string, using the appropriate
50
- * handler if one is found or calling #to_json if not.
51
- */
52
- public static <T extends IRubyObject> RubyString
53
- generateJson(ThreadContext context, T object,
54
- GeneratorState config) {
55
- Session session = new Session(context, config);
56
- Handler<? super T> handler = getHandlerFor(context.getRuntime(), object);
57
- return handler.generateNew(session, object);
58
- }
59
-
60
- /**
61
- * Returns the best serialization handler for the given object.
62
- */
63
- // Java's generics can't handle this satisfactorily, so I'll just leave
64
- // the best I could get and ignore the warnings
65
- @SuppressWarnings("unchecked")
66
- private static <T extends IRubyObject>
67
- Handler<? super T> getHandlerFor(Ruby runtime, T object) {
68
- RubyClass metaClass = object.getMetaClass();
69
- if (metaClass == runtime.getString()) return (Handler)STRING_HANDLER;
70
- if (metaClass == runtime.getFixnum()) return (Handler)FIXNUM_HANDLER;
71
- if (metaClass == runtime.getHash()) return (Handler)HASH_HANDLER;
72
- if (metaClass == runtime.getArray()) return (Handler)ARRAY_HANDLER;
73
- if (object.isNil()) return (Handler)NIL_HANDLER;
74
- if (object == runtime.getTrue()) return (Handler)TRUE_HANDLER;
75
- if (object == runtime.getFalse()) return (Handler)FALSE_HANDLER;
76
- if (metaClass == runtime.getFloat()) return (Handler)FLOAT_HANDLER;
77
- if (metaClass == runtime.getBignum()) return (Handler)BIGNUM_HANDLER;
78
- return GENERIC_HANDLER;
79
- }
80
-
81
-
82
- /* Generator context */
83
-
84
- /**
85
- * A class that concentrates all the information that is shared by
86
- * generators working on a single session.
87
- *
88
- * <p>A session is defined as the process of serializing a single root
89
- * object; any handler directly called by container handlers (arrays and
90
- * hashes/objects) shares this object with its caller.
91
- *
92
- * <p>Note that anything called indirectly (via {@link GENERIC_HANDLER})
93
- * won't be part of the session.
94
- */
95
- static class Session {
96
- private final ThreadContext context;
97
- private GeneratorState state;
98
- private IRubyObject possibleState;
99
- private RuntimeInfo info;
100
- private StringEncoder stringEncoder;
101
-
102
- private boolean tainted = false;
103
- private boolean untrusted = false;
104
-
105
- Session(ThreadContext context, GeneratorState state) {
106
- this.context = context;
107
- this.state = state;
108
- }
109
-
110
- Session(ThreadContext context, IRubyObject possibleState) {
111
- this.context = context;
112
- this.possibleState = possibleState == null || possibleState.isNil()
113
- ? null : possibleState;
114
- }
115
-
116
- public ThreadContext getContext() {
117
- return context;
118
- }
119
-
120
- public Ruby getRuntime() {
121
- return context.getRuntime();
122
- }
123
-
124
- public GeneratorState getState() {
125
- if (state == null) {
126
- state = GeneratorState.fromState(context, getInfo(), possibleState);
127
- }
128
- return state;
129
- }
130
-
131
- public RuntimeInfo getInfo() {
132
- if (info == null) info = RuntimeInfo.forRuntime(getRuntime());
133
- return info;
134
- }
135
-
136
- public StringEncoder getStringEncoder() {
137
- if (stringEncoder == null) {
138
- stringEncoder = new StringEncoder(context, getState().asciiOnly());
139
- }
140
- return stringEncoder;
141
- }
142
-
143
- public void infectBy(IRubyObject object) {
144
- if (object.isTaint()) tainted = true;
145
- if (object.isUntrusted()) untrusted = true;
146
- }
147
-
148
- public <T extends IRubyObject> T infect(T object) {
149
- if (tainted) object.setTaint(true);
150
- if (untrusted) object.setUntrusted(true);
151
- return object;
152
- }
153
- }
154
-
155
-
156
- /* Handler base classes */
157
-
158
- private static abstract class Handler<T extends IRubyObject> {
159
- /**
160
- * Returns an estimative of how much space the serialization of the
161
- * given object will take. Used for allocating enough buffer space
162
- * before invoking other methods.
163
- */
164
- int guessSize(Session session, T object) {
165
- return 4;
166
- }
167
-
168
- RubyString generateNew(Session session, T object) {
169
- RubyString result;
170
- ByteList buffer = new ByteList(guessSize(session, object));
171
- generate(session, object, buffer);
172
- result = RubyString.newString(session.getRuntime(), buffer);
173
- ThreadContext context = session.getContext();
174
- RuntimeInfo info = session.getInfo();
175
- if (info.encodingsSupported()) {
176
- result.force_encoding(context, info.utf8.get());
177
- }
178
- return result;
179
- }
180
-
181
- abstract void generate(Session session, T object, ByteList buffer);
182
- }
183
-
184
- /**
185
- * A handler that returns a fixed keyword regardless of the passed object.
186
- */
187
- private static class KeywordHandler<T extends IRubyObject>
188
- extends Handler<T> {
189
- private final ByteList keyword;
190
-
191
- private KeywordHandler(String keyword) {
192
- this.keyword = new ByteList(ByteList.plain(keyword), false);
193
- }
194
-
195
- @Override
196
- int guessSize(Session session, T object) {
197
- return keyword.length();
198
- }
199
-
200
- @Override
201
- RubyString generateNew(Session session, T object) {
202
- return RubyString.newStringShared(session.getRuntime(), keyword);
203
- }
204
-
205
- @Override
206
- void generate(Session session, T object, ByteList buffer) {
207
- buffer.append(keyword);
208
- }
209
- }
210
-
211
-
212
- /* Handlers */
213
-
214
- static final Handler<RubyBignum> BIGNUM_HANDLER =
215
- new Handler<RubyBignum>() {
216
- @Override
217
- void generate(Session session, RubyBignum object, ByteList buffer) {
218
- // JRUBY-4751: RubyBignum.to_s() returns generic object
219
- // representation (fixed in 1.5, but we maintain backwards
220
- // compatibility; call to_s(IRubyObject[]) then
221
- buffer.append(((RubyString)object.to_s(IRubyObject.NULL_ARRAY)).getByteList());
222
- }
223
- };
224
-
225
- static final Handler<RubyFixnum> FIXNUM_HANDLER =
226
- new Handler<RubyFixnum>() {
227
- @Override
228
- void generate(Session session, RubyFixnum object, ByteList buffer) {
229
- buffer.append(object.to_s().getByteList());
230
- }
231
- };
232
-
233
- static final Handler<RubyFloat> FLOAT_HANDLER =
234
- new Handler<RubyFloat>() {
235
- @Override
236
- void generate(Session session, RubyFloat object, ByteList buffer) {
237
- double value = RubyFloat.num2dbl(object);
238
-
239
- if (Double.isInfinite(value) || Double.isNaN(value)) {
240
- if (!session.getState().allowNaN()) {
241
- throw Utils.newException(session.getContext(),
242
- Utils.M_GENERATOR_ERROR,
243
- object + " not allowed in JSON");
244
- }
245
- }
246
- buffer.append(((RubyString)object.to_s()).getByteList());
247
- }
248
- };
249
-
250
- static final Handler<RubyArray> ARRAY_HANDLER =
251
- new Handler<RubyArray>() {
252
- @Override
253
- int guessSize(Session session, RubyArray object) {
254
- GeneratorState state = session.getState();
255
- int depth = state.getDepth();
256
- int perItem =
257
- 4 // prealloc
258
- + (depth + 1) * state.getIndent().length() // indent
259
- + 1 + state.getArrayNl().length(); // ',' arrayNl
260
- return 2 + object.size() * perItem;
261
- }
262
-
263
- @Override
264
- void generate(Session session, RubyArray object, ByteList buffer) {
265
- ThreadContext context = session.getContext();
266
- Ruby runtime = context.getRuntime();
267
- GeneratorState state = session.getState();
268
- int depth = state.increaseDepth();
269
-
270
- ByteList indentUnit = state.getIndent();
271
- byte[] shift = Utils.repeat(indentUnit, depth);
272
-
273
- ByteList arrayNl = state.getArrayNl();
274
- byte[] delim = new byte[1 + arrayNl.length()];
275
- delim[0] = ',';
276
- System.arraycopy(arrayNl.unsafeBytes(), arrayNl.begin(), delim, 1,
277
- arrayNl.length());
278
-
279
- session.infectBy(object);
280
-
281
- buffer.append((byte)'[');
282
- buffer.append(arrayNl);
283
- boolean firstItem = true;
284
- for (int i = 0, t = object.getLength(); i < t; i++) {
285
- IRubyObject element = object.eltInternal(i);
286
- session.infectBy(element);
287
- if (firstItem) {
288
- firstItem = false;
289
- } else {
290
- buffer.append(delim);
291
- }
292
- buffer.append(shift);
293
- Handler<IRubyObject> handler = getHandlerFor(runtime, element);
294
- handler.generate(session, element, buffer);
295
- }
296
-
297
- state.decreaseDepth();
298
- if (arrayNl.length() != 0) {
299
- buffer.append(arrayNl);
300
- buffer.append(shift, 0, state.getDepth() * indentUnit.length());
301
- }
302
-
303
- buffer.append((byte)']');
304
- }
305
- };
306
-
307
- static final Handler<RubyHash> HASH_HANDLER =
308
- new Handler<RubyHash>() {
309
- @Override
310
- int guessSize(Session session, RubyHash object) {
311
- GeneratorState state = session.getState();
312
- int perItem =
313
- 12 // key, colon, comma
314
- + (state.getDepth() + 1) * state.getIndent().length()
315
- + state.getSpaceBefore().length()
316
- + state.getSpace().length();
317
- return 2 + object.size() * perItem;
318
- }
319
-
320
- @Override
321
- void generate(final Session session, RubyHash object,
322
- final ByteList buffer) {
323
- ThreadContext context = session.getContext();
324
- final Ruby runtime = context.getRuntime();
325
- final GeneratorState state = session.getState();
326
- final int depth = state.increaseDepth();
327
-
328
- final ByteList objectNl = state.getObjectNl();
329
- final byte[] indent = Utils.repeat(state.getIndent(), depth);
330
- final ByteList spaceBefore = state.getSpaceBefore();
331
- final ByteList space = state.getSpace();
332
-
333
- buffer.append((byte)'{');
334
- buffer.append(objectNl);
335
- object.visitAll(new RubyHash.Visitor() {
336
- private boolean firstPair = true;
337
-
338
- @Override
339
- public void visit(IRubyObject key, IRubyObject value) {
340
- if (firstPair) {
341
- firstPair = false;
342
- } else {
343
- buffer.append((byte)',');
344
- buffer.append(objectNl);
345
- }
346
- if (objectNl.length() != 0) buffer.append(indent);
347
-
348
- STRING_HANDLER.generate(session, key.asString(), buffer);
349
- session.infectBy(key);
350
-
351
- buffer.append(spaceBefore);
352
- buffer.append((byte)':');
353
- buffer.append(space);
354
-
355
- Handler<IRubyObject> valueHandler = getHandlerFor(runtime, value);
356
- valueHandler.generate(session, value, buffer);
357
- session.infectBy(value);
358
- }
359
- });
360
- state.decreaseDepth();
361
- if (objectNl.length() != 0) {
362
- buffer.append(objectNl);
363
- buffer.append(Utils.repeat(state.getIndent(), state.getDepth()));
364
- }
365
- buffer.append((byte)'}');
366
- }
367
- };
368
-
369
- static final Handler<RubyString> STRING_HANDLER =
370
- new Handler<RubyString>() {
371
- @Override
372
- int guessSize(Session session, RubyString object) {
373
- // for most applications, most strings will be just a set of
374
- // printable ASCII characters without any escaping, so let's
375
- // just allocate enough space for that + the quotes
376
- return 2 + object.getByteList().length();
377
- }
378
-
379
- @Override
380
- void generate(Session session, RubyString object, ByteList buffer) {
381
- RuntimeInfo info = session.getInfo();
382
- RubyString src;
383
-
384
- if (info.encodingsSupported() &&
385
- object.encoding(session.getContext()) != info.utf8.get()) {
386
- src = (RubyString)object.encode(session.getContext(),
387
- info.utf8.get());
388
- } else {
389
- src = object;
390
- }
391
-
392
- session.getStringEncoder().encode(src.getByteList(), buffer);
393
- }
394
- };
395
-
396
- static final Handler<RubyBoolean> TRUE_HANDLER =
397
- new KeywordHandler<RubyBoolean>("true");
398
- static final Handler<RubyBoolean> FALSE_HANDLER =
399
- new KeywordHandler<RubyBoolean>("false");
400
- static final Handler<IRubyObject> NIL_HANDLER =
401
- new KeywordHandler<IRubyObject>("null");
402
-
403
- /**
404
- * The default handler (<code>Object#to_json</code>): coerces the object
405
- * to string using <code>#to_s</code>, and serializes that string.
406
- */
407
- static final Handler<IRubyObject> OBJECT_HANDLER =
408
- new Handler<IRubyObject>() {
409
- @Override
410
- RubyString generateNew(Session session, IRubyObject object) {
411
- RubyString str = object.asString();
412
- return STRING_HANDLER.generateNew(session, str);
413
- }
414
-
415
- @Override
416
- void generate(Session session, IRubyObject object, ByteList buffer) {
417
- RubyString str = object.asString();
418
- STRING_HANDLER.generate(session, str, buffer);
419
- }
420
- };
421
-
422
- /**
423
- * A handler that simply calls <code>#to_json(state)</code> on the
424
- * given object.
425
- */
426
- static final Handler<IRubyObject> GENERIC_HANDLER =
427
- new Handler<IRubyObject>() {
428
- @Override
429
- RubyString generateNew(Session session, IRubyObject object) {
430
- if (object.respondsTo("to_json")) {
431
- IRubyObject result = object.callMethod(session.getContext(), "to_json",
432
- new IRubyObject[] {session.getState()});
433
- if (result instanceof RubyString) return (RubyString)result;
434
- throw session.getRuntime().newTypeError("to_json must return a String");
435
- } else {
436
- return OBJECT_HANDLER.generateNew(session, object);
437
- }
438
- }
439
-
440
- @Override
441
- void generate(Session session, IRubyObject object, ByteList buffer) {
442
- RubyString result = generateNew(session, object);
443
- buffer.append(result.getByteList());
444
- }
445
- };
446
- }