jrjackson 0.0.7 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,29 @@
1
+ package com.jrjackson;
2
+
3
+ import org.jruby.Ruby;
4
+ import org.jruby.RubyClass;
5
+ import org.jruby.RubyModule;
6
+ import org.jruby.runtime.ObjectAllocator;
7
+ import org.jruby.runtime.builtin.IRubyObject;
8
+ import org.jruby.runtime.load.BasicLibraryService;
9
+
10
+ import java.io.IOException;
11
+
12
+ public class JrJacksonService implements BasicLibraryService {
13
+ public boolean basicLoad(final Ruby ruby) throws IOException {
14
+ RubyModule jr_jackson = ruby.defineModule("JrJackson");
15
+
16
+ RubyModule jr_jackson_str = ruby.defineModuleUnder("Str", jr_jackson);
17
+ jr_jackson_str.defineAnnotatedMethods(JrJacksonStr.class);
18
+
19
+ RubyModule jr_jackson_raw = ruby.defineModuleUnder("Raw", jr_jackson);
20
+ jr_jackson_raw.defineAnnotatedMethods(JrJacksonRaw.class);
21
+
22
+ RubyModule jr_jackson_sym = ruby.defineModuleUnder("Sym", jr_jackson);
23
+ jr_jackson_sym.defineAnnotatedMethods(JrJacksonSym.class);
24
+
25
+ RubyClass runtimeError = ruby.getRuntimeError();
26
+ RubyClass parseError = jr_jackson.defineClassUnder("ParseError", runtimeError, runtimeError.getAllocator());
27
+ return true;
28
+ }
29
+ }
@@ -0,0 +1,80 @@
1
+ package com.jrjackson;
2
+
3
+ import org.jruby.Ruby;
4
+ import org.jruby.RubyClass;
5
+ import org.jruby.RubyObject;
6
+ import org.jruby.RubyString;
7
+ import org.jruby.RubyIO;
8
+ import org.jruby.anno.JRubyMethod;
9
+ import org.jruby.anno.JRubyModule;
10
+ import org.jruby.exceptions.RaiseException;
11
+ import org.jruby.ext.stringio.RubyStringIO;
12
+ import org.jruby.java.addons.IOJavaAddons;
13
+ import org.jruby.javasupport.JavaUtil;
14
+ import org.jruby.runtime.ThreadContext;
15
+ import org.jruby.runtime.builtin.IRubyObject;
16
+ import org.jruby.util.RubyDateFormat;
17
+
18
+ import java.io.InputStream;
19
+ import java.io.IOException;
20
+ import java.text.DateFormat;
21
+ import java.util.*;
22
+
23
+ import com.fasterxml.jackson.databind.ObjectMapper;
24
+ import com.fasterxml.jackson.databind.module.SimpleModule;
25
+ import com.fasterxml.jackson.core.Version;
26
+ import com.fasterxml.jackson.core.JsonProcessingException;
27
+
28
+ @JRubyModule(name = "JrJacksonStr")
29
+ public class JrJacksonStr extends RubyObject {
30
+ private static final ObjectMapper mapper = new ObjectMapper();
31
+ private static final SimpleModule module = new SimpleModule("JrJacksonStrModule");
32
+
33
+ static {
34
+ mapper.registerModule(module.addDeserializer(Object.class, RubyObjectDeserializer.instance));
35
+ mapper.setDateFormat(new RubyDateFormat("yyyy-MM-dd HH:mm:ss z", Locale.US, true));
36
+ }
37
+
38
+ public JrJacksonStr(Ruby ruby, RubyClass metaclass) {
39
+ super(ruby, metaclass);
40
+ }
41
+
42
+ @JRubyMethod(module = true, name = {"parse", "load"}, required = 1)
43
+ public static IRubyObject parse(ThreadContext context, IRubyObject self, IRubyObject arg) {
44
+ Ruby ruby = context.getRuntime();
45
+ try {
46
+ Object o;
47
+ if (arg instanceof RubyString) {
48
+ o = mapper.readValue(arg.toString(), Object.class);
49
+ } else if ((arg instanceof RubyIO) || (arg instanceof RubyStringIO)) {
50
+ IRubyObject stream = IOJavaAddons.AnyIO.any_to_inputstream(context, arg);
51
+ o = mapper.readValue((InputStream)stream.toJava(InputStream.class), Object.class);
52
+ } else {
53
+ throw ruby.newArgumentError("Unsupported source. This method accepts String or IO");
54
+ }
55
+ return (RubyObject)JavaUtil.convertJavaToRuby(ruby, o);
56
+ }
57
+ catch (JsonProcessingException e) {
58
+ throw ParseError.newParseError(ruby, e.getLocalizedMessage());
59
+ }
60
+ catch (IOException e) {
61
+ throw ruby.newIOError(e.getLocalizedMessage());
62
+ }
63
+ }
64
+
65
+ @JRubyMethod(module = true, name = {"generate", "dump"}, required = 1)
66
+ public static IRubyObject generate(ThreadContext context, IRubyObject self, IRubyObject arg) {
67
+ Ruby ruby = context.getRuntime();
68
+ Object obj = arg.toJava(Object.class);
69
+ try {
70
+ String s = mapper.writeValueAsString(obj);
71
+ return ruby.newString(s);
72
+ }
73
+ catch (JsonProcessingException e) {
74
+ throw ParseError.newParseError(ruby, e.getLocalizedMessage());
75
+ }
76
+ catch (IOException e) {
77
+ throw ruby.newIOError(e.getLocalizedMessage());
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,80 @@
1
+ package com.jrjackson;
2
+
3
+ import org.jruby.Ruby;
4
+ import org.jruby.RubyClass;
5
+ import org.jruby.RubyObject;
6
+ import org.jruby.RubyString;
7
+ import org.jruby.RubyIO;
8
+ import org.jruby.anno.JRubyMethod;
9
+ import org.jruby.anno.JRubyModule;
10
+ import org.jruby.exceptions.RaiseException;
11
+ import org.jruby.ext.stringio.RubyStringIO;
12
+ import org.jruby.java.addons.IOJavaAddons;
13
+ import org.jruby.javasupport.JavaUtil;
14
+ import org.jruby.runtime.ThreadContext;
15
+ import org.jruby.runtime.builtin.IRubyObject;
16
+ import org.jruby.util.RubyDateFormat;
17
+
18
+ import java.io.InputStream;
19
+ import java.io.IOException;
20
+ import java.text.DateFormat;
21
+ import java.util.*;
22
+
23
+ import com.fasterxml.jackson.databind.ObjectMapper;
24
+ import com.fasterxml.jackson.databind.module.SimpleModule;
25
+ import com.fasterxml.jackson.core.Version;
26
+ import com.fasterxml.jackson.core.JsonProcessingException;
27
+
28
+ @JRubyModule(name = "JrJacksonSym")
29
+ public class JrJacksonSym extends RubyObject {
30
+ private static final ObjectMapper mapper = new ObjectMapper();
31
+ private static final SimpleModule module = new SimpleModule("JrJacksonSymModule");
32
+
33
+ static {
34
+ mapper.registerModule(module.addDeserializer(Object.class, RubyObjectSymDeserializer.instance));
35
+ mapper.setDateFormat(new RubyDateFormat("yyyy-MM-dd HH:mm:ss z", Locale.US, true));
36
+ }
37
+
38
+ public JrJacksonSym(Ruby ruby, RubyClass metaclass) {
39
+ super(ruby, metaclass);
40
+ }
41
+
42
+ @JRubyMethod(module = true, name = {"parse", "load"}, required = 1)
43
+ public static IRubyObject parse(ThreadContext context, IRubyObject self, IRubyObject arg) {
44
+ Ruby ruby = context.getRuntime();
45
+ try {
46
+ Object o;
47
+ if (arg instanceof RubyString) {
48
+ o = mapper.readValue(arg.toString(), Object.class);
49
+ } else if ((arg instanceof RubyIO) || (arg instanceof RubyStringIO)) {
50
+ IRubyObject stream = IOJavaAddons.AnyIO.any_to_inputstream(context, arg);
51
+ o = mapper.readValue((InputStream)stream.toJava(InputStream.class), Object.class);
52
+ } else {
53
+ throw ruby.newArgumentError("Unsupported source. This method accepts String or IO");
54
+ }
55
+ return (RubyObject)JavaUtil.convertJavaToRuby(ruby, o);
56
+ }
57
+ catch (JsonProcessingException e) {
58
+ throw ParseError.newParseError(ruby, e.getLocalizedMessage());
59
+ }
60
+ catch (IOException e) {
61
+ throw ruby.newIOError(e.getLocalizedMessage());
62
+ }
63
+ }
64
+
65
+ @JRubyMethod(module = true, name = {"generate", "dump"}, required = 1)
66
+ public static IRubyObject generate(ThreadContext context, IRubyObject self, IRubyObject arg) {
67
+ Ruby ruby = context.getRuntime();
68
+ Object obj = arg.toJava(Object.class);
69
+ try {
70
+ String s = mapper.writeValueAsString(obj);
71
+ return ruby.newString(s);
72
+ }
73
+ catch (JsonProcessingException e) {
74
+ throw ParseError.newParseError(ruby, e.getLocalizedMessage());
75
+ }
76
+ catch (IOException e) {
77
+ throw ruby.newIOError(e.getLocalizedMessage());
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,15 @@
1
+ package com.jrjackson;
2
+
3
+ import org.jruby.Ruby;
4
+ import org.jruby.RubyClass;
5
+ import org.jruby.RubyException;
6
+ import org.jruby.anno.JRubyClass;
7
+ import org.jruby.exceptions.RaiseException;
8
+
9
+ @JRubyClass(name = "JrJackson::ParseError", parent = "RuntimeError")
10
+ public class ParseError {
11
+ public static RaiseException newParseError(Ruby ruby, String message) {
12
+ RubyClass errorClass = ruby.getModule("JrJackson").getClass("ParseError");
13
+ return new RaiseException(RubyException.newException(ruby, errorClass, message), true);
14
+ }
15
+ }
@@ -0,0 +1,257 @@
1
+ package com.jrjackson;
2
+
3
+ import java.io.IOException;
4
+ import java.util.*;
5
+
6
+ import com.fasterxml.jackson.core.*;
7
+
8
+ import com.fasterxml.jackson.databind.DeserializationContext;
9
+ import com.fasterxml.jackson.databind.DeserializationFeature;
10
+ import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
11
+ import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
12
+ import com.fasterxml.jackson.databind.util.ObjectBuffer;
13
+ import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
14
+
15
+ import org.jruby.Ruby;
16
+ import org.jruby.RubyObject;
17
+ import org.jruby.RubyArray;
18
+ import org.jruby.RubyHash;
19
+ import org.jruby.RubyString;
20
+ import org.jruby.runtime.builtin.IRubyObject;
21
+ import org.jruby.javasupport.JavaUtil;
22
+ import org.jruby.javasupport.util.RuntimeHelpers;
23
+
24
+
25
+ public class RubyObjectDeserializer
26
+ extends StdDeserializer<RubyObject>
27
+ {
28
+ private static final long serialVersionUID = 1L;
29
+
30
+ private final static RubyObject[] NO_OBJECTS = new RubyObject[0];
31
+
32
+ protected final static Ruby __ruby__ = Ruby.getGlobalRuntime();
33
+
34
+ /**
35
+ * @since 2.2
36
+ */
37
+ public final static RubyObjectDeserializer instance = new RubyObjectDeserializer();
38
+
39
+ public RubyObjectDeserializer() { super(RubyObject.class); }
40
+
41
+ /*
42
+ /**********************************************************
43
+ /* Deserializer API
44
+ /**********************************************************
45
+ */
46
+
47
+ @Override
48
+ public RubyObject deserialize(JsonParser jp, DeserializationContext ctxt)
49
+ throws IOException, JsonProcessingException
50
+ {
51
+ switch (jp.getCurrentToken()) {
52
+ case START_OBJECT:
53
+ return mapObject(jp, ctxt);
54
+ case START_ARRAY:
55
+ return mapArray(jp, ctxt);
56
+ case FIELD_NAME:
57
+ return mapObject(jp, ctxt);
58
+ case VALUE_EMBEDDED_OBJECT:
59
+ return toRuby(jp.getEmbeddedObject());
60
+ case VALUE_STRING:
61
+ return rubyString(jp);
62
+
63
+ case VALUE_NUMBER_INT:
64
+ /* [JACKSON-100]: caller may want to get all integral values
65
+ * returned as BigInteger, for consistency
66
+ */
67
+ if (ctxt.isEnabled(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS)) {
68
+ return toRuby(jp.getBigIntegerValue()); // should be optimal, whatever it is
69
+ }
70
+ return toRuby(jp.getNumberValue()); // should be optimal, whatever it is
71
+
72
+ case VALUE_NUMBER_FLOAT:
73
+ /* [JACKSON-72]: need to allow overriding the behavior regarding
74
+ * which type to use
75
+ */
76
+ if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
77
+ return toRuby(jp.getDecimalValue());
78
+ }
79
+ return toRuby(Double.valueOf(jp.getDoubleValue()));
80
+
81
+ case VALUE_TRUE:
82
+ return __ruby__.newBoolean(Boolean.TRUE);
83
+ case VALUE_FALSE:
84
+ return __ruby__.newBoolean(Boolean.FALSE);
85
+
86
+ case VALUE_NULL: // should not get this but...
87
+ return null;
88
+
89
+ case END_ARRAY: // invalid
90
+ case END_OBJECT: // invalid
91
+ default:
92
+ throw ctxt.mappingException(Object.class);
93
+ }
94
+ }
95
+
96
+ @Override
97
+ public RubyObject deserializeWithType(JsonParser jp, DeserializationContext ctxt,
98
+ TypeDeserializer typeDeserializer)
99
+ throws IOException, JsonProcessingException
100
+ {
101
+ JsonToken t = jp.getCurrentToken();
102
+ switch (t) {
103
+ // First: does it look like we had type id wrapping of some kind?
104
+ case START_ARRAY:
105
+ case START_OBJECT:
106
+ case FIELD_NAME:
107
+ /* Output can be as JSON Object, Array or scalar: no way to know
108
+ * a this point:
109
+ */
110
+ return toRuby(typeDeserializer.deserializeTypedFromAny(jp, ctxt));
111
+
112
+ /* Otherwise we probably got a "native" type (ones that map
113
+ * naturally and thus do not need or use type ids)
114
+ */
115
+ case VALUE_STRING:
116
+ return rubyString(jp);
117
+
118
+ case VALUE_NUMBER_INT:
119
+ // For [JACKSON-100], see above:
120
+ if (ctxt.isEnabled(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS)) {
121
+ return toRuby(jp.getBigIntegerValue());
122
+ }
123
+ /* and as per [JACKSON-839], allow "upgrade" to bigger types: out-of-range
124
+ * entries can not be produced without type, so this should "just work",
125
+ * even if it is bit unclean
126
+ */
127
+ return toRuby(jp.getNumberValue());
128
+
129
+ case VALUE_NUMBER_FLOAT:
130
+ // For [JACKSON-72], see above
131
+ if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
132
+ return toRuby(jp.getDecimalValue());
133
+ }
134
+ return __ruby__.newFloat(Double.valueOf(jp.getDoubleValue()));
135
+
136
+ case VALUE_TRUE:
137
+ return __ruby__.newBoolean(Boolean.TRUE);
138
+ case VALUE_FALSE:
139
+ return __ruby__.newBoolean(Boolean.FALSE);
140
+ case VALUE_EMBEDDED_OBJECT:
141
+ return toRuby(jp.getEmbeddedObject());
142
+
143
+ case VALUE_NULL: // should not get this far really but...
144
+ return null;
145
+ default:
146
+ throw ctxt.mappingException(Object.class);
147
+ }
148
+ }
149
+
150
+ /*
151
+ /**********************************************************
152
+ /* Internal methods
153
+ /**********************************************************
154
+ */
155
+
156
+ protected RubyObject toRuby(Object o) {
157
+ return (RubyObject)JavaUtil.convertJavaToRuby(__ruby__, o);
158
+ }
159
+
160
+ protected RubyString rubyString(JsonParser jp) throws IOException {
161
+ return __ruby__.newString(jp.getText());
162
+ }
163
+
164
+ /**
165
+ * Method called to map a JSON Array into a Java value.
166
+ */
167
+ protected RubyObject mapArray(JsonParser jp, DeserializationContext ctxt)
168
+ throws IOException, JsonProcessingException
169
+ {
170
+ // if (ctxt.isEnabled(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY)) {
171
+ // return mapArrayToArray(jp, ctxt);
172
+ // }
173
+ // Minor optimization to handle small lists (default size for ArrayList is 10)
174
+ if (jp.nextToken() == JsonToken.END_ARRAY) {
175
+ return RubyArray.newArray(__ruby__);
176
+ }
177
+ ObjectBuffer buffer = ctxt.leaseObjectBuffer();
178
+ Object[] values = buffer.resetAndStart();
179
+ int ptr = 0;
180
+ long totalSize = 0;
181
+ do {
182
+ Object value = deserialize(jp, ctxt);
183
+ ++totalSize;
184
+ if (ptr >= values.length) {
185
+ values = buffer.appendCompletedChunk(values);
186
+ ptr = 0;
187
+ }
188
+ values[ptr++] = value;
189
+ } while (jp.nextToken() != JsonToken.END_ARRAY);
190
+ // let's create almost full array, with 1/8 slack
191
+ RubyArray result = RubyArray.newArray(__ruby__, (totalSize + (totalSize >> 3) + 1));
192
+ buffer.completeAndClearBuffer(values, ptr, result);
193
+ return result;
194
+ }
195
+
196
+ /**
197
+ * Method called to map a JSON Object into a Java value.
198
+ */
199
+ protected RubyObject mapObject(JsonParser jp, DeserializationContext ctxt)
200
+ throws IOException, JsonProcessingException
201
+ {
202
+ JsonToken t = jp.getCurrentToken();
203
+ if (t == JsonToken.START_OBJECT) {
204
+ t = jp.nextToken();
205
+ }
206
+ // 1.6: minor optimization; let's handle 1 and 2 entry cases separately
207
+ if (t != JsonToken.FIELD_NAME) { // and empty one too
208
+ // empty map might work; but caller may want to modify... so better just give small modifiable
209
+ return RubyHash.newHash(__ruby__);
210
+ }
211
+ RubyString field1 = rubyString(jp);
212
+ jp.nextToken();
213
+ RubyObject value1 = deserialize(jp, ctxt);
214
+ if (jp.nextToken() != JsonToken.FIELD_NAME) { // single entry; but we want modifiable
215
+ return RuntimeHelpers.constructHash(__ruby__, field1, value1);
216
+ }
217
+ RubyString field2 = rubyString(jp);
218
+ jp.nextToken();
219
+ RubyObject value2 = deserialize(jp, ctxt);
220
+ if (jp.nextToken() != JsonToken.FIELD_NAME) {
221
+ return RuntimeHelpers.constructHash(__ruby__, field1, value1, field2, value2);
222
+ }
223
+ // And then the general case; default map size is 16
224
+ RubyHash result = RuntimeHelpers.constructHash(__ruby__, field1, value1, field2, value2);
225
+ do {
226
+ RubyString fieldName = rubyString(jp);
227
+ jp.nextToken();
228
+ result.fastASet(fieldName, deserialize(jp, ctxt));
229
+ } while (jp.nextToken() != JsonToken.END_OBJECT);
230
+ return result;
231
+ }
232
+
233
+ /**
234
+ * Method called to map a JSON Array into a Java Object array (Object[]).
235
+ */
236
+ // protected Object[] mapArrayToArray(JsonParser jp, DeserializationContext ctxt)
237
+ // throws IOException, JsonProcessingException
238
+ // {
239
+ // // Minor optimization to handle small lists (default size for ArrayList is 10)
240
+ // if (jp.nextToken() == JsonToken.END_ARRAY) {
241
+ // return NO_OBJECTS;
242
+ // }
243
+ // ObjectBuffer buffer = ctxt.leaseObjectBuffer();
244
+ // Object[] values = buffer.resetAndStart();
245
+ // int ptr = 0;
246
+ // do {
247
+ // Object value = deserialize(jp, ctxt);
248
+ // if (ptr >= values.length) {
249
+ // values = buffer.appendCompletedChunk(values);
250
+ // ptr = 0;
251
+ // }
252
+ // values[ptr++] = value;
253
+ // } while (jp.nextToken() != JsonToken.END_ARRAY);
254
+ // return buffer.completeAndClearBuffer(values, ptr);
255
+ // }
256
+ }
257
+