com.opengamma.web.json.FudgeMsgJSONReader.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.web.json.FudgeMsgJSONReader.java

Source

/**
 * Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.web.json;

import java.io.IOException;
import java.io.Reader;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.fudgemsg.FudgeContext;
import org.fudgemsg.FudgeFieldType;
import org.fudgemsg.FudgeMsg;
import org.fudgemsg.FudgeMsgEnvelope;
import org.fudgemsg.FudgeRuntimeException;
import org.fudgemsg.MutableFudgeMsg;
import org.fudgemsg.taxonomy.FudgeTaxonomy;
import org.fudgemsg.types.IndicatorType;
import org.fudgemsg.wire.FudgeRuntimeIOException;
import org.fudgemsg.wire.json.FudgeJSONSettings;
import org.fudgemsg.wire.types.FudgeWireType;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.threeten.bp.LocalDate;

import com.google.common.collect.Lists;
import com.opengamma.OpenGammaRuntimeException;

/**
 * A Fudge reader that interprets JSON.
 */
public class FudgeMsgJSONReader {

    /**
     * The taxonomy identifier to use for any messages that are passed without envelopes.
     */
    private static final short DEFAULT_TAXONOMY_ID = 0;
    /**
     * The schema version to add to the envelope header for any messages that are passed without envelopes.
     */
    private static final int DEFAULT_MESSAGE_VERSION = 0;
    /**
     * The processing directive flags to add to the envelope header for any messages that are passed without envelopes.
     */
    private static final int DEFAULT_MESSAGE_PROCESSING_DIRECTIVES = 0;

    private final FudgeJSONSettings _settings;
    private final FudgeContext _fudgeContext;
    private final Reader _underlying;
    private int _taxonomyId = DEFAULT_TAXONOMY_ID;
    private FudgeTaxonomy _taxonomy;
    private int _processingDirectives = DEFAULT_MESSAGE_PROCESSING_DIRECTIVES;
    private int _schemaVersion = DEFAULT_MESSAGE_VERSION;
    private JSONObject _jsonObject;
    private final List<String> _envelopeAttibutesFields = Lists.newArrayList();

    /**
     * Creates a new instance for reading a Fudge stream from a JSON reader.
     *
     * @param fudgeContext  the Fudge context, not null
     * @param reader  the underlying reader, not null
     */
    public FudgeMsgJSONReader(final FudgeContext fudgeContext, final Reader reader) {
        this(fudgeContext, reader, new FudgeJSONSettings());
    }

    /**
     * Creates a new instance for reading a Fudge stream from a JSON reader.
     *
     * @param fudgeContext  the Fudge context, not null
     * @param reader  the underlying reader, not null
     * @param settings  the JSON settings to fine tune the read, not null
     */
    public FudgeMsgJSONReader(final FudgeContext fudgeContext, final Reader reader,
            final FudgeJSONSettings settings) {
        _fudgeContext = fudgeContext;
        _underlying = reader;
        _settings = settings;
        try {
            _jsonObject = new JSONObject(new JSONTokener(reader));
            init(_fudgeContext, _jsonObject, _settings);
        } catch (final JSONException ex) {
            wrapException("Creating json object from reader", ex);
        }
    }

    private void init(final FudgeContext fudgeContext, final JSONObject jsonObject,
            final FudgeJSONSettings settings) throws JSONException {
        final String processingDirectivesField = settings.getProcessingDirectivesField();
        if (jsonObject.has(processingDirectivesField)) {
            _processingDirectives = integerValue(jsonObject.get(processingDirectivesField));
            _envelopeAttibutesFields.add(processingDirectivesField);
        }
        final String schemaVersionField = getSettings().getSchemaVersionField();
        if (jsonObject.has(schemaVersionField)) {
            _schemaVersion = integerValue(jsonObject.get(schemaVersionField));
            _envelopeAttibutesFields.add(schemaVersionField);
        }
        final String taxonomyField = getSettings().getTaxonomyField();
        if (jsonObject.has(taxonomyField)) {
            _taxonomyId = integerValue(jsonObject.get(taxonomyField));
            _taxonomy = fudgeContext.getTaxonomyResolver().resolveTaxonomy((short) _taxonomyId);
            _envelopeAttibutesFields.add(taxonomyField);
        }
    }

