json 2.3.0 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,13 +10,10 @@ import org.jruby.RubyArray;
10
10
  import org.jruby.RubyBasicObject;
11
11
  import org.jruby.RubyBignum;
12
12
  import org.jruby.RubyBoolean;
13
- import org.jruby.RubyClass;
14
13
  import org.jruby.RubyFixnum;
15
14
  import org.jruby.RubyFloat;
16
15
  import org.jruby.RubyHash;
17
- import org.jruby.RubyNumeric;
18
16
  import org.jruby.RubyString;
19
- import org.jruby.runtime.ClassIndex;
20
17
  import org.jruby.runtime.ThreadContext;
21
18
  import org.jruby.runtime.builtin.IRubyObject;
22
19
  import org.jruby.util.ByteList;
@@ -32,8 +29,7 @@ public final class Generator {
32
29
  static <T extends IRubyObject> RubyString
33
30
  generateJson(ThreadContext context, T object,
34
31
  Handler<? super T> handler, IRubyObject[] args) {
35
- Session session = new Session(context, args.length > 0 ? args[0]
36
- : null);
32
+ Session session = new Session(context, args.length > 0 ? args[0] : null);
37
33
  return session.infect(handler.generateNew(session, object));
38
34
  }
39
35
 
@@ -43,7 +39,7 @@ public final class Generator {
43
39
  */
44
40
  static <T extends IRubyObject> RubyString
45
41
  generateJson(ThreadContext context, T object, IRubyObject[] args) {
46
- Handler<? super T> handler = getHandlerFor(context.getRuntime(), object);
42
+ Handler<? super T> handler = getHandlerFor(context.runtime, object);
47
43
  return generateJson(context, object, handler, args);
48
44
  }
49
45
 
@@ -55,33 +51,18 @@ public final class Generator {
55
51
  generateJson(ThreadContext context, T object,
56
52
  GeneratorState config) {
57
53
  Session session = new Session(context, config);
58
- Handler<? super T> handler = getHandlerFor(context.getRuntime(), object);
54
+ Handler<? super T> handler = getHandlerFor(context.runtime, object);
59
55
  return handler.generateNew(session, object);
60
56
  }
61
57
 
62
- // NOTE: drop this once Ruby 1.9.3 support is gone!
63
- private static final int FIXNUM = 1;
64
- private static final int BIGNUM = 2;
65
- private static final int ARRAY = 3;
66
- private static final int STRING = 4;
67
- private static final int NIL = 5;
68
- private static final int TRUE = 6;
69
- private static final int FALSE = 7;
70
- private static final int HASH = 10;
71
- private static final int FLOAT = 11;
72
- // hard-coded due JRuby 1.7 compatibility
73
- // https://github.com/jruby/jruby/blob/1.7.27/core/src/main/java/org/jruby/runtime/ClassIndex.java
74
-
75
58
  /**
76
59
  * Returns the best serialization handler for the given object.
77
60
  */
78
61
  // Java's generics can't handle this satisfactorily, so I'll just leave
79
62
  // the best I could get and ignore the warnings
80
63
  @SuppressWarnings("unchecked")
81
- private static <T extends IRubyObject>
82
- Handler<? super T> getHandlerFor(Ruby runtime, T object) {
83
- switch (((RubyBasicObject) object).getNativeTypeIndex()) {
84
- // can not use getNativeClassIndex due 1.7 compatibility
64
+ private static <T extends IRubyObject> Handler<? super T> getHandlerFor(Ruby runtime, T object) {
65
+ switch (((RubyBasicObject) object).getNativeClassIndex()) {
85
66
  case NIL : return (Handler) NIL_HANDLER;
86
67
  case TRUE : return (Handler) TRUE_HANDLER;
87
68
  case FALSE : return (Handler) FALSE_HANDLER;
@@ -158,7 +139,7 @@ public final class Generator {
158
139
 
159
140
  public StringEncoder getStringEncoder() {
160
141
  if (stringEncoder == null) {
161
- stringEncoder = new StringEncoder(context, getState().asciiOnly());
142
+ stringEncoder = new StringEncoder(context, getState().asciiOnly(), getState().escapeSlash());
162
143
  }
163
144
  return stringEncoder;
164
145
  }
@@ -353,13 +334,13 @@ public final class Generator {
353
334
 
354
335
  buffer.append((byte)'{');
355
336
  buffer.append(objectNl);
356
- object.visitAll(new RubyHash.Visitor() {
357
- private boolean firstPair = true;
358
337
 
338
+ final boolean[] firstPair = new boolean[]{true};
339
+ object.visitAll(new RubyHash.Visitor() {
359
340
  @Override
360
341
  public void visit(IRubyObject key, IRubyObject value) {
361
- if (firstPair) {
362
- firstPair = false;
342
+ if (firstPair[0]) {
343
+ firstPair[0] = false;
363
344
  } else {
364
345
  buffer.append((byte)',');
365
346
  buffer.append(objectNl);
@@ -379,7 +360,7 @@ public final class Generator {
379
360
  }
380
361
  });
381
362
  state.decreaseDepth();
382
- if (objectNl.length() != 0) {
363
+ if (!firstPair[0] && objectNl.length() != 0) {
383
364
  buffer.append(objectNl);
384
365
  buffer.append(Utils.repeat(state.getIndent(), state.getDepth()));
385
366
  }
@@ -82,6 +82,12 @@ public class GeneratorState extends RubyObject {
82
82
  */
83
83
  private boolean quirksMode = DEFAULT_QUIRKS_MODE;
84
84
  static final boolean DEFAULT_QUIRKS_MODE = false;
85
+ /**
86
+ * If set to <code>true</code> the forward slash will be escaped in
87
+ * json output.
88
+ */
89
+ private boolean escapeSlash = DEFAULT_ESCAPE_SLASH;
90
+ static final boolean DEFAULT_ESCAPE_SLASH = false;
85
91
  /**
86
92
  * The initial buffer length of this state. (This isn't really used on all
87
93
  * non-C implementations.)
@@ -171,6 +177,9 @@ public class GeneratorState extends RubyObject {
171
177
  * <code>-Infinity</code> should be generated, otherwise an exception is
172
178
  * thrown if these values are encountered.
173
179
  * This options defaults to <code>false</code>.
180
+ * <dt><code>:escape_slash</code>
181
+ * <dd>set to <code>true</code> if the forward slashes should be escaped
182
+ * in the json output (default: <code>false</code>)
174
183
  */
175
184
  @JRubyMethod(optional=1, visibility=Visibility.PRIVATE)
176
185
  public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
@@ -194,6 +203,7 @@ public class GeneratorState extends RubyObject {
194
203
  this.allowNaN = orig.allowNaN;
195
204
  this.asciiOnly = orig.asciiOnly;
196
205
  this.quirksMode = orig.quirksMode;
206
+ this.escapeSlash = orig.escapeSlash;
197
207
  this.bufferInitialLength = orig.bufferInitialLength;
198
208
  this.depth = orig.depth;
199
209
  return this;
@@ -346,6 +356,24 @@ public class GeneratorState extends RubyObject {
346
356
  return max_nesting;
347
357
  }
348
358
 
359
+ /**
360
+ * Returns true if forward slashes are escaped in the json output.
361
+ */
362
+ public boolean escapeSlash() {
363
+ return escapeSlash;
364
+ }
365
+
366
+ @JRubyMethod(name="escape_slash")
367
+ public RubyBoolean escape_slash_get(ThreadContext context) {
368
+ return context.getRuntime().newBoolean(escapeSlash);
369
+ }
370
+
371
+ @JRubyMethod(name="escape_slash=")
372
+ public IRubyObject escape_slash_set(IRubyObject escape_slash) {
373
+ escapeSlash = escape_slash.isTrue();
374
+ return escape_slash.getRuntime().newBoolean(escapeSlash);
375
+ }
376
+
349
377
  public boolean allowNaN() {
350
378
  return allowNaN;
351
379
  }
@@ -430,6 +458,7 @@ public class GeneratorState extends RubyObject {
430
458
  maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING);
431
459
  allowNaN = opts.getBool("allow_nan", DEFAULT_ALLOW_NAN);
432
460
  asciiOnly = opts.getBool("ascii_only", DEFAULT_ASCII_ONLY);
461
+ escapeSlash = opts.getBool("escape_slash", DEFAULT_ESCAPE_SLASH);
433
462
  bufferInitialLength = opts.getInt("buffer_initial_length", DEFAULT_BUFFER_INITIAL_LENGTH);
434
463
 
435
464
  depth = opts.getInt("depth", 0);
@@ -457,6 +486,7 @@ public class GeneratorState extends RubyObject {
457
486
  result.op_aset(context, runtime.newSymbol("allow_nan"), allow_nan_p(context));
458
487
  result.op_aset(context, runtime.newSymbol("ascii_only"), ascii_only_p(context));
459
488
  result.op_aset(context, runtime.newSymbol("max_nesting"), max_nesting_get(context));
489
+ result.op_aset(context, runtime.newSymbol("escape_slash"), escape_slash_get(context));
460
490
  result.op_aset(context, runtime.newSymbol("depth"), depth_get(context));
461
491
  result.op_aset(context, runtime.newSymbol("buffer_initial_length"), buffer_initial_length_get(context));
462
492
  for (String name: getInstanceVariableNameList()) {