package com.jrjackson; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonStreamContext; import java.io.IOException; import java.util.HashMap; import org.jruby.internal.runtime.methods.DynamicMethod; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.RubyString; import org.jruby.exceptions.RaiseException; /** * * @author Guy Boertje */ public class SchParse extends StreamParse { protected final DynamicMethod _hash_key; protected final boolean _no_hash_key; protected final DynamicMethod _hash_set; protected final boolean _no_hash_set; protected final DynamicMethod _array_append; protected final boolean _no_array_append; protected final HashMap _objectMap = new HashMap(); protected JsonStreamContext _deepestContext; public SchParse(ThreadContext ctx, IRubyObject handler) throws RaiseException { super(ctx, handler); _hash_key = _meta.searchMethod("hash_key"); _no_hash_key = _hash_key.isUndefined(); _hash_set = _meta.searchMethod("hash_set"); _no_hash_set = _hash_set.isUndefined(); _array_append = _meta.searchMethod("array_append"); _no_array_append = _array_append.isUndefined(); //_hash_key is optional if ( _no_hash_start || _no_hash_end || _no_array_start || _no_array_end || _no_add_value || _no_hash_set || _no_array_append ) { throw ParseError.newParseError(_ruby, "Handler does not implement public API"); } } @Override public IRubyObject deserialize(JsonParser jp) throws RaiseException { try { while (jp.nextValue() != null) { handleCurrentToken(jp); } return _ctx.nil; } catch (JsonProcessingException e) { throw ParseError.newParseError(_ruby, e.getLocalizedMessage()); } catch (IOException e) { throw _ruby.newIOError(e.getLocalizedMessage()); } } private void callAddValue(JsonStreamContext x) { JsonStreamContext px = x.getParent(); IRubyObject target = _objectMap.get(x); IRubyObject dtarget = _objectMap.get(_deepestContext); if (px == null) { _add_value.call(_ctx, _handler, _meta, "add_value", dtarget); return; } if (x.inArray()) { _array_append.call(_ctx, _handler, _meta, "array_append", target, dtarget); } else if (x.inObject()) { IRubyObject treatedKey = callHashKey(x); _hash_set.call(_ctx, _handler, _meta, "hash_set", target, treatedKey, dtarget); } else { _add_value.call(_ctx, _handler, _meta, "add_value", target); } } private void callAddValue(JsonStreamContext x, IRubyObject val) { IRubyObject target = _objectMap.get(x); if (x.inArray()) { _array_append.call(_ctx, _handler, _meta, "array_append", target, val); } else if (x.inObject()) { IRubyObject treatedKey = callHashKey(x); _hash_set.call(_ctx, _handler, _meta, "hash_set", target, treatedKey, val); } else { _add_value.call(_ctx, _handler, _meta, "add_value", val); } } private IRubyObject callHashKey(JsonStreamContext x) { String k = x.getCurrentName(); if (k == null) { return _ctx.nil; } RubyString key = RubyUtils.rubyString(_ruby, k); if (_no_hash_key) { return key; } return _hash_key.call(_ctx, _handler, _meta, "hash_key", key); } private void handleCurrentToken(JsonParser jp) throws IOException, JsonProcessingException { JsonStreamContext cx = jp.getParsingContext(); IRubyObject value; IRubyObject rubyObject; switch (jp.getCurrentToken()) { case START_OBJECT: _deepestContext = cx; rubyObject = _hash_start.call(_ctx, _handler, _meta, "hash_start"); _objectMap.put(cx, rubyObject); break; case START_ARRAY: _deepestContext = cx; rubyObject = _array_start.call(_ctx, _handler, _meta, "array_start"); _objectMap.put(cx, rubyObject); break; case FIELD_NAME: break; case VALUE_EMBEDDED_OBJECT: value = RubyUtils.rubyObject(_ruby, jp.getEmbeddedObject()); callAddValue(cx, value); break; case VALUE_STRING: value = keyConverter.convert(_ruby, jp); callAddValue(cx, value); break; case VALUE_NUMBER_INT: /* [JACKSON-100]: caller may want to get all integral values * returned as BigInteger, for consistency */ JsonParser.NumberType numberType = jp.getNumberType(); value = RubyUtils.rubyBignum(_ruby, jp.getBigIntegerValue()); callAddValue(cx, value); break; case VALUE_NUMBER_FLOAT: value = RubyUtils.rubyBigDecimal(_ruby, jp.getDecimalValue()); callAddValue(cx, value); break; case VALUE_TRUE: value = _ruby.newBoolean(Boolean.TRUE); callAddValue(cx, value); break; case VALUE_FALSE: value = _ruby.newBoolean(Boolean.FALSE); callAddValue(cx, value); break; case VALUE_NULL: // should not get this but... value = _ctx.nil; callAddValue(cx, value); break; case END_ARRAY: _array_end.call(_ctx, _handler, _meta, "array_end"); callAddValue(cx); _deepestContext = cx; break; case END_OBJECT: _hash_end.call(_ctx, _handler, _meta, "hash_end"); callAddValue(cx); _deepestContext = cx; break; } } } // System.out.println("--- callAddValue final ---"); // if (px != null) { // System.out.println("-------- parent --------"); // System.out.println(px.getTypeDesc()); // System.out.println(px.getCurrentName()); // } // System.out.println("-------- current --------"); // System.out.println(x.getTypeDesc()); // System.out.println(x.getCurrentName()); // System.out.println(target); // // System.out.println("-------- deepest --------"); // System.out.println(_deepestContext.getTypeDesc()); // System.out.println(_deepestContext.getCurrentName()); // System.out.println(dtarget);