    //-------------------------------------------------------------------------
    /**
     * Gets the underlying reader.
     *
     * @return the reader, not null
     */
    public Reader getUnderlying() {
        return _underlying;
    }

    /**
     * Gets the fudgeContext.
     * @return the fudgeContext
     */
    public FudgeContext getFudgeContext() {
        return _fudgeContext;
    }

    /**
     * Gets the settings.
     * @return the settings
     */
    public FudgeJSONSettings getSettings() {
        return _settings;
    }

    private RuntimeException wrapException(String message, final JSONException ex) {
        message = "Error " + message + " from JSON stream";
        if (ex.getCause() instanceof IOException) {
            return new FudgeRuntimeIOException(message, (IOException) ex.getCause());
        } else {
            return new FudgeRuntimeException(message, ex);
        }
    }

    /**
     * Reads the next message, discarding the envelope.
     *
     * @return the message read without the envelope
     */
    public FudgeMsg readMessage() {
        final FudgeMsgEnvelope msgEnv = readMessageEnvelope();
        if (msgEnv == null) {
            return null;
        }
        return msgEnv.getMessage();
    }

    /**
     * Reads the next message, returning the envelope.
     *
     * @return the {@link FudgeMsgEnvelope}
     */
    public FudgeMsgEnvelope readMessageEnvelope() {
        FudgeMsgEnvelope msgEnv = null;
        try {
            final JSONObject meta = (JSONObject) _jsonObject.get("meta");
            final JSONObject data = (JSONObject) _jsonObject.get("data");
            final MutableFudgeMsg msg = processFields(data, meta);
            msgEnv = getFudgeMsgEnvelope(msg, _jsonObject);
        } catch (final JSONException ex) {
            wrapException("reading message envelope", ex);
        }
        return msgEnv;
    }

    private FudgeMsgEnvelope getFudgeMsgEnvelope(final MutableFudgeMsg fudgeMsg, final JSONObject jsonObject)
            throws JSONException {
        return new FudgeMsgEnvelope(fudgeMsg, _schemaVersion, _processingDirectives);
    }

    private int integerValue(final Object o) {
        if (o instanceof Number) {
            return ((Number) o).intValue();
        } else if (o instanceof String) {
            return Integer.parseInt((String) o);
        } else {
            throw new NumberFormatException(o + " is not a number");
        }
    }

    private byte byteValue(final Object o) {
        if (o instanceof Number) {
            return ((Number) o).byteValue();
        } else if (o instanceof String) {
            return Byte.parseByte((String) o);
        } else {
            throw new NumberFormatException(o + " is not a number");
        }
    }

    private short shortValue(final Object o) {
        if (o instanceof Number) {
            return ((Number) o).shortValue();
        } else if (o instanceof String) {
            return Short.parseShort((String) o);
        } else {
            throw new NumberFormatException(o + " is not a number");
        }
    }

    private long longValue(final Object o) {
        if (o instanceof Number) {
            return ((Number) o).longValue();
        } else if (o instanceof String) {
            return Long.parseLong((String) o);
        } else {
            throw new NumberFormatException(o + " is not a number");
        }
    }

    private double doubleValue(final Object o) {
        if (o instanceof Number) {
            return ((Number) o).doubleValue();
        } else if (o instanceof String) {
            return Double.parseDouble((String) o);
        } else {
            throw new NumberFormatException(o + " is not a number");
        }
    }

    private float floatValue(final Object o) {
        if (o instanceof Number) {
            return ((Number) o).floatValue();
        } else if (o instanceof String) {
            return Float.parseFloat((String) o);
        } else {
            throw new NumberFormatException(o + " is not a number");
        }
    }

