--- /dev/null
+/*
+ * 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 java.lang.ref.WeakReference;
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyBoolean;
+import org.jruby.RubyFixnum;
+import org.jruby.RubyFloat;
+import org.jruby.RubyHash;
+import org.jruby.RubyInteger;
+import org.jruby.RubyModule;
+import org.jruby.RubyNumeric;
+import org.jruby.RubyString;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.util.ByteList;
+
+/**
+ * A class that populates the
+ * <code>Json::Ext::Generator::GeneratorMethods</code> module.
+ *
+ * @author mernen
+ */
+class GeneratorMethods {
+ /**
+ * Populates the given module with all modules and their methods
+ * @param info
+ * @param generatorMethodsModule The module to populate
+ * (normally <code>JSON::Generator::GeneratorMethods</code>)
+ */
+ static void populate(RuntimeInfo info, RubyModule module) {
+ defineMethods(module, "Array", RbArray.class);
+ defineMethods(module, "FalseClass", RbFalse.class);
+ defineMethods(module, "Float", RbFloat.class);
+ defineMethods(module, "Hash", RbHash.class);
+ defineMethods(module, "Integer", RbInteger.class);
+ defineMethods(module, "NilClass", RbNil.class);
+ defineMethods(module, "Object", RbObject.class);
+ defineMethods(module, "String", RbString.class);
+ defineMethods(module, "TrueClass", RbTrue.class);
+
+ info.stringExtendModule = new WeakReference<RubyModule>(module.defineModuleUnder("String")
+ .defineModuleUnder("Extend"));
+ info.stringExtendModule.get().defineAnnotatedMethods(StringExtend.class);
+ }
+
+ /**
+ * Convenience method for defining methods on a submodule.
+ * @param parentModule
+ * @param submoduleName
+ * @param klass
+ */
+ private static void defineMethods(RubyModule parentModule,
+ String submoduleName, Class klass) {
+ RubyModule submodule = parentModule.defineModuleUnder(submoduleName);
+ submodule.defineAnnotatedMethods(klass);
+ }
+
+
+ public static class RbHash {
+ @JRubyMethod(rest=true)
+ public static IRubyObject to_json(ThreadContext context,
+ IRubyObject vSelf, IRubyObject[] args) {
+ return Generator.generateJson(context, (RubyHash)vSelf,
+ Generator.HASH_HANDLER, args);
+ }
+ }
+
+ public static class RbArray {
+ @JRubyMethod(rest=true)
+ public static IRubyObject to_json(ThreadContext context,
+ IRubyObject vSelf, IRubyObject[] args) {
+ return Generator.generateJson(context, (RubyArray)vSelf,
+ Generator.ARRAY_HANDLER, args);
+ }
+ }
+
+ public static class RbInteger {
+ @JRubyMethod(rest=true)
+ public static IRubyObject to_json(ThreadContext context,
+ IRubyObject vSelf, IRubyObject[] args) {
+ return Generator.generateJson(context, vSelf, args);
+ }
+ }
+
+ public static class RbFloat {
+ @JRubyMethod(rest=true)
+ public static IRubyObject to_json(ThreadContext context,
+ IRubyObject vSelf, IRubyObject[] args) {
+ return Generator.generateJson(context, (RubyFloat)vSelf,
+ Generator.FLOAT_HANDLER, args);
+ }
+ }
+
+ public static class RbString {
+ @JRubyMethod(rest=true)
+ public static IRubyObject to_json(ThreadContext context,
+ IRubyObject vSelf, IRubyObject[] args) {
+ return Generator.generateJson(context, (RubyString)vSelf,
+ Generator.STRING_HANDLER, args);
+ }
+
+ /**
+ * <code>{@link RubyString String}#to_json_raw(*)</code>
+ *
+ * <p>This method creates a JSON text from the result of a call to
+ * {@link #to_json_raw_object} of this String.
+ */
+ @JRubyMethod(rest=true)
+ public static IRubyObject to_json_raw(ThreadContext context,
+ IRubyObject vSelf, IRubyObject[] args) {
+ RubyHash obj = toJsonRawObject(context, Utils.ensureString(vSelf));
+ return Generator.generateJson(context, obj,
+ Generator.HASH_HANDLER, args);
+ }
+
+ /**
+ * <code>{@link RubyString String}#to_json_raw_object(*)</code>
+ *
+ * <p>This method creates a raw object Hash, that can be nested into
+ * other data structures and will be unparsed as a raw string. This
+ * method should be used if you want to convert raw strings to JSON
+ * instead of UTF-8 strings, e.g. binary data.
+ */
+ @JRubyMethod(rest=true)
+ public static IRubyObject to_json_raw_object(ThreadContext context,
+ IRubyObject vSelf, IRubyObject[] args) {
+ return toJsonRawObject(context, Utils.ensureString(vSelf));
+ }
+
+ private static RubyHash toJsonRawObject(ThreadContext context,
+ RubyString self) {
+ Ruby runtime = context.getRuntime();
+ RubyHash result = RubyHash.newHash(runtime);
+
+ IRubyObject createId = RuntimeInfo.forRuntime(runtime)
+ .jsonModule.get().callMethod(context, "create_id");
+ result.op_aset(context, createId, self.getMetaClass().to_s());
+
+ ByteList bl = self.getByteList();
+ byte[] uBytes = bl.unsafeBytes();
+ RubyArray array = runtime.newArray(bl.length());
+ for (int i = bl.begin(), t = bl.begin() + bl.length(); i < t; i++) {
+ array.store(i, runtime.newFixnum(uBytes[i] & 0xff));
+ }
+
+ result.op_aset(context, runtime.newString("raw"), array);
+ return result;
+ }
+
+ @JRubyMethod(required=1, module=true)
+ public static IRubyObject included(ThreadContext context,
+ IRubyObject vSelf, IRubyObject module) {
+ RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime());
+ return module.callMethod(context, "extend", info.stringExtendModule.get());
+ }
+ }
+
+ public static class StringExtend {
+ /**
+ * <code>{@link RubyString String}#json_create(o)</code>
+ *
+ * <p>Raw Strings are JSON Objects (the raw bytes are stored in an
+ * array for the key "raw"). The Ruby String can be created by this
+ * module method.
+ */
+ @JRubyMethod(required=1)
+ public static IRubyObject json_create(ThreadContext context,
+ IRubyObject vSelf, IRubyObject vHash) {
+ Ruby runtime = context.getRuntime();
+ RubyHash o = vHash.convertToHash();
+ IRubyObject rawData = o.fastARef(runtime.newString("raw"));
+ if (rawData == null) {
+ throw runtime.newArgumentError("\"raw\" value not defined "
+ + "for encoded String");
+ }
+ RubyArray ary = Utils.ensureArray(rawData);
+ byte[] bytes = new byte[ary.getLength()];
+ for (int i = 0, t = ary.getLength(); i < t; i++) {
+ IRubyObject element = ary.eltInternal(i);
+ if (element instanceof RubyFixnum) {
+ bytes[i] = (byte)RubyNumeric.fix2long(element);
+ } else {
+ throw runtime.newTypeError(element, runtime.getFixnum());
+ }
+ }
+ return runtime.newString(new ByteList(bytes, false));
+ }
+ }
+
+ public static class RbTrue {
+ @JRubyMethod(rest=true)
+ public static IRubyObject to_json(ThreadContext context,
+ IRubyObject vSelf, IRubyObject[] args) {
+ return Generator.generateJson(context, (RubyBoolean)vSelf,
+ Generator.TRUE_HANDLER, args);
+ }
+ }
+
+ public static class RbFalse {
+ @JRubyMethod(rest=true)
+ public static IRubyObject to_json(ThreadContext context,
+ IRubyObject vSelf, IRubyObject[] args) {
+ return Generator.generateJson(context, (RubyBoolean)vSelf,
+ Generator.FALSE_HANDLER, args);
+ }
+ }
+
+ public static class RbNil {
+ @JRubyMethod(rest=true)
+ public static IRubyObject to_json(ThreadContext context,
+ IRubyObject vSelf, IRubyObject[] args) {
+ return Generator.generateJson(context, vSelf,
+ Generator.NIL_HANDLER, args);
+ }
+ }
+
+ public static class RbObject {
+ @JRubyMethod(rest=true)
+ public static IRubyObject to_json(ThreadContext context,
+ IRubyObject self, IRubyObject[] args) {
+ return RbString.to_json(context, self.asString(), args);
+ }
+ }
+}