diff options
Diffstat (limited to 'src/json/ext/GeneratorState.java')
-rw-r--r-- | src/json/ext/GeneratorState.java | 473 |
1 files changed, 0 insertions, 473 deletions
diff --git a/src/json/ext/GeneratorState.java b/src/json/ext/GeneratorState.java deleted file mode 100644 index dc99000..0000000 --- a/src/json/ext/GeneratorState.java +++ /dev/null @@ -1,473 +0,0 @@ -/* - * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. - * - * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files - * for details. - */ -package json.ext; - -import org.jruby.Ruby; -import org.jruby.RubyBoolean; -import org.jruby.RubyClass; -import org.jruby.RubyHash; -import org.jruby.RubyInteger; -import org.jruby.RubyNumeric; -import org.jruby.RubyObject; -import org.jruby.RubyString; -import org.jruby.anno.JRubyMethod; -import org.jruby.runtime.Block; -import org.jruby.runtime.ObjectAllocator; -import org.jruby.runtime.ThreadContext; -import org.jruby.runtime.Visibility; -import org.jruby.runtime.builtin.IRubyObject; -import org.jruby.util.ByteList; - -/** - * The <code>JSON::Ext::Generator::State</code> class. - * - * <p>This class is used to create State instances, that are use to hold data - * while generating a JSON text from a a Ruby data structure. - * - * @author mernen - */ -public class GeneratorState extends RubyObject { - /** - * The indenting unit string. Will be repeated several times for larger - * indenting levels. - */ - private ByteList indent = ByteList.EMPTY_BYTELIST; - /** - * The spacing to be added after a semicolon on a JSON object. - * @see #spaceBefore - */ - private ByteList space = ByteList.EMPTY_BYTELIST; - /** - * The spacing to be added before a semicolon on a JSON object. - * @see #space - */ - private ByteList spaceBefore = ByteList.EMPTY_BYTELIST; - /** - * Any suffix to be added after the comma for each element on a JSON object. - * It is assumed to be a newline, if set. - */ - private ByteList objectNl = ByteList.EMPTY_BYTELIST; - /** - * Any suffix to be added after the comma for each element on a JSON Array. - * It is assumed to be a newline, if set. - */ - private ByteList arrayNl = ByteList.EMPTY_BYTELIST; - - /** - * The maximum level of nesting of structures allowed. - * <code>0</code> means disabled. - */ - private int maxNesting = DEFAULT_MAX_NESTING; - static final int DEFAULT_MAX_NESTING = 19; - /** - * Whether special float values (<code>NaN</code>, <code>Infinity</code>, - * <code>-Infinity</code>) are accepted. - * If set to <code>false</code>, an exception will be thrown upon - * encountering one. - */ - private boolean allowNaN = DEFAULT_ALLOW_NAN; - static final boolean DEFAULT_ALLOW_NAN = false; - /** - * XXX - */ - 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); - } - }; - - public GeneratorState(Ruby runtime, RubyClass metaClass) { - super(runtime, metaClass); - } - - /** - * <code>State.from_state(opts)</code> - * - * <p>Creates a State object from <code>opts</code>, which ought to be - * {@link RubyHash Hash} to create a new <code>State</code> instance - * configured by <codes>opts</code>, something else to create an - * unconfigured instance. If <code>opts</code> is a <code>State</code> - * object, it is just returned. - * @param clazzParam The receiver of the method call - * ({@link RubyClass} <code>State</code>) - * @param opts The object to use as a base for the new <code>State</code> - * @param block The block passed to the method - * @return A <code>GeneratorState</code> as determined above - */ - @JRubyMethod(meta=true) - public static IRubyObject from_state(ThreadContext context, - IRubyObject klass, IRubyObject opts) { - return fromState(context, opts); - } - - static GeneratorState fromState(ThreadContext context, IRubyObject opts) { - return fromState(context, RuntimeInfo.forRuntime(context.getRuntime()), opts); - } - - static GeneratorState fromState(ThreadContext context, RuntimeInfo info, - IRubyObject opts) { - RubyClass klass = info.generatorStateClass; - if (opts != null) { - // if the given parameter is a Generator::State, return itself - if (klass.isInstance(opts)) return (GeneratorState)opts; - - // if the given parameter is a Hash, pass it to the instantiator - if (context.getRuntime().getHash().isInstance(opts)) { - return (GeneratorState)klass.newInstance(context, - new IRubyObject[] {opts}, Block.NULL_BLOCK); - } - } - - // for other values, return the safe prototype - return (GeneratorState)info.getSafeStatePrototype(context).dup(); - } - - /** - * <code>State#initialize(opts = {})</code> - * - * Instantiates a new <code>State</code> object, configured by <code>opts</code>. - * - * <code>opts</code> can have the following keys: - * - * <dl> - * <dt><code>:indent</code> - * <dd>a {@link RubyString String} used to indent levels (default: <code>""</code>) - * <dt><code>:space</code> - * <dd>a String that is put after a <code>':'</code> or <code>','</code> - * delimiter (default: <code>""</code>) - * <dt><code>:space_before</code> - * <dd>a String that is put before a <code>":"</code> pair delimiter - * (default: <code>""</code>) - * <dt><code>:object_nl</code> - * <dd>a String that is put at the end of a JSON object (default: <code>""</code>) - * <dt><code>:array_nl</code> - * <dd>a String that is put at the end of a JSON array (default: <code>""</code>) - * <dt><code>:allow_nan</code> - * <dd><code>true</code> if <code>NaN</code>, <code>Infinity</code>, and - * <code>-Infinity</code> should be generated, otherwise an exception is - * thrown if these values are encountered. - * This options defaults to <code>false</code>. - */ - @JRubyMethod(optional=1, visibility=Visibility.PRIVATE) - public IRubyObject initialize(ThreadContext context, IRubyObject[] args) { - configure(context, args.length > 0 ? args[0] : null); - return this; - } - - @JRubyMethod - public IRubyObject initialize_copy(ThreadContext context, IRubyObject vOrig) { - Ruby runtime = context.getRuntime(); - if (!(vOrig instanceof GeneratorState)) { - throw runtime.newTypeError(vOrig, getType()); - } - GeneratorState orig = (GeneratorState)vOrig; - this.indent = orig.indent; - this.space = orig.space; - this.spaceBefore = orig.spaceBefore; - this.objectNl = orig.objectNl; - this.arrayNl = orig.arrayNl; - this.maxNesting = orig.maxNesting; - this.allowNaN = orig.allowNaN; - this.asciiOnly = orig.asciiOnly; - this.depth = orig.depth; - return this; - } - - /** - * XXX - */ - @JRubyMethod - public IRubyObject generate(ThreadContext context, IRubyObject obj) { - 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"); - } - return result; - } - - /** - * Ensures the given string is in the form "[...]" or "{...}", being - * possibly surrounded by white space. - * The string's encoding must be ASCII-compatible. - * @param value - * @return - */ - private static boolean objectOrArrayLiteral(RubyString value) { - ByteList bl = value.getByteList(); - int len = bl.length(); - - for (int pos = 0; pos < len - 1; pos++) { - int b = bl.get(pos); - if (Character.isWhitespace(b)) continue; - - // match the opening brace - switch (b) { - case '[': - return matchClosingBrace(bl, pos, len, ']'); - case '{': - return matchClosingBrace(bl, pos, len, '}'); - default: - return false; - } - } - return false; - } - - private static boolean matchClosingBrace(ByteList bl, int pos, int len, - int brace) { - for (int endPos = len - 1; endPos > pos; endPos--) { - int b = bl.get(endPos); - if (Character.isWhitespace(b)) continue; - return b == brace; - } - return false; - } - - @JRubyMethod(name="[]", required=1) - public IRubyObject op_aref(ThreadContext context, IRubyObject vName) { - String name = vName.asJavaString(); - if (getMetaClass().isMethodBound(name, true)) { - return send(context, vName, Block.NULL_BLOCK); - } - return context.getRuntime().getNil(); - } - - public ByteList getIndent() { - return indent; - } - - @JRubyMethod(name="indent") - public RubyString indent_get(ThreadContext context) { - return context.getRuntime().newString(indent); - } - - @JRubyMethod(name="indent=") - public IRubyObject indent_set(ThreadContext context, IRubyObject indent) { - this.indent = prepareByteList(context, indent); - return indent; - } - - public ByteList getSpace() { - return space; - } - - @JRubyMethod(name="space") - public RubyString space_get(ThreadContext context) { - return context.getRuntime().newString(space); - } - - @JRubyMethod(name="space=") - public IRubyObject space_set(ThreadContext context, IRubyObject space) { - this.space = prepareByteList(context, space); - return space; - } - - public ByteList getSpaceBefore() { - return spaceBefore; - } - - @JRubyMethod(name="space_before") - public RubyString space_before_get(ThreadContext context) { - return context.getRuntime().newString(spaceBefore); - } - - @JRubyMethod(name="space_before=") - public IRubyObject space_before_set(ThreadContext context, - IRubyObject spaceBefore) { - this.spaceBefore = prepareByteList(context, spaceBefore); - return spaceBefore; - } - - public ByteList getObjectNl() { - return objectNl; - } - - @JRubyMethod(name="object_nl") - public RubyString object_nl_get(ThreadContext context) { - return context.getRuntime().newString(objectNl); - } - - @JRubyMethod(name="object_nl=") - public IRubyObject object_nl_set(ThreadContext context, - IRubyObject objectNl) { - this.objectNl = prepareByteList(context, objectNl); - return objectNl; - } - - public ByteList getArrayNl() { - return arrayNl; - } - - @JRubyMethod(name="array_nl") - public RubyString array_nl_get(ThreadContext context) { - return context.getRuntime().newString(arrayNl); - } - - @JRubyMethod(name="array_nl=") - public IRubyObject array_nl_set(ThreadContext context, - IRubyObject arrayNl) { - this.arrayNl = prepareByteList(context, arrayNl); - return arrayNl; - } - - @JRubyMethod(name="check_circular?") - public RubyBoolean check_circular_p(ThreadContext context) { - 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); - } - - @JRubyMethod(name="max_nesting=") - public IRubyObject max_nesting_set(IRubyObject max_nesting) { - maxNesting = RubyNumeric.fix2int(max_nesting); - return max_nesting; - } - - public boolean allowNaN() { - return allowNaN; - } - - @JRubyMethod(name="allow_nan?") - public RubyBoolean allow_nan_p(ThreadContext context) { - return context.getRuntime().newBoolean(allowNaN); - } - - public boolean asciiOnly() { - return asciiOnly; - } - - @JRubyMethod(name="ascii_only?") - public RubyBoolean ascii_only_p(ThreadContext context) { - 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(); - 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(); - } - - /** - * <code>State#configure(opts)</code> - * - * <p>Configures this State instance with the {@link RubyHash Hash} - * <code>opts</code>, and returns itself. - * @param vOpts The options hash - * @return The receiver - */ - @JRubyMethod - public IRubyObject configure(ThreadContext context, IRubyObject vOpts) { - OptionsReader opts = new OptionsReader(context, vOpts); - - ByteList indent = opts.getString("indent"); - if (indent != null) this.indent = indent; - - ByteList space = opts.getString("space"); - if (space != null) this.space = space; - - ByteList spaceBefore = opts.getString("space_before"); - if (spaceBefore != null) this.spaceBefore = spaceBefore; - - ByteList arrayNl = opts.getString("array_nl"); - if (arrayNl != null) this.arrayNl = arrayNl; - - ByteList objectNl = opts.getString("object_nl"); - if (objectNl != null) this.objectNl = objectNl; - - maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING); - allowNaN = opts.getBool("allow_nan", DEFAULT_ALLOW_NAN); - asciiOnly = opts.getBool("ascii_only", DEFAULT_ASCII_ONLY); - - depth = opts.getInt("depth", 0); - - return this; - } - - /** - * <code>State#to_h()</code> - * - * <p>Returns the configuration instance variables as a hash, that can be - * passed to the configure method. - * @return - */ - @JRubyMethod - public RubyHash to_h(ThreadContext context) { - Ruby runtime = context.getRuntime(); - RubyHash result = RubyHash.newHash(runtime); - - result.op_aset(context, runtime.newSymbol("indent"), indent_get(context)); - result.op_aset(context, runtime.newSymbol("space"), space_get(context)); - result.op_aset(context, runtime.newSymbol("space_before"), space_before_get(context)); - result.op_aset(context, runtime.newSymbol("object_nl"), object_nl_get(context)); - result.op_aset(context, runtime.newSymbol("array_nl"), array_nl_get(context)); - 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; - } - - public int increaseDepth() { - depth++; - checkMaxNesting(); - return depth; - } - - public int decreaseDepth() { - return --depth; - } - - /** - * Checks if the current depth is allowed as per this state's options. - * @param context - * @param depth The corrent depth - */ - private void checkMaxNesting() { - if (maxNesting != 0 && depth > maxNesting) { - depth--; - throw Utils.newException(getRuntime().getCurrentContext(), - Utils.M_NESTING_ERROR, "nesting of " + depth + " is too deep"); - } - } -} |