78dc0782fffb34787e613ef6b71907ad2142aa68
[packages/precise/mcollective.git] / lib / mcollective / vendor / json / java / src / json / ext / Generator.java
1 /*
2  * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
3  *
4  * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
5  * for details.
6  */
7 package json.ext;
8
9 import org.jruby.Ruby;
10 import org.jruby.RubyArray;
11 import org.jruby.RubyBignum;
12 import org.jruby.RubyBoolean;
13 import org.jruby.RubyClass;
14 import org.jruby.RubyFixnum;
15 import org.jruby.RubyFloat;
16 import org.jruby.RubyHash;
17 import org.jruby.RubyNumeric;
18 import org.jruby.RubyString;
19 import org.jruby.runtime.ThreadContext;
20 import org.jruby.runtime.builtin.IRubyObject;
21 import org.jruby.util.ByteList;
22
23 public final class Generator {
24     private Generator() {
25         throw new RuntimeException();
26     }
27
28     /**
29      * Encodes the given object as a JSON string, using the given handler.
30      */
31     static <T extends IRubyObject> RubyString
32             generateJson(ThreadContext context, T object,
33                          Handler<? super T> handler, IRubyObject[] args) {
34         Session session = new Session(context, args.length > 0 ? args[0]
35                                                                : null);
36         return session.infect(handler.generateNew(session, object));
37     }
38
39     /**
40      * Encodes the given object as a JSON string, detecting the appropriate handler
41      * for the given object.
42      */
43     static <T extends IRubyObject> RubyString
44             generateJson(ThreadContext context, T object, IRubyObject[] args) {
45         Handler<? super T> handler = getHandlerFor(context.getRuntime(), object);
46         return generateJson(context, object, handler, args);
47     }
48
49     /**
50      * Encodes the given object as a JSON string, using the appropriate
51      * handler if one is found or calling #to_json if not.
52      */
53     public static <T extends IRubyObject> RubyString
54             generateJson(ThreadContext context, T object,
55                          GeneratorState config) {
56         Session session = new Session(context, config);
57         Handler<? super T> handler = getHandlerFor(context.getRuntime(), object);
58         return handler.generateNew(session, object);
59     }
60
61     /**
62      * Returns the best serialization handler for the given object.
63      */
64     // Java's generics can't handle this satisfactorily, so I'll just leave
65     // the best I could get and ignore the warnings
66     @SuppressWarnings("unchecked")
67     private static <T extends IRubyObject>
68             Handler<? super T> getHandlerFor(Ruby runtime, T object) {
69         RubyClass metaClass = object.getMetaClass();
70         if (metaClass == runtime.getString()) return (Handler)STRING_HANDLER;
71         if (metaClass == runtime.getFixnum()) return (Handler)FIXNUM_HANDLER;
72         if (metaClass == runtime.getHash())   return (Handler)HASH_HANDLER;
73         if (metaClass == runtime.getArray())  return (Handler)ARRAY_HANDLER;
74         if (object.isNil())                   return (Handler)NIL_HANDLER;
75         if (object == runtime.getTrue())      return (Handler)TRUE_HANDLER;
76         if (object == runtime.getFalse())     return (Handler)FALSE_HANDLER;
77         if (metaClass == runtime.getFloat())  return (Handler)FLOAT_HANDLER;
78         if (metaClass == runtime.getBignum()) return (Handler)BIGNUM_HANDLER;
79         return GENERIC_HANDLER;
80     }
81
82
83     /* Generator context */
84
85     /**
86      * A class that concentrates all the information that is shared by
87      * generators working on a single session.
88      *
89      * <p>A session is defined as the process of serializing a single root
90      * object; any handler directly called by container handlers (arrays and
91      * hashes/objects) shares this object with its caller.
92      *
93      * <p>Note that anything called indirectly (via {@link GENERIC_HANDLER})
94      * won't be part of the session.
95      */
96     static class Session {
97         private final ThreadContext context;
98         private GeneratorState state;
99         private IRubyObject possibleState;
100         private RuntimeInfo info;
101         private StringEncoder stringEncoder;
102
103         private boolean tainted = false;
104         private boolean untrusted = false;
105
106         Session(ThreadContext context, GeneratorState state) {
107             this.context = context;
108             this.state = state;
109         }
110
111         Session(ThreadContext context, IRubyObject possibleState) {
112             this.context = context;
113             this.possibleState = possibleState == null || possibleState.isNil()
114                     ? null : possibleState;
115         }
116
117         public ThreadContext getContext() {
118             return context;
119         }
120
121         public Ruby getRuntime() {
122             return context.getRuntime();
123         }
124
125         public GeneratorState getState() {
126             if (state == null) {
127                 state = GeneratorState.fromState(context, getInfo(), possibleState);
128             }
129             return state;
130         }
131
132         public RuntimeInfo getInfo() {
133             if (info == null) info = RuntimeInfo.forRuntime(getRuntime());
134             return info;
135         }
136
137         public StringEncoder getStringEncoder() {
138             if (stringEncoder == null) {
139                 stringEncoder = new StringEncoder(context, getState().asciiOnly());
140             }
141             return stringEncoder;
142         }
143
144         public void infectBy(IRubyObject object) {
145             if (object.isTaint()) tainted = true;
146             if (object.isUntrusted()) untrusted = true;
147         }
148
149         public <T extends IRubyObject> T infect(T object) {
150             if (tainted) object.setTaint(true);
151             if (untrusted) object.setUntrusted(true);
152             return object;
153         }
154     }
155
156
157     /* Handler base classes */
158
159     private static abstract class Handler<T extends IRubyObject> {
160         /**
161          * Returns an estimative of how much space the serialization of the
162          * given object will take. Used for allocating enough buffer space
163          * before invoking other methods.
164          */
165         int guessSize(Session session, T object) {
166             return 4;
167         }
168
169         RubyString generateNew(Session session, T object) {
170             ByteList buffer = new ByteList(guessSize(session, object));
171             generate(session, object, buffer);
172             return RubyString.newString(session.getRuntime(), buffer);
173         }
174
175         abstract void generate(Session session, T object, ByteList buffer);
176     }
177
178     /**
179      * A handler that returns a fixed keyword regardless of the passed object.
180      */
181     private static class KeywordHandler<T extends IRubyObject>
182             extends Handler<T> {
183         private final ByteList keyword;
184
185         private KeywordHandler(String keyword) {
186             this.keyword = new ByteList(ByteList.plain(keyword), false);
187         }
188
189         @Override
190         int guessSize(Session session, T object) {
191             return keyword.length();
192         }
193
194         @Override
195         RubyString generateNew(Session session, T object) {
196             return RubyString.newStringShared(session.getRuntime(), keyword);
197         }
198
199         @Override
200         void generate(Session session, T object, ByteList buffer) {
201             buffer.append(keyword);
202         }
203     }
204
205
206     /* Handlers */
207
208     static final Handler<RubyBignum> BIGNUM_HANDLER =
209         new Handler<RubyBignum>() {
210             @Override
211             void generate(Session session, RubyBignum object, ByteList buffer) {
212                 // JRUBY-4751: RubyBignum.to_s() returns generic object
213                 // representation (fixed in 1.5, but we maintain backwards
214                 // compatibility; call to_s(IRubyObject[]) then
215                 buffer.append(((RubyString)object.to_s(IRubyObject.NULL_ARRAY)).getByteList());
216             }
217         };
218
219     static final Handler<RubyFixnum> FIXNUM_HANDLER =
220         new Handler<RubyFixnum>() {
221             @Override
222             void generate(Session session, RubyFixnum object, ByteList buffer) {
223                 buffer.append(object.to_s().getByteList());
224             }
225         };
226
227     static final Handler<RubyFloat> FLOAT_HANDLER =
228         new Handler<RubyFloat>() {
229             @Override
230             void generate(Session session, RubyFloat object, ByteList buffer) {
231                 double value = RubyFloat.num2dbl(object);
232
233                 if (Double.isInfinite(value) || Double.isNaN(value)) {
234                     if (!session.getState().allowNaN()) {
235                         throw Utils.newException(session.getContext(),
236                                 Utils.M_GENERATOR_ERROR,
237                                 object + " not allowed in JSON");
238                     }
239                 }
240                 buffer.append(((RubyString)object.to_s()).getByteList());
241             }
242         };
243
244     static final Handler<RubyArray> ARRAY_HANDLER =
245         new Handler<RubyArray>() {
246             @Override
247             int guessSize(Session session, RubyArray object) {
248                 GeneratorState state = session.getState();
249                 int depth = state.getDepth();
250                 int perItem =
251                     4                                           // prealloc
252                     + (depth + 1) * state.getIndent().length()  // indent
253                     + 1 + state.getArrayNl().length();          // ',' arrayNl
254                 return 2 + object.size() * perItem;
255             }
256
257             @Override
258             void generate(Session session, RubyArray object, ByteList buffer) {
259                 ThreadContext context = session.getContext();
260                 Ruby runtime = context.getRuntime();
261                 GeneratorState state = session.getState();
262                 int depth = state.increaseDepth();
263
264                 ByteList indentUnit = state.getIndent();
265                 byte[] shift = Utils.repeat(indentUnit, depth);
266
267                 ByteList arrayNl = state.getArrayNl();
268                 byte[] delim = new byte[1 + arrayNl.length()];
269                 delim[0] = ',';
270                 System.arraycopy(arrayNl.unsafeBytes(), arrayNl.begin(), delim, 1,
271                         arrayNl.length());
272
273                 session.infectBy(object);
274
275                 buffer.append((byte)'[');
276                 buffer.append(arrayNl);
277                 boolean firstItem = true;
278                 for (int i = 0, t = object.getLength(); i < t; i++) {
279                     IRubyObject element = object.eltInternal(i);
280                     session.infectBy(element);
281                     if (firstItem) {
282                         firstItem = false;
283                     } else {
284                         buffer.append(delim);
285                     }
286                     buffer.append(shift);
287                     Handler<IRubyObject> handler = getHandlerFor(runtime, element);
288                     handler.generate(session, element, buffer);
289                 }
290
291                 state.decreaseDepth();
292                 if (arrayNl.length() != 0) {
293                     buffer.append(arrayNl);
294                     buffer.append(shift, 0, state.getDepth() * indentUnit.length());
295                 }
296
297                 buffer.append((byte)']');
298             }
299         };
300
301     static final Handler<RubyHash> HASH_HANDLER =
302         new Handler<RubyHash>() {
303             @Override
304             int guessSize(Session session, RubyHash object) {
305                 GeneratorState state = session.getState();
306                 int perItem =
307                     12    // key, colon, comma
308                     + (state.getDepth() + 1) * state.getIndent().length()
309                     + state.getSpaceBefore().length()
310                     + state.getSpace().length();
311                 return 2 + object.size() * perItem;
312             }
313
314             @Override
315             void generate(final Session session, RubyHash object,
316                           final ByteList buffer) {
317                 ThreadContext context = session.getContext();
318                 final Ruby runtime = context.getRuntime();
319                 final GeneratorState state = session.getState();
320                 final int depth = state.increaseDepth();
321
322                 final ByteList objectNl = state.getObjectNl();
323                 final byte[] indent = Utils.repeat(state.getIndent(), depth);
324                 final ByteList spaceBefore = state.getSpaceBefore();
325                 final ByteList space = state.getSpace();
326
327                 buffer.append((byte)'{');
328                 buffer.append(objectNl);
329                 object.visitAll(new RubyHash.Visitor() {
330                     private boolean firstPair = true;
331
332                     @Override
333                     public void visit(IRubyObject key, IRubyObject value) {
334                         if (firstPair) {
335                             firstPair = false;
336                         } else {
337                             buffer.append((byte)',');
338                             buffer.append(objectNl);
339                         }
340                         if (objectNl.length() != 0) buffer.append(indent);
341
342                         STRING_HANDLER.generate(session, key.asString(), buffer);
343                         session.infectBy(key);
344
345                         buffer.append(spaceBefore);
346                         buffer.append((byte)':');
347                         buffer.append(space);
348
349                         Handler<IRubyObject> valueHandler = getHandlerFor(runtime, value);
350                         valueHandler.generate(session, value, buffer);
351                         session.infectBy(value);
352                     }
353                 });
354                 state.decreaseDepth();
355                 if (objectNl.length() != 0) {
356                     buffer.append(objectNl);
357                     buffer.append(Utils.repeat(state.getIndent(), state.getDepth()));
358                 }
359                 buffer.append((byte)'}');
360             }
361         };
362
363     static final Handler<RubyString> STRING_HANDLER =
364         new Handler<RubyString>() {
365             @Override
366             int guessSize(Session session, RubyString object) {
367                 // for most applications, most strings will be just a set of
368                 // printable ASCII characters without any escaping, so let's
369                 // just allocate enough space for that + the quotes
370                 return 2 + object.getByteList().length();
371             }
372
373             @Override
374             void generate(Session session, RubyString object, ByteList buffer) {
375                 RuntimeInfo info = session.getInfo();
376                 RubyString src;
377
378                 if (info.encodingsSupported() &&
379                         object.encoding(session.getContext()) != info.utf8.get()) {
380                     src = (RubyString)object.encode(session.getContext(),
381                                                     info.utf8.get());
382                 } else {
383                     src = object;
384                 }
385
386                 session.getStringEncoder().encode(src.getByteList(), buffer);
387             }
388         };
389
390     static final Handler<RubyBoolean> TRUE_HANDLER =
391         new KeywordHandler<RubyBoolean>("true");
392     static final Handler<RubyBoolean> FALSE_HANDLER =
393         new KeywordHandler<RubyBoolean>("false");
394     static final Handler<IRubyObject> NIL_HANDLER =
395         new KeywordHandler<IRubyObject>("null");
396
397     /**
398      * The default handler (<code>Object#to_json</code>): coerces the object
399      * to string using <code>#to_s</code>, and serializes that string.
400      */
401     static final Handler<IRubyObject> OBJECT_HANDLER =
402         new Handler<IRubyObject>() {
403             @Override
404             RubyString generateNew(Session session, IRubyObject object) {
405                 RubyString str = object.asString();
406                 return STRING_HANDLER.generateNew(session, str);
407             }
408
409             @Override
410             void generate(Session session, IRubyObject object, ByteList buffer) {
411                 RubyString str = object.asString();
412                 STRING_HANDLER.generate(session, str, buffer);
413             }
414         };
415
416     /**
417      * A handler that simply calls <code>#to_json(state)</code> on the
418      * given object.
419      */
420     static final Handler<IRubyObject> GENERIC_HANDLER =
421         new Handler<IRubyObject>() {
422             @Override
423             RubyString generateNew(Session session, IRubyObject object) {
424                 IRubyObject result =
425                     object.callMethod(session.getContext(), "to_json",
426                           new IRubyObject[] {session.getState()});
427                 if (result instanceof RubyString) return (RubyString)result;
428                 throw session.getRuntime().newTypeError("to_json must return a String");
429             }
430
431             @Override
432             void generate(Session session, IRubyObject object, ByteList buffer) {
433                 RubyString result = generateNew(session, object);
434                 buffer.append(result.getByteList());
435             }
436         };
437 }