    private MutableFudgeMsg processFields(final JSONObject data, final JSONObject meta) {
        final MutableFudgeMsg fudgeMsg = getFudgeContext().newMessage();
        @SuppressWarnings("unchecked")
        final Iterator<String> keys = data.keys();
        while (keys.hasNext()) {
            final String fieldName = keys.next();
            final Object dataValue = getFieldValue(data, fieldName);
            final Object metaValue = getFieldValue(meta, fieldName);
            if (dataValue instanceof JSONObject) {
                final MutableFudgeMsg subMsg = processFields((JSONObject) dataValue, (JSONObject) metaValue);
                addField(fudgeMsg, fieldName, FudgeWireType.SUB_MESSAGE, subMsg);
            } else if (dataValue instanceof JSONArray) {
                final JSONArray dataArray = (JSONArray) dataValue;
                if (dataArray.length() > 0) {
                    if (isPrimitiveArray(metaValue)) {
                        try {
                            final FudgeFieldType fieldType = getFieldType((String) metaValue);
                            if (fieldType == null) {
                                throw new OpenGammaRuntimeException(
                                        "Unknown field type " + metaValue + " for " + fieldName + ":" + dataValue);
                            }
                            final Object primitiveArray = jsonArrayToPrimitiveArray(dataArray, fieldType);
                            addField(fudgeMsg, fieldName, getFieldType((String) metaValue), primitiveArray);
                        } catch (final JSONException e) {
                            wrapException("converting json array to primitive array", e);
                        }
                    } else {
                        //treat as repeated fields
                        addRepeatedFields(fudgeMsg, fieldName, dataArray, (JSONArray) metaValue);
                    }
                }
            } else {
                final FudgeFieldType fieldType = getFieldType((String) metaValue);
                if (fieldType == null) {
                    throw new OpenGammaRuntimeException(
                            "Unknown field type " + metaValue + " for " + fieldName + ":" + dataValue);
                }
                addField(fudgeMsg, fieldName, fieldType, dataValue);
            }
        }
        return fudgeMsg;
    }

    private boolean isPrimitiveArray(final Object metaValue) {
        if (metaValue instanceof JSONArray) {
            return false;
        }
        return true;
    }

    private void addField(final MutableFudgeMsg fudgeMsg, final String fieldName, final FudgeFieldType fieldType,
            final Object fieldValue) {
        Integer ordinal = null;
        String name = null;
        try {
            ordinal = Integer.parseInt(fieldName);
        } catch (final NumberFormatException nfe) {
            if (StringUtils.isNotEmpty(fieldName)) {
                if (!getPreserveFieldNames()) {
                    if (_taxonomy != null) {
                        ordinal = _taxonomy.getFieldOrdinal(fieldName);
                    }
                } else {
                    name = fieldName;
                }
            }
        }
        switch (fieldType.getTypeId()) {
        case FudgeWireType.BYTE_TYPE_ID:
            fudgeMsg.add(name, ordinal, fieldType, byteValue(fieldValue));
            break;
        case FudgeWireType.SHORT_TYPE_ID:
            fudgeMsg.add(name, ordinal, fieldType, shortValue(fieldValue));
            break;
        case FudgeWireType.DOUBLE_TYPE_ID:
            fudgeMsg.add(name, ordinal, fieldType, doubleValue(fieldValue));
            break;
        case FudgeWireType.FLOAT_TYPE_ID:
            fudgeMsg.add(name, ordinal, fieldType, floatValue(fieldValue));
            break;
        case FudgeWireType.DATE_TYPE_ID:
            fudgeMsg.add(name, ordinal, fieldType, toLocalDate(fieldValue));
            break;
        case FudgeWireType.LONG_TYPE_ID:
            fudgeMsg.add(name, ordinal, fieldType, longValue(fieldValue));
            break;
        default:
            fudgeMsg.add(name, ordinal, fieldType, fieldValue);
            break;
        }
    }

    private LocalDate toLocalDate(final Object fieldValue) {
        if (fieldValue != null) {
            return LocalDate.parse((String) fieldValue);
        }
        return null;
    }

    private boolean getPreserveFieldNames() {
        return getSettings().getPreserveFieldNames();
    }

