diff options
author | Charles Oliver Nutter <headius@headius.com> | 2010-08-07 10:58:10 -0500 |
---|---|---|
committer | Charles Oliver Nutter <headius@headius.com> | 2010-08-07 10:58:10 -0500 |
commit | 914cca595ccaed53da6d94698c6095fed3648f19 (patch) | |
tree | 9a5be6d699d0350d5b2d609fdd5919b8728f0655 | |
parent | 36ba9064c646e32c9490f5cbd26f3f5848687bc8 (diff) | |
parent | 56f281e91ea596b46b48b80df57445fee6710590 (diff) | |
download | json-914cca595ccaed53da6d94698c6095fed3648f19.tar.gz |
Merge remote branch 'mernen/master'
-rw-r--r-- | .gitignore | 13 | ||||
-rw-r--r-- | src/json/ext/Generator.java | 95 | ||||
-rw-r--r-- | src/json/ext/GeneratorState.java | 64 | ||||
-rw-r--r-- | src/json/ext/OptionsReader.java | 21 |
4 files changed, 110 insertions, 83 deletions
@@ -2,3 +2,16 @@ coverage pkg .nfs.* + +# java build +/src/json/ext/Parser.java +/*.gem +/build +/lib/json/ext/*.jar +# java: NetBeans +/nbproject/private +# java: Eclipse +/.classpath +/.project +/.settings +/.externalToolBuilders diff --git a/src/json/ext/Generator.java b/src/json/ext/Generator.java index b2f852c..230d68f 100644 --- a/src/json/ext/Generator.java +++ b/src/json/ext/Generator.java @@ -33,8 +33,7 @@ public final class Generator { Handler<? super T> handler, IRubyObject[] args) { Session session = new Session(context, args.length > 0 ? args[0] : null); - int depth = args.length > 1 ? RubyNumeric.fix2int(args[1]) : 0; - return session.infect(handler.generateNew(session, object, depth)); + return session.infect(handler.generateNew(session, object)); } /** @@ -53,10 +52,10 @@ public final class Generator { */ public static <T extends IRubyObject> RubyString generateJson(ThreadContext context, T object, - GeneratorState config, int depth) { + GeneratorState config) { Session session = new Session(context, config); Handler<? super T> handler = getHandlerFor(context.getRuntime(), object); - return handler.generateNew(session, object, depth); + return handler.generateNew(session, object); } /** @@ -163,18 +162,17 @@ public final class Generator { * given object will take. Used for allocating enough buffer space * before invoking other methods. */ - int guessSize(Session session, T object, int depth) { + int guessSize(Session session, T object) { return 4; } - RubyString generateNew(Session session, T object, int depth) { - ByteList buffer = new ByteList(guessSize(session, object, depth)); - generate(session, object, buffer, depth); + RubyString generateNew(Session session, T object) { + ByteList buffer = new ByteList(guessSize(session, object)); + generate(session, object, buffer); return RubyString.newString(session.getRuntime(), buffer); } - abstract void generate(Session session, T object, ByteList buffer, - int depth); + abstract void generate(Session session, T object, ByteList buffer); } /** @@ -189,17 +187,17 @@ public final class Generator { } @Override - int guessSize(Session session, T object, int depth) { + int guessSize(Session session, T object) { return keyword.length(); } @Override - RubyString generateNew(Session session, T object, int depth) { + RubyString generateNew(Session session, T object) { return RubyString.newStringShared(session.getRuntime(), keyword); } @Override - void generate(Session session, T object, ByteList buffer, int depth) { + void generate(Session session, T object, ByteList buffer) { buffer.append(keyword); } } @@ -210,8 +208,7 @@ public final class Generator { static final Handler<RubyBignum> BIGNUM_HANDLER = new Handler<RubyBignum>() { @Override - void generate(Session session, RubyBignum object, ByteList buffer, - int depth) { + void generate(Session session, RubyBignum object, ByteList buffer) { // JRUBY-4751: RubyBignum.to_s() returns generic object // representation (fixed in 1.5, but we maintain backwards // compatibility; call to_s(IRubyObject[]) then @@ -222,8 +219,7 @@ public final class Generator { static final Handler<RubyFixnum> FIXNUM_HANDLER = new Handler<RubyFixnum>() { @Override - void generate(Session session, RubyFixnum object, ByteList buffer, - int depth) { + void generate(Session session, RubyFixnum object, ByteList buffer) { buffer.append(object.to_s().getByteList()); } }; @@ -231,8 +227,7 @@ public final class Generator { static final Handler<RubyFloat> FLOAT_HANDLER = new Handler<RubyFloat>() { @Override - void generate(Session session, RubyFloat object, ByteList buffer, - int depth) { + void generate(Session session, RubyFloat object, ByteList buffer) { double value = RubyFloat.num2dbl(object); if (Double.isInfinite(value) || Double.isNaN(value)) { @@ -249,8 +244,9 @@ public final class Generator { static final Handler<RubyArray> ARRAY_HANDLER = new Handler<RubyArray>() { @Override - int guessSize(Session session, RubyArray object, int depth) { + int guessSize(Session session, RubyArray object) { GeneratorState state = session.getState(); + int depth = state.getDepth(); int perItem = 4 // prealloc + (depth + 1) * state.getIndent().length() // indent @@ -259,15 +255,14 @@ public final class Generator { } @Override - void generate(Session session, RubyArray object, ByteList buffer, - int depth) { + void generate(Session session, RubyArray object, ByteList buffer) { ThreadContext context = session.getContext(); Ruby runtime = context.getRuntime(); GeneratorState state = session.getState(); - state.checkMaxNesting(context, depth + 1); + int depth = state.increaseDepth(); ByteList indentUnit = state.getIndent(); - byte[] shift = Utils.repeat(indentUnit, depth + 1); + byte[] shift = Utils.repeat(indentUnit, depth); ByteList arrayNl = state.getArrayNl(); byte[] delim = new byte[1 + arrayNl.length()]; @@ -290,12 +285,13 @@ public final class Generator { } buffer.append(shift); Handler<IRubyObject> handler = getHandlerFor(runtime, element); - handler.generate(session, element, buffer, depth + 1); + handler.generate(session, element, buffer); } + state.decreaseDepth(); if (arrayNl.length() != 0) { buffer.append(arrayNl); - buffer.append(shift, 0, depth * indentUnit.length()); + buffer.append(shift, 0, state.getDepth() * indentUnit.length()); } buffer.append((byte)']'); @@ -305,11 +301,11 @@ public final class Generator { static final Handler<RubyHash> HASH_HANDLER = new Handler<RubyHash>() { @Override - int guessSize(Session session, RubyHash object, int depth) { + int guessSize(Session session, RubyHash object) { GeneratorState state = session.getState(); int perItem = 12 // key, colon, comma - + (depth + 1) * state.getIndent().length() + + (state.getDepth() + 1) * state.getIndent().length() + state.getSpaceBefore().length() + state.getSpace().length(); return 2 + object.size() * perItem; @@ -317,14 +313,14 @@ public final class Generator { @Override void generate(final Session session, RubyHash object, - final ByteList buffer, final int depth) { + final ByteList buffer) { ThreadContext context = session.getContext(); final Ruby runtime = context.getRuntime(); final GeneratorState state = session.getState(); - state.checkMaxNesting(context, depth + 1); + final int depth = state.increaseDepth(); final ByteList objectNl = state.getObjectNl(); - final byte[] indent = Utils.repeat(state.getIndent(), depth + 1); + final byte[] indent = Utils.repeat(state.getIndent(), depth); final ByteList spaceBefore = state.getSpaceBefore(); final ByteList space = state.getSpace(); @@ -343,8 +339,7 @@ public final class Generator { } if (objectNl.length() != 0) buffer.append(indent); - STRING_HANDLER.generate(session, key.asString(), - buffer, depth + 1); + STRING_HANDLER.generate(session, key.asString(), buffer); session.infectBy(key); buffer.append(spaceBefore); @@ -352,14 +347,15 @@ public final class Generator { buffer.append(space); Handler<IRubyObject> valueHandler = getHandlerFor(runtime, value); - valueHandler.generate(session, value, buffer, depth + 1); + valueHandler.generate(session, value, buffer); session.infectBy(value); } }); + state.decreaseDepth(); if (objectNl.length() != 0) { buffer.append(objectNl); if (indent.length != 0) { - for (int i = 0; i < depth; i++) { + for (int i = 0; i < state.getDepth(); i++) { buffer.append(indent); } } @@ -371,7 +367,7 @@ public final class Generator { static final Handler<RubyString> STRING_HANDLER = new Handler<RubyString>() { @Override - int guessSize(Session session, RubyString object, int depth) { + int guessSize(Session session, RubyString object) { // for most applications, most strings will be just a set of // printable ASCII characters without any escaping, so let's // just allocate enough space for that + the quotes @@ -379,8 +375,7 @@ public final class Generator { } @Override - void generate(Session session, RubyString object, ByteList buffer, - int depth) { + void generate(Session session, RubyString object, ByteList buffer) { RuntimeInfo info = session.getInfo(); RubyString src; @@ -410,42 +405,36 @@ public final class Generator { static final Handler<IRubyObject> OBJECT_HANDLER = new Handler<IRubyObject>() { @Override - RubyString generateNew(Session session, IRubyObject object, - int depth) { + RubyString generateNew(Session session, IRubyObject object) { RubyString str = object.asString(); - return STRING_HANDLER.generateNew(session, str, depth); + return STRING_HANDLER.generateNew(session, str); } @Override - void generate(Session session, IRubyObject object, ByteList buffer, - int depth) { + void generate(Session session, IRubyObject object, ByteList buffer) { RubyString str = object.asString(); - STRING_HANDLER.generate(session, str, buffer, depth); + STRING_HANDLER.generate(session, str, buffer); } }; /** - * A handler that simply calls <code>#to_json(state, depth)</code> on the + * A handler that simply calls <code>#to_json(state)</code> on the * given object. */ static final Handler<IRubyObject> GENERIC_HANDLER = new Handler<IRubyObject>() { @Override - RubyString generateNew(Session session, IRubyObject object, - int depth) { - RubyNumeric vDepth = - RubyNumeric.int2fix(session.getRuntime(), depth); + RubyString generateNew(Session session, IRubyObject object) { IRubyObject result = object.callMethod(session.getContext(), "to_json", - new IRubyObject[] {session.getState(), vDepth}); + new IRubyObject[] {session.getState()}); if (result instanceof RubyString) return (RubyString)result; throw session.getRuntime().newTypeError("to_json must return a String"); } @Override - void generate(Session session, IRubyObject object, ByteList buffer, - int depth) { - RubyString result = generateNew(session, object, depth); + void generate(Session session, IRubyObject object, ByteList buffer) { + RubyString result = generateNew(session, object); buffer.append(result.getByteList()); } }; diff --git a/src/json/ext/GeneratorState.java b/src/json/ext/GeneratorState.java index 107a204..63263e8 100644 --- a/src/json/ext/GeneratorState.java +++ b/src/json/ext/GeneratorState.java @@ -77,6 +77,11 @@ public class GeneratorState extends RubyObject { private boolean asciiOnly = DEFAULT_ASCII_ONLY; static final boolean DEFAULT_ASCII_ONLY = false; + /** + * The current depth (inside a #to_json call) + */ + private int depth = 0; + static final ObjectAllocator ALLOCATOR = new ObjectAllocator() { public IRubyObject allocate(Ruby runtime, RubyClass klazz) { return new GeneratorState(runtime, klazz); @@ -176,6 +181,7 @@ public class GeneratorState extends RubyObject { this.maxNesting = orig.maxNesting; this.allowNaN = orig.allowNaN; this.asciiOnly = orig.asciiOnly; + this.depth = orig.depth; return this; } @@ -184,7 +190,7 @@ public class GeneratorState extends RubyObject { */ @JRubyMethod public IRubyObject generate(ThreadContext context, IRubyObject obj) { - RubyString result = Generator.generateJson(context, obj, this, 0); + RubyString result = Generator.generateJson(context, obj, this); if (!objectOrArrayLiteral(result)) { throw Utils.newException(context, Utils.M_GENERATOR_ERROR, "only generation of JSON objects or arrays allowed"); @@ -323,6 +329,13 @@ public class GeneratorState extends RubyObject { return context.getRuntime().newBoolean(maxNesting != 0); } + /** + * Returns the maximum level of nesting configured for this state. + */ + public int getMaxNesting() { + return maxNesting; + } + @JRubyMethod(name="max_nesting") public RubyInteger max_nesting_get(ThreadContext context) { return context.getRuntime().newFixnum(maxNesting); @@ -352,14 +365,26 @@ public class GeneratorState extends RubyObject { return context.getRuntime().newBoolean(asciiOnly); } + public int getDepth() { + return depth; + } + + @JRubyMethod(name="depth") + public RubyInteger depth_get(ThreadContext context) { + return context.getRuntime().newFixnum(depth); + } + + @JRubyMethod(name="depth=") + public IRubyObject depth_set(IRubyObject vDepth) { + depth = RubyNumeric.fix2int(vDepth); + return vDepth; + } + private ByteList prepareByteList(ThreadContext context, IRubyObject value) { RubyString str = value.convertToString(); - IRubyObject encoding = str.encoding(context); - if (encoding != null) { - RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime()); - if (encoding != info.utf8) { - str = (RubyString)str.encode(context, info.utf8); - } + RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime()); + if (info.encodingsSupported() && str.encoding(context) != info.utf8) { + str = (RubyString)str.encode(context, info.utf8); } return str.getByteList().dup(); } @@ -374,7 +399,7 @@ public class GeneratorState extends RubyObject { */ @JRubyMethod public IRubyObject configure(ThreadContext context, IRubyObject vOpts) { - OptionsReader opts = OptionsReader.withStrings(context, vOpts); + OptionsReader opts = new OptionsReader(context, vOpts); ByteList indent = opts.getString("indent"); if (indent != null) this.indent = indent; @@ -395,6 +420,8 @@ public class GeneratorState extends RubyObject { allowNaN = opts.getBool("allow_nan", DEFAULT_ALLOW_NAN); asciiOnly = opts.getBool("ascii_only", DEFAULT_ASCII_ONLY); + depth = opts.getInt("depth", 0); + return this; } @@ -418,15 +445,18 @@ public class GeneratorState extends RubyObject { result.op_aset(context, runtime.newSymbol("allow_nan"), allow_nan_p(context)); result.op_aset(context, runtime.newSymbol("ascii_only"), ascii_only_p(context)); result.op_aset(context, runtime.newSymbol("max_nesting"), max_nesting_get(context)); + result.op_aset(context, runtime.newSymbol("depth"), depth_get(context)); return result; } - /** - * Returns the maximum level of nesting configured for this state. - * @return - */ - public int getMaxNesting() { - return maxNesting; + public int increaseDepth() { + depth++; + checkMaxNesting(); + return depth; + } + + public int decreaseDepth() { + return --depth; } /** @@ -434,10 +464,10 @@ public class GeneratorState extends RubyObject { * @param context * @param depth The corrent depth */ - void checkMaxNesting(ThreadContext context, int depth) { + private void checkMaxNesting() { if (maxNesting != 0 && depth > maxNesting) { - throw Utils.newException(context, Utils.M_NESTING_ERROR, - "nesting of " + depth + " is too deep"); + throw Utils.newException(getRuntime().getCurrentContext(), + Utils.M_NESTING_ERROR, "nesting of " + depth + " is too deep"); } } } diff --git a/src/json/ext/OptionsReader.java b/src/json/ext/OptionsReader.java index 29de188..3bc8d5f 100644 --- a/src/json/ext/OptionsReader.java +++ b/src/json/ext/OptionsReader.java @@ -18,14 +18,12 @@ import org.jruby.util.ByteList; final class OptionsReader { private final ThreadContext context; private final Ruby runtime; - private final RuntimeInfo info; private final RubyHash opts; + private RuntimeInfo info; - private OptionsReader(ThreadContext context, Ruby runtime, - RuntimeInfo info, IRubyObject vOpts) { + OptionsReader(ThreadContext context, IRubyObject vOpts) { this.context = context; - this.runtime = runtime; - this.info = info; + this.runtime = context.getRuntime(); if (vOpts == null || vOpts.isNil()) { opts = null; @@ -36,14 +34,10 @@ final class OptionsReader { } } - OptionsReader(ThreadContext context, IRubyObject vOpts) { - this(context, context.getRuntime(), null, vOpts); - } - - static OptionsReader withStrings(ThreadContext context, IRubyObject vOpts) { - Ruby runtime = context.getRuntime(); - return new OptionsReader(context, runtime, - RuntimeInfo.forRuntime(runtime), vOpts); + private RuntimeInfo getRuntimeInfo() { + if (info != null) return info; + info = RuntimeInfo.forRuntime(runtime); + return info; } /** @@ -84,6 +78,7 @@ final class OptionsReader { if (value == null || !value.isTrue()) return null; RubyString str = value.convertToString(); + RuntimeInfo info = getRuntimeInfo(); if (info.encodingsSupported() && str.encoding(context) != info.utf8) { str = (RubyString)str.encode(context, info.utf8); } |