2 * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
4 * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
9 import java.lang.ref.WeakReference;
10 import org.jruby.Ruby;
11 import org.jruby.RubyArray;
12 import org.jruby.RubyBoolean;
13 import org.jruby.RubyFixnum;
14 import org.jruby.RubyFloat;
15 import org.jruby.RubyHash;
16 import org.jruby.RubyInteger;
17 import org.jruby.RubyModule;
18 import org.jruby.RubyNumeric;
19 import org.jruby.RubyString;
20 import org.jruby.anno.JRubyMethod;
21 import org.jruby.runtime.ThreadContext;
22 import org.jruby.runtime.builtin.IRubyObject;
23 import org.jruby.util.ByteList;
26 * A class that populates the
27 * <code>Json::Ext::Generator::GeneratorMethods</code> module.
31 class GeneratorMethods {
33 * Populates the given module with all modules and their methods
35 * @param generatorMethodsModule The module to populate
36 * (normally <code>JSON::Generator::GeneratorMethods</code>)
38 static void populate(RuntimeInfo info, RubyModule module) {
39 defineMethods(module, "Array", RbArray.class);
40 defineMethods(module, "FalseClass", RbFalse.class);
41 defineMethods(module, "Float", RbFloat.class);
42 defineMethods(module, "Hash", RbHash.class);
43 defineMethods(module, "Integer", RbInteger.class);
44 defineMethods(module, "NilClass", RbNil.class);
45 defineMethods(module, "Object", RbObject.class);
46 defineMethods(module, "String", RbString.class);
47 defineMethods(module, "TrueClass", RbTrue.class);
49 info.stringExtendModule = new WeakReference<RubyModule>(module.defineModuleUnder("String")
50 .defineModuleUnder("Extend"));
51 info.stringExtendModule.get().defineAnnotatedMethods(StringExtend.class);
55 * Convenience method for defining methods on a submodule.
57 * @param submoduleName
60 private static void defineMethods(RubyModule parentModule,
61 String submoduleName, Class klass) {
62 RubyModule submodule = parentModule.defineModuleUnder(submoduleName);
63 submodule.defineAnnotatedMethods(klass);
67 public static class RbHash {
68 @JRubyMethod(rest=true)
69 public static IRubyObject to_json(ThreadContext context,
70 IRubyObject vSelf, IRubyObject[] args) {
71 return Generator.generateJson(context, (RubyHash)vSelf,
72 Generator.HASH_HANDLER, args);
76 public static class RbArray {
77 @JRubyMethod(rest=true)
78 public static IRubyObject to_json(ThreadContext context,
79 IRubyObject vSelf, IRubyObject[] args) {
80 return Generator.generateJson(context, (RubyArray)vSelf,
81 Generator.ARRAY_HANDLER, args);
85 public static class RbInteger {
86 @JRubyMethod(rest=true)
87 public static IRubyObject to_json(ThreadContext context,
88 IRubyObject vSelf, IRubyObject[] args) {
89 return Generator.generateJson(context, vSelf, args);
93 public static class RbFloat {
94 @JRubyMethod(rest=true)
95 public static IRubyObject to_json(ThreadContext context,
96 IRubyObject vSelf, IRubyObject[] args) {
97 return Generator.generateJson(context, (RubyFloat)vSelf,
98 Generator.FLOAT_HANDLER, args);
102 public static class RbString {
103 @JRubyMethod(rest=true)
104 public static IRubyObject to_json(ThreadContext context,
105 IRubyObject vSelf, IRubyObject[] args) {
106 return Generator.generateJson(context, (RubyString)vSelf,
107 Generator.STRING_HANDLER, args);
111 * <code>{@link RubyString String}#to_json_raw(*)</code>
113 * <p>This method creates a JSON text from the result of a call to
114 * {@link #to_json_raw_object} of this String.
116 @JRubyMethod(rest=true)
117 public static IRubyObject to_json_raw(ThreadContext context,
118 IRubyObject vSelf, IRubyObject[] args) {
119 RubyHash obj = toJsonRawObject(context, Utils.ensureString(vSelf));
120 return Generator.generateJson(context, obj,
121 Generator.HASH_HANDLER, args);
125 * <code>{@link RubyString String}#to_json_raw_object(*)</code>
127 * <p>This method creates a raw object Hash, that can be nested into
128 * other data structures and will be unparsed as a raw string. This
129 * method should be used if you want to convert raw strings to JSON
130 * instead of UTF-8 strings, e.g. binary data.
132 @JRubyMethod(rest=true)
133 public static IRubyObject to_json_raw_object(ThreadContext context,
134 IRubyObject vSelf, IRubyObject[] args) {
135 return toJsonRawObject(context, Utils.ensureString(vSelf));
138 private static RubyHash toJsonRawObject(ThreadContext context,
140 Ruby runtime = context.getRuntime();
141 RubyHash result = RubyHash.newHash(runtime);
143 IRubyObject createId = RuntimeInfo.forRuntime(runtime)
144 .jsonModule.get().callMethod(context, "create_id");
145 result.op_aset(context, createId, self.getMetaClass().to_s());
147 ByteList bl = self.getByteList();
148 byte[] uBytes = bl.unsafeBytes();
149 RubyArray array = runtime.newArray(bl.length());
150 for (int i = bl.begin(), t = bl.begin() + bl.length(); i < t; i++) {
151 array.store(i, runtime.newFixnum(uBytes[i] & 0xff));
154 result.op_aset(context, runtime.newString("raw"), array);
158 @JRubyMethod(required=1, module=true)
159 public static IRubyObject included(ThreadContext context,
160 IRubyObject vSelf, IRubyObject module) {
161 RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime());
162 return module.callMethod(context, "extend", info.stringExtendModule.get());
166 public static class StringExtend {
168 * <code>{@link RubyString String}#json_create(o)</code>
170 * <p>Raw Strings are JSON Objects (the raw bytes are stored in an
171 * array for the key "raw"). The Ruby String can be created by this
174 @JRubyMethod(required=1)
175 public static IRubyObject json_create(ThreadContext context,
176 IRubyObject vSelf, IRubyObject vHash) {
177 Ruby runtime = context.getRuntime();
178 RubyHash o = vHash.convertToHash();
179 IRubyObject rawData = o.fastARef(runtime.newString("raw"));
180 if (rawData == null) {
181 throw runtime.newArgumentError("\"raw\" value not defined "
182 + "for encoded String");
184 RubyArray ary = Utils.ensureArray(rawData);
185 byte[] bytes = new byte[ary.getLength()];
186 for (int i = 0, t = ary.getLength(); i < t; i++) {
187 IRubyObject element = ary.eltInternal(i);
188 if (element instanceof RubyFixnum) {
189 bytes[i] = (byte)RubyNumeric.fix2long(element);
191 throw runtime.newTypeError(element, runtime.getFixnum());
194 return runtime.newString(new ByteList(bytes, false));
198 public static class RbTrue {
199 @JRubyMethod(rest=true)
200 public static IRubyObject to_json(ThreadContext context,
201 IRubyObject vSelf, IRubyObject[] args) {
202 return Generator.generateJson(context, (RubyBoolean)vSelf,
203 Generator.TRUE_HANDLER, args);
207 public static class RbFalse {
208 @JRubyMethod(rest=true)
209 public static IRubyObject to_json(ThreadContext context,
210 IRubyObject vSelf, IRubyObject[] args) {
211 return Generator.generateJson(context, (RubyBoolean)vSelf,
212 Generator.FALSE_HANDLER, args);
216 public static class RbNil {
217 @JRubyMethod(rest=true)
218 public static IRubyObject to_json(ThreadContext context,
219 IRubyObject vSelf, IRubyObject[] args) {
220 return Generator.generateJson(context, vSelf,
221 Generator.NIL_HANDLER, args);
225 public static class RbObject {
226 @JRubyMethod(rest=true)
227 public static IRubyObject to_json(ThreadContext context,
228 IRubyObject self, IRubyObject[] args) {
229 return RbString.to_json(context, self.asString(), args);