    private void addRepeatedFields(final MutableFudgeMsg fudgeMsg, final String fieldName,
            final JSONArray dataArray, final JSONArray metaArray) {
        try {
            for (int i = 0; i < dataArray.length(); i++) {
                final Object arrValue = dataArray.get(i);
                final Object metaValue = metaArray.get(i);
                if (arrValue instanceof JSONObject) {
                    final MutableFudgeMsg subMsg = processFields((JSONObject) arrValue, (JSONObject) metaValue);
                    addField(fudgeMsg, fieldName, FudgeWireType.SUB_MESSAGE, subMsg);
                } else {
                    final FudgeFieldType fieldType = getFieldType((String) metaValue);
                    if (fieldType == null) {
                        throw new OpenGammaRuntimeException(
                                "Unknown field type " + metaValue + " for " + fieldName + ":" + arrValue);
                    }
                    addField(fudgeMsg, fieldName, fieldType, arrValue);
                }
            }
        } catch (final JSONException e) {
            wrapException("adding repeated fields", e);
        }
    }

    private FudgeFieldType getFieldType(final String typeValue) {
        final Integer typeId = getSettings().stringToFudgeTypeId(typeValue);
        return getFudgeContext().getTypeDictionary().getByTypeId(typeId);
    }

    private Object jsonArrayToPrimitiveArray(final JSONArray arr, final FudgeFieldType fieldType)
            throws JSONException {
        switch (fieldType.getTypeId()) {
        case FudgeWireType.BYTE_ARRAY_TYPE_ID:
        case FudgeWireType.BYTE_ARRAY_4_TYPE_ID:
        case FudgeWireType.BYTE_ARRAY_8_TYPE_ID:
        case FudgeWireType.BYTE_ARRAY_16_TYPE_ID:
        case FudgeWireType.BYTE_ARRAY_20_TYPE_ID:
        case FudgeWireType.BYTE_ARRAY_32_TYPE_ID:
        case FudgeWireType.BYTE_ARRAY_64_TYPE_ID:
        case FudgeWireType.BYTE_ARRAY_128_TYPE_ID:
        case FudgeWireType.BYTE_ARRAY_256_TYPE_ID:
        case FudgeWireType.BYTE_ARRAY_512_TYPE_ID:
            final byte[] byteArr = new byte[arr.length()];
            for (int j = 0; j < byteArr.length; j++) {
                byteArr[j] = ((Number) arr.get(j)).byteValue();
            }
            return byteArr;
        case FudgeWireType.SHORT_ARRAY_TYPE_ID:
            final short[] shortArr = new short[arr.length()];
            for (int j = 0; j < shortArr.length; j++) {
                shortArr[j] = ((Number) arr.get(j)).shortValue();
            }
            return shortArr;
        case FudgeWireType.INT_ARRAY_TYPE_ID:
            final int[] intArr = new int[arr.length()];
            for (int j = 0; j < intArr.length; j++) {
                intArr[j] = ((Number) arr.get(j)).intValue();
            }
            return intArr;
        case FudgeWireType.LONG_ARRAY_TYPE_ID:
            final long[] longArr = new long[arr.length()];
            for (int j = 0; j < longArr.length; j++) {
                longArr[j] = ((Number) arr.get(j)).longValue();
            }
            return longArr;
        case FudgeWireType.DOUBLE_ARRAY_TYPE_ID:
            final double[] doubleArr = new double[arr.length()];
            for (int j = 0; j < doubleArr.length; j++) {
                doubleArr[j] = ((Number) arr.get(j)).doubleValue();
            }
            return doubleArr;
        case FudgeWireType.FLOAT_ARRAY_TYPE_ID:
            final float[] floatArr = new float[arr.length()];
            for (int j = 0; j < floatArr.length; j++) {
                floatArr[j] = ((Number) arr.get(j)).floatValue();
            }
            return floatArr;
        default:
            return null;
        }
    }

    private Object getFieldValue(final JSONObject jsonObject, final String fieldName) {
        Object fieldValue = null;
        try {
            fieldValue = jsonObject.get(fieldName);
            if (JSONObject.NULL.equals(fieldValue)) {
                fieldValue = IndicatorType.INSTANCE;
            }
        } catch (final JSONException e) {
            wrapException("reading field value", e);
        }
        return fieldValue;
    }

}