package com.ctriposs.baiji.specific; import com.ctriposs.baiji.exception.BaijiRuntimeException; import com.ctriposs.baiji.schema.*; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.node.ObjectNode; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.util.Iterator; import java.util.List; import java.util.Map; public class SpecificJsonReader<T> { private final Schema root; // ObjectMapper is thread safe private static final ObjectMapper MAPPER = new ObjectMapper(); public SpecificJsonReader(Schema schema) { this.root = schema; } /** * Parse as JSON Node * @param reuse * @param is the source stream * @return a record instance */ public T read(T reuse, InputStream is) throws IOException { JsonNode jsonNode = MAPPER.readTree(is); if (root instanceof RecordSchema) { RecordSchema recordSchema = (RecordSchema) root; try { return (T) readRecord(jsonNode, reuse, new RecordReader(recordSchema), recordSchema); } catch (Exception e) { throw new BaijiRuntimeException(e); } } else { throw new BaijiRuntimeException("Schema must be RecordSchema"); } } /** Called to read a record.*/ protected Object readRecord(JsonNode node, Object reuse, JsonReadable recordReader, RecordSchema recordSchema) throws Exception { Object r = recordReader.read(reuse); for (Field field : recordSchema.getFields()) { if (node.has(field.getName())) { Object value = readField(field.getSchema(), node.get(field.getName())); put(r, field.getPos(), value); } } return r; } /** Called to read the inner record.*/ protected Object readInnerRecord(Object reuse, JsonReadable recordReader, RecordSchema recordSchema) throws Exception { Object obj = recordReader.read(null); for (Field field : recordSchema.getFields()) { if (((ObjectNode) reuse).has(field.getName())) { Object value = readField(field.getSchema(), ((ObjectNode) reuse).get(field.getName())); put(obj, field.getPos(), value); } } return obj; } protected void put(Object obj, int fieldPos, Object fieldValue) { ((SpecificRecord) obj).put(fieldPos, fieldValue); } /** Called to read a field of a record.*/ protected Object readField(Schema schema, Object datum) throws Exception { try { switch (schema.getType()) { case NULL: return readNull(); case INT: return readInt(datum); case BOOLEAN: return readBoolean(datum); case DOUBLE: return readDouble(datum); case LONG: return readLong(datum); case STRING: return readString(datum); case BYTES: return readBytes(datum); case RECORD: RecordReader recordReader = new RecordReader((RecordSchema) schema); return readInnerRecord(datum, recordReader, (RecordSchema) schema); case MAP: MapSchema mapSchema = (MapSchema) schema; return readMap(datum, mapSchema); case ENUM: EnumReader enumReader = new EnumReader(schema); return enumReader.read(datum); case UNION: UnionSchema unionSchema = (UnionSchema) schema; return readUnion(datum, unionSchema); case ARRAY: ArraySchema arraySchema = (ArraySchema) schema; return readArray(datum, arraySchema); default: throw new BaijiRuntimeException(""); } } catch (NullPointerException e) { throw e; } } private Object readNull() { return null; } private Object readInt(Object obj) { return (obj instanceof JsonNode) ? ((JsonNode) obj).getIntValue() : obj; } private Object readBoolean(Object obj) { return (obj instanceof JsonNode) ? ((JsonNode) obj).getBooleanValue() : obj; } private Object readDouble(Object obj) { return (obj instanceof JsonNode) ? ((JsonNode) obj).getDoubleValue() : obj; } private Object readLong(Object obj) { return (obj instanceof JsonNode) ? ((JsonNode) obj).getLongValue() : obj; } private Object readString(Object obj) { return (obj instanceof JsonNode) ? ((JsonNode) obj).getTextValue() : obj; } private byte[] readBytes(Object obj) throws IOException { return ((JsonNode) obj).getBinaryValue(); } private Map readMap(Object obj, MapSchema mapSchema) throws Exception { MapReader mapReader = new MapReader(mapSchema); Map map = (Map) mapReader.read(null); for (Iterator<Map.Entry<String, JsonNode>> iterator = ((JsonNode) obj).getFields(); iterator.hasNext();) { Map.Entry<String, JsonNode> entry = iterator.next(); Schema valueSchema = mapSchema.getValueSchema(); Object value = readField(valueSchema, entry.getValue()); mapReader.add(map, entry.getKey(), value); } return map; } private List readArray(Object obj, ArraySchema arraySchema) throws Exception { ArrayReader arrayReader = new ArrayReader(arraySchema); List list = (List) arrayReader.read(null); for (Iterator<JsonNode> iterator = ((JsonNode) obj).getElements(); iterator.hasNext();) { JsonNode node = iterator.next(); Schema itemSchema = arraySchema.getItemSchema(); Object value = readField(itemSchema, node); list.add(value); } return list; } private Object readUnion(Object obj, UnionSchema unionSchema) throws Exception { for (int i = 0; i < unionSchema.size(); i++) { Schema schema = unionSchema.get(i); if (schema.getType() == SchemaType.NULL) continue; return readField(schema, obj); } throw new BaijiRuntimeException(""); } private class RecordReader implements JsonReadable { private final Constructor constructor; public RecordReader(RecordSchema recordSchema) { this.constructor = getConstructor(recordSchema); } @Override public Object read(Object reuse) throws Exception { return reuse == null ? constructor.newInstance() : reuse; } } private class MapReader implements JsonReadable { private final Constructor constructor; public MapReader(Schema schema) { this.constructor = getConstructor(schema); } @Override public Object read(Object reuse) throws Exception { return reuse == null ? constructor.newInstance() : reuse; } public void add(Object map, String key, Object value) { ((Map) map).put(key, value); } } private class ArrayReader implements JsonReadable { private final Constructor constructor; public ArrayReader(Schema schema) { this.constructor = getConstructor(schema); } @Override public Object read(Object reuse) throws Exception { List list; if (reuse != null) { try { list = (List) reuse; } catch (ClassCastException e) { throw new BaijiRuntimeException(e); } list.clear(); } else { list = (List) constructor.newInstance(); } return list; } } private class EnumReader implements JsonReadable { private int[] translator; private EnumSchema enumSchema; public EnumReader(Schema schema) { enumSchema = (EnumSchema) schema; translator = new int[enumSchema.getSymbols().size()]; for (String symbol : enumSchema.getSymbols()) { int index = enumSchema.ordinal(symbol); translator[index] = enumSchema.ordinal(symbol); } } @Override public Object read(Object reuse) throws Exception { String value = ((JsonNode) reuse).getTextValue(); return Enum.valueOf((Class)ObjectCreator.INSTANCE.getClass(enumSchema), value); } } private interface JsonReadable { Object read(Object reuse) throws Exception; } private static Constructor getConstructor(Schema schema) { ObjectCreator objectCreator = ObjectCreator.INSTANCE; Constructor constructor = null; try { constructor = objectCreator.getClass(schema).getConstructor(new Class[]{}); } catch (NoSuchMethodException e) { System.out.println("null pointer exception"); return null; } return constructor; } }