All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.refinitiv.eta.json.converter.JsonConverterBaseImpl Maven / Gradle / Ivy

The newest version!
/*|-----------------------------------------------------------------------------
 *|            This source code is provided under the Apache 2.0 license
 *|  and is provided AS IS with no warranty or guarantee of fit for purpose.
 *|                See the project's LICENSE.md for details.
 *|           Copyright (C) 2019-2022 LSEG. All rights reserved.     
 *|-----------------------------------------------------------------------------
 */

package com.refinitiv.eta.json.converter;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.refinitiv.eta.codec.*;
import com.refinitiv.eta.json.util.JsonFactory;
import com.refinitiv.eta.rdm.DomainTypes;
import com.refinitiv.eta.transport.TransportBuffer;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.Map;

import static com.refinitiv.eta.codec.CodecReturnCodes.*;
import static com.refinitiv.eta.json.converter.ConstCharArrays.*;

class JsonConverterBaseImpl extends JsonAbstractConverter {

    private final ThreadLocal mapper;

    private ThreadLocal currentState = ThreadLocal.withInitial(() -> null);;
    private Map rsslMsgHandlerMap = new HashMap<>();
    private Map primitiveHandlerMap = new HashMap<>();
    private Map containerHandlerMap = new HashMap<>();
    private Map rsslMsgChunkHandlerMap = new HashMap<>();
    private boolean catchUnexpectedKeys;
    private boolean catchUnexpectedFids;
    private boolean allowEnumDisplayStrings;
    private boolean useDefaultQoS;
    private boolean expandEnumFields;
    private int defaultServiceId;
    private boolean hasDefaultServiceId;
    private DataDictionary dictionary;
    private ThreadLocal dictionaryEntry = ThreadLocal.withInitial(() -> null);
    private ThreadLocal jsonOutputBuffer = ThreadLocal.withInitial(() -> new JsonBuffer());
    private ThreadLocal inputStream = ThreadLocal.withInitial(() -> new ByteBufferInputStream());
    private Map enumTableDefinitionMap = new HashMap<>(256);

    private static final Map STRING_TO_RWF_MSG_CLASS = new HashMap<>();;
    private static final Map STRING_TO_JSON_MSG_CLASS = new HashMap<>();
    
    static final String UPDATE_STR = "Update";
    static final String GENERIC_STR = "Generic";
    static final String REFRESH_STR = "Refresh";
    static final String REQUEST_STR = "Request";
    static final String POST_STR = "Post";
    static final String STATUS_STR = "Status";
    static final String CLOSE_STR = "Close";
    static final String ACK_STR = "Ack";

    static
    {
        STRING_TO_RWF_MSG_CLASS.put(REQUEST_STR, MsgClasses.REQUEST);
        STRING_TO_RWF_MSG_CLASS.put(REFRESH_STR, MsgClasses.REFRESH);
        STRING_TO_RWF_MSG_CLASS.put(STATUS_STR, MsgClasses.STATUS);
        STRING_TO_RWF_MSG_CLASS.put(UPDATE_STR, MsgClasses.UPDATE);
        STRING_TO_RWF_MSG_CLASS.put(CLOSE_STR, MsgClasses.CLOSE);
        STRING_TO_RWF_MSG_CLASS.put(ACK_STR, MsgClasses.ACK);
        STRING_TO_RWF_MSG_CLASS.put(GENERIC_STR, MsgClasses.GENERIC);
        STRING_TO_RWF_MSG_CLASS.put(POST_STR, MsgClasses.POST);

        STRING_TO_JSON_MSG_CLASS.put(JsonMsgClasses.PING_STR, JsonMsgClasses.PING);
        STRING_TO_JSON_MSG_CLASS.put(JsonMsgClasses.PONG_STR, JsonMsgClasses.PONG);
        STRING_TO_JSON_MSG_CLASS.put(JsonMsgClasses.ERROR_STR, JsonMsgClasses.ERROR);
    }

    JsonConverterBaseImpl() {
        mapper = ThreadLocal.withInitial(() -> new ObjectMapper());
        initPrimitiveHandlers();
        initContainerHandlers();
        initRsslMsgChunkHandlers();
        initMessageHandlers();
    }

    private void initMessageHandlers() {
        rsslMsgHandlerMap.put(MsgClasses.REQUEST, new JsonRequestMsgConverter(this));
        rsslMsgHandlerMap.put(MsgClasses.STATUS, new JsonStatusMsgConverter(this));
        rsslMsgHandlerMap.put(MsgClasses.UPDATE, new JsonUpdateMsgConverter(this));
        rsslMsgHandlerMap.put(MsgClasses.REFRESH, new JsonRefreshMsgConverter(this));
        rsslMsgHandlerMap.put(MsgClasses.GENERIC, new JsonGenericMsgConverter(this));
        rsslMsgHandlerMap.put(MsgClasses.ACK, new JsonAckMsgConverter(this));
        rsslMsgHandlerMap.put(MsgClasses.CLOSE, new JsonCloseMsgConverter(this));
        rsslMsgHandlerMap.put(MsgClasses.POST, new JsonPostMsgConverter(this));
    }

    private void initRsslMsgChunkHandlers() {
        rsslMsgChunkHandlerMap.put(RsslMsgChunkType.MSG_KEY_CHUNK, new JsonMsgKeyConverter(this));
        rsslMsgChunkHandlerMap.put(RsslMsgChunkType.PRIORITY_CHUNK, new JsonPriorityConverter(this));
        rsslMsgChunkHandlerMap.put(RsslMsgChunkType.POST_USER_INFO_CHUNK, new JsonPostUserInfoConverter(this));
        rsslMsgChunkHandlerMap.put(RsslMsgChunkType.CONFINFO_CHUNK, new JsonConfInfoConverter(this));
    }

    private void initPrimitiveHandlers() {

        primitiveHandlerMap.put(DataTypes.QOS, new JsonQosConverter(this));
        primitiveHandlerMap.put(DataTypes.STATE, new JsonStateConverter(this));
        primitiveHandlerMap.put(DataTypes.ARRAY, new JsonArrayConverter(this));
        primitiveHandlerMap.put(DataTypes.ENUM, new JsonEnumerationConverter(this));
        primitiveHandlerMap.put(DataTypes.BUFFER, new JsonBufferConverter(this));
        primitiveHandlerMap.put(DataTypes.UTF8_STRING, new JsonUtf8Converter(this));
        primitiveHandlerMap.put(DataTypes.RMTES_STRING, new JsonRMTESConverter(this));
        primitiveHandlerMap.put(DataTypes.ASCII_STRING, new JsonAsciiConverter(this));
        AbstractPrimitiveTypeConverter convInt = new JsonIntConverter(this);
        for (int i = 0; i < convInt.dataTypes.length; i++)
            primitiveHandlerMap.put(convInt.dataTypes[i], convInt);
        AbstractPrimitiveTypeConverter convLong = new JsonLongConverter(this);
        for (int i = 0; i < convLong.dataTypes.length; i++)
            primitiveHandlerMap.put(convLong.dataTypes[i], convLong);
        AbstractPrimitiveTypeConverter convDateTime = new JsonDateTimeConverter(this);
        for (int i = 0; i < convDateTime.dataTypes.length; i++)
            primitiveHandlerMap.put(convDateTime.dataTypes[i], convDateTime);
        primitiveHandlerMap.put(DataTypes.DATE, new JsonDateConverter(this));
        primitiveHandlerMap.put(DataTypes.DATE_4, new JsonDateConverter(this));
        AbstractPrimitiveTypeConverter convTime = new JsonTimeConverter(this);
        for (int i = 0; i < convTime.dataTypes.length; i++)
            primitiveHandlerMap.put(convTime.dataTypes[i], convTime);
        primitiveHandlerMap.put(DataTypes.DOUBLE, new JsonDoubleConverter(this));
        primitiveHandlerMap.put(DataTypes.DOUBLE_8, new JsonDoubleConverter(this));
        primitiveHandlerMap.put(DataTypes.FLOAT, new JsonFloatConverter(this));
        primitiveHandlerMap.put(DataTypes.FLOAT_4, new JsonFloatConverter(this));
        primitiveHandlerMap.put(DataTypes.REAL, new JsonRealConverter(this));
        primitiveHandlerMap.put(DataTypes.REAL_4RB, new JsonRealConverter(this));
        primitiveHandlerMap.put(DataTypes.REAL_8RB, new JsonRealConverter(this));
        primitiveHandlerMap.put(DataTypes.UNKNOWN, new JsonUnknownTypeConverter(this));
    }

    private void initContainerHandlers() {
        containerHandlerMap.put(DataTypes.ELEMENT_LIST, new JsonElementListConverter(this));
        containerHandlerMap.put(DataTypes.FIELD_LIST, new JsonFieldListConverter(this));
        containerHandlerMap.put(DataTypes.FILTER_LIST, new JsonFilterListConverter(this));
        containerHandlerMap.put(DataTypes.VECTOR, new JsonVectorConverter(this));
        containerHandlerMap.put(DataTypes.SERIES, new JsonSeriesConverter(this));
        containerHandlerMap.put(DataTypes.MAP, new JsonMapConverter(this));
        containerHandlerMap.put(DataTypes.OPAQUE, new JsonOpaqueConverter(this));
        containerHandlerMap.put(DataTypes.XML, new JsonXmlConverter(this));
        containerHandlerMap.put(DataTypes.ANSI_PAGE, new JsonAnsiPageConverter(this));
        containerHandlerMap.put(DataTypes.JSON, new JsonJsonConverter(this));
        containerHandlerMap.put(DataTypes.MSG, new JsonMsgConverter(this));
    }

    @Override
    public int parseJsonBuffer(Buffer jsonBuffer, ParseJsonOptions options, JsonConverterError error) {
        if (options.getProtocolType() != JsonProtocol.JSON_JPT_JSON2) {
            return error.setError(JsonConverterErrorCodes.JSON_ERROR, "Protocol type not supported: " + options.getProtocolType());
        }
        return parseJsonBuffer(jsonBuffer.data().array(), error);
    }

    @Override
    public int parseJsonBuffer(TransportBuffer inBuffer, ParseJsonOptions options, JsonConverterError error) {
        if (options.getProtocolType() != JsonProtocol.JSON_JPT_JSON2) {
            return error.setError(JsonConverterErrorCodes.JSON_ERROR, "Protocol type not supported: " + options.getProtocolType());
        }

        return parseJsonBuffer(inBuffer, error);
    }

    @Override
    public int decodeJsonMsg(JsonMsg jsonMsg, DecodeJsonMsgOptions options, JsonConverterError error) {

        if (options.getJsonProtocolType() != JsonProtocol.JSON_JPT_JSON2) {
            return error.setError(JsonConverterErrorCodes.JSON_ERROR, "Protocol type not supported: " + options.getJsonProtocolType());
        }

        jsonMsg.rwfMsg().clear();

        int ret = setCurrentMessageRoot(error);
        if (ret != SUCCESS)
            return ret;

        if (getJsonMsgType(jsonMsg, currentState.get().getWorkingNode(), error) != SUCCESS) {
            currentState.get().setFailedNode(currentState.get().getWorkingNode());
            return FAILURE;
        }

        EncodeIterator encIter = JsonFactory.createEncodeIterator();
        try {
            if (jsonMsg.jsonMsgClass() == JsonMsgClasses.RSSL_MESSAGE) {
                prepareJsonMsgToDecode(jsonMsg);
                encIter.clear();
                encIter.setBufferAndRWFVersion(jsonMsg.rwfMsg().encodedMsgBuffer(), Codec.majorVersion(), Codec.minorVersion());
                decodeRsslMessage(jsonMsg.rwfMsg().msgClass(), currentState.get().getWorkingNode(), jsonMsg.rwfMsg(), error, encIter);
                if (error.isFailed()) {
                    currentState.get().setFailedNode(currentState.get().getWorkingNode());
                    return FAILURE;
                }
            }
        } finally {
            JsonFactory.releaseEncodeIterator(encIter);
        }

        return error.isSuccessful() ? SUCCESS : FAILURE;
    }

    private int setCurrentMessageRoot(JsonConverterError error) {
        JsonConverterState state = currentState.get();
        JsonNode root = state.getCurrentRoot();

        if (Objects.isNull(root))
            return END_OF_CONTAINER;

        if (root.isObject()) {
            state.setCurrentRoot(null);
            state.setWorkingNode(root);
            return SUCCESS;
        } else if (root.isArray()) {
            if (state.getArrayCounter() >= root.size())
                return END_OF_CONTAINER;
            JsonNode currentEntry = root.get(state.getArrayCounter());
            if (currentEntry.isObject()) {
                state.setWorkingNode(currentEntry);
                state.setArrayCounter(state.getArrayCounter() + 1);
                return error.isSuccessful() ? SUCCESS : FAILURE;
            } else if (currentEntry.isArray()) {
                if (state.getEntryCounter() >= currentEntry.size()) {
                    state.setEntryCounter(0);
                    state.setArrayCounter(state.getArrayCounter() + 1);
                    return setCurrentMessageRoot(error);
                }
                state.setWorkingNode(currentEntry.get(state.getEntryCounter()));
                state.setEntryCounter(state.getEntryCounter() + 1);
                return SUCCESS;
            } else {
                error.setError(JsonConverterErrorCodes.JSON_ERROR_PARSE_ERROR, "Error parsing JSON message: expected single message or array of messages, found " + currentEntry.getNodeType().toString() + " type", "root");
                currentState.get().setFailedNode(currentEntry);
                return FAILURE;
            }
        } else {
            error.setError(JsonConverterErrorCodes.JSON_ERROR_PARSE_ERROR, "Error parsing JSON message: expected single message or array of messages, found " + root.getNodeType().toString() + " type", "root");
            currentState.get().setFailedNode(root);
            return FAILURE;
        }
    }

    @Override
    public int convertRWFToJson(Msg inMsg, RWFToJsonOptions options, ConversionResults outResults, JsonConverterError error) {

        if (options.getJsonProtocolType() != JsonProtocol.JSON_JPT_JSON2) {
            error.setError(FAILURE, "Invalid protocol type.");
            return FAILURE;
        }
        JsonBuffer buffer = jsonOutputBuffer.get();
        buffer.position = 0;
        int requiredLenght = estimateJsonLength(inMsg.encodedDataBody().length());
        if (buffer.data == null || buffer.data.length < requiredLenght) {
            JsonFactory.releaseByteArray(buffer.data);
            buffer.data = JsonFactory.createByteArray(requiredLenght);
        }
        DecodeIterator iter = JsonFactory.createDecodeIterator();
        try {
            iter.clear();
            if (inMsg.encodedDataBody() == null || inMsg.encodedDataBody().data() == null) {
                error.setError(FAILURE, "RWF Msg encodedDataBody() is not initialized.");
                return FAILURE;
            }
            iter.setBufferAndRWFVersion(inMsg.encodedDataBody(), Codec.majorVersion(), Codec.minorVersion());
            if (processMsg(iter, inMsg, buffer, error, true)) {
                if (outResults != null)
                    outResults.setLength(buffer.position + 25);
                return SUCCESS;
            } else
                return FAILURE;
        } finally {
            JsonFactory.releaseDecodeIterator(iter);
        }
    }

    @Override
    public int convertRWFToJson(Msg inMsg, RWFToJsonOptions options, JsonConverterError error) {
        return convertRWFToJson(inMsg, options, null, error);
    }

    @Override
    public int getJsonBuffer(Buffer buffer, GetJsonMsgOptions options, JsonConverterError error) {

        JsonBuffer json = jsonOutputBuffer.get();
        if (options.getStreamId() == null || options.isCloseMsg()) {
            if (json.position >= buffer.length())
                buffer.data(ByteBuffer.wrap(new byte[json.position]));
            buffer.data().put(json.data, 0, json.position);
        } else {
            int fullLength = json.position - BufferHelper.getCurrentStreamIdLength(json, error) + BasicPrimitiveConverter.getLongLengthCompare(options.getStreamId());
            byte[] outputData = new byte[fullLength];
            BufferHelper.composeMessage(outputData, options.getStreamId(), json, error);
            buffer.data(ByteBuffer.wrap(outputData));
        }
        return SUCCESS;
    }

    @Override
    public int getJsonBuffer(TransportBuffer buffer, GetJsonMsgOptions options, JsonConverterError error) {
        JsonBuffer json = jsonOutputBuffer.get();
        if (options.getStreamId() == null || options.isCloseMsg()) {
            if (buffer.data().limit() - buffer.data().position() < json.position) {
                error.setError(JsonConverterErrorCodes.JSON_ERROR, "Buffer length is not enough to encode the message, expected " + json.position + " bytes, found " + (buffer.data().limit() - buffer.data().position()) + " bytes");
                return FAILURE;
            }
            buffer.data().put(json.data, 0, json.position);
        } else {
            int fullLength = json.position - BufferHelper.getCurrentStreamIdLength(json, error) + BasicPrimitiveConverter.getLongLengthCompare(options.getStreamId());
            if (buffer.data().limit() - buffer.data().position() < fullLength) {
                error.setError(JsonConverterErrorCodes.JSON_ERROR, "Buffer length is not enough to encode the message, expected " + fullLength + " bytes, found " + (buffer.data().limit() - buffer.data().position()) + " bytes");
                return FAILURE;
            }
            BufferHelper.composeMessage(buffer.data(), options.getStreamId(), json, error);
        }
        return SUCCESS;
    }

    @Override
    public int getErrorMessage(Buffer outBuffer, GetJsonErrorParams params, JsonConverterError error) {

        try {
            byte[] currentMessage = null;
            if (currentState.get().getFailedNode() != null) {
                currentMessage = currentState.get().getFailedNode().toString().getBytes("UTF-8");
            } else if (currentState.get().getFailedMessage() != null) {
                currentMessage = currentState.get().getFailedMessage();
            }

            int countCharsToEscape = BasicPrimitiveConverter.charsToEscapeCount(currentMessage);

            int estimatedMaxLength = (currentMessage != null ? currentMessage.length : 0)
                    + params.getFile().getBytes(StandardCharsets.UTF_8).length
                    + params.getText().getBytes(StandardCharsets.UTF_8).length
                    + BasicPrimitiveConverter.getLongLengthCompare(params.getLine())
                    + countCharsToEscape + 207;

            JsonBuffer buffer;
            try {
                buffer = new JsonBuffer(estimatedMaxLength);
            } catch (Exception e) {

                error.setError(JsonConverterErrorCodes.JSON_ERROR, "Failed to create JSON Error message: " + e.getMessage());
                return FAILURE;
            }

            boolean res = BufferHelper.beginObject(buffer, error)
                    && BufferHelper.writeArrayAndColon(JSON_ID, buffer, false, error)
                    && BasicPrimitiveConverter.writeLong(params.getStreamId(), buffer, error)
                    && BufferHelper.writeArrayAndColon(ConstCharArrays.JSON_TYPE, buffer, true, error)
                    && BufferHelper.writeArray(ConstCharArrays.JSON_ERROR, buffer, true, error)
                    && BufferHelper.writeArrayAndColon(ConstCharArrays.JSON_TEXT, buffer, true, error)
                    && BasicPrimitiveConverter.writeSafeString(params.getText().getBytes(StandardCharsets.UTF_8), buffer, error)
                    && BufferHelper.writeArrayAndColon(ConstCharArrays.JSON_DEBUG, buffer, true, error)
                    && BufferHelper.beginObject(buffer, error);

            if (res && params.getFile() != null) {
                res = BufferHelper.writeArrayAndColon(ConstCharArrays.JSON_FILE, buffer, false, error)
                        && BufferHelper.writeArray(params.getFile(), buffer, true, error);
            }

            if (res && params.getLine() != GetJsonErrorParams.EMPTY_LINE_VALUE) {
                res = BufferHelper.writeArrayAndColon(ConstCharArrays.JSON_LINE, buffer, true, error)
                        && BasicPrimitiveConverter.writeLong(params.getLine(), buffer, error);
            }

            if (res && currentMessage != null) {
                res = BufferHelper.writeArrayAndColon(ConstCharArrays.JSON_MESSAGE, buffer, true, error)
                        && BasicPrimitiveConverter.writeSafeString(currentMessage, buffer, error);
            }

            res = res && BufferHelper.endObject(buffer, error) && BufferHelper.endObject(buffer, error);

            if (res) {
                ByteBuffer output = ByteBuffer.wrap(buffer.data, 0, buffer.position);
                outBuffer.data(output);
            }

            return res ? SUCCESS : FAILURE;
        } catch (UnsupportedEncodingException e) {

            error.setError(JsonConverterErrorCodes.JSON_ERROR, "Failed to create JSON Error message: " + e.getMessage());
            return FAILURE;
        }
    }

    boolean processMsg(DecodeIterator decIter, Msg inMsg, JsonBuffer outBuffer, JsonConverterError error, boolean first) {

        String msgClass = getMsgClassString(inMsg.msgClass());
        if (msgClass == null) {
            error.setError(FAILURE, "Unsupported msg class " + inMsg.msgClass());
            return false;
        }
        if (inMsg.msgClass() != MsgClasses.CLOSE) {
            BufferHelper.beginObject(outBuffer, error);
            BufferHelper.writeArrayAndColon(JSON_ID, outBuffer, false, error);
            BasicPrimitiveConverter.writeLong(inMsg.streamId(), outBuffer, error);
            BufferHelper.writeArrayAndColon(JSON_TYPE, outBuffer, true, error);
            BufferHelper.writeArray(msgClass, outBuffer, true, error);
            if (inMsg.domainType() != DomainTypes.MARKET_PRICE) {
                BufferHelper.writeArrayAndColon(JSON_DOMAIN, outBuffer, true, error);
                String domain = getDomainString(inMsg.domainType());
                if (domain != null)
                    BufferHelper.writeArray(domain, outBuffer, true, error);
                else
                    BasicPrimitiveConverter.writeLong(inMsg.domainType(), outBuffer, error);
            }
        } else { //close message handles everything on its own
            BufferHelper.beginObject(outBuffer, error);
        }

        return error.isSuccessful() && this.getRsslMessageHandler(inMsg.msgClass()).encodeJson(decIter, inMsg, outBuffer, error) && BufferHelper.endObject(outBuffer, error);
    }

    AbstractRsslMessageTypeConverter getRsslMessageHandler(int msgClassTypeId, JsonConverterError error) {
        AbstractRsslMessageTypeConverter handler = getRsslMessageHandler(msgClassTypeId);
        if (handler != null)
            return handler;

        currentState.get().setFailedNode(currentState.get().getWorkingNode());
        error.setError(JsonConverterErrorCodes.JSON_ERROR_UNSUPPORTED_MESSAGE, "dataType [" + msgClassTypeId + "]" + " no rsslMessageHandler found");
        return null;
    }

    @Override
    AbstractTypeConverter getHandler(int dataType, JsonConverterError error) {
        AbstractTypeConverter handler = getContainerHandler(dataType);
        if (handler != null)
            return handler;

        handler = getPrimitiveHandler(dataType);

        if (handler != null)
            return handler;

        currentState.get().setFailedNode(currentState.get().getWorkingNode());
        error.setError(JsonConverterErrorCodes.JSON_ERROR_UNSUPPORTED_MESSAGE, "dataType [" + dataType + "]" + " no primitive/container handler found");
        return null;
    }

    @Override
    AbstractTypeConverter getHandler(RsslMsgChunkType rsslMsgChunkType, JsonConverterError error) {
        AbstractTypeConverter handler = getMsgChunkHandler(rsslMsgChunkType);
        if (handler != null)
            return handler;

        currentState.get().setFailedNode(currentState.get().getWorkingNode());
        error.setError(JsonConverterErrorCodes.JSON_ERROR_UNSUPPORTED_MESSAGE, "dataType [" + rsslMsgChunkType + "]" + " no rssl msgChunk handler found");
        return handler;
    }

    @Override
    AbstractPrimitiveTypeConverter getPrimitiveHandler(int dataType) {

        if (dataType >= DataTypes.UNKNOWN && dataType <= DataTypes.RMTES_STRING
            || dataType >= DataTypes.INT_1 && dataType <= DataTypes.TIME_8) {
            return primitiveHandlerMap.get(dataType);
        }
        return null;
    }

    @Override
    AbstractContainerTypeConverter getContainerHandler(int dataType) {
        return containerHandlerMap.get(dataType);
    }

    @Override
    AbstractRsslMessageChunkTypeConverter getMsgChunkHandler(RsslMsgChunkType rsslMsgChunkType) {
        return rsslMsgChunkHandlerMap.get(rsslMsgChunkType);
    }

    @Override
    AbstractRsslMessageTypeConverter getRsslMessageHandler(int msgClassId) {
        return rsslMsgHandlerMap.get(msgClassId);
    }

    @Override
    int getContainerDataType(String jsonTagName, JsonNode jsonNode, JsonConverterError error) {
        int dataType = getContainerType(jsonTagName);
        if (dataType == DataTypes.NO_DATA) {
            currentState.get().setFailedNode(currentState.get().getWorkingNode());
            error.setError(JsonConverterErrorCodes.JSON_ERROR_INVALID_CONTAINER_TYPE, "jsonTag " + jsonTagName + " container is not supported");
        }
        if (!checkContainerValueType(dataType, jsonNode)) {
            currentState.get().setFailedNode(currentState.get().getWorkingNode());
            error.setError(JsonConverterErrorCodes.JSON_ERROR_UNEXPECTED_VALUE, "found value of type " + jsonNode.getNodeType().toString() + " for container type " + jsonTagName);
        }

        return dataType;
    }

    @Override
    int getContainerType(String type) {
        switch (type) {
            case JSON_FIELDLIST:
            case JSON_FIELDS:
                return DataTypes.FIELD_LIST;
            case JSON_FILTERLIST:
                return DataTypes.FILTER_LIST;
            case JSON_ELEMENTS:
            case JSON_ELEMENTLIST:
                return DataTypes.ELEMENT_LIST;
            case JSON_JSON:
                return DataTypes.JSON;
            case JSON_MAP:
                return DataTypes.MAP;
            case JSON_MESSAGE:
                return DataTypes.MSG;
            case JSON_OPAQUE:
                return DataTypes.OPAQUE;
            case JSON_VECTOR:
                return DataTypes.VECTOR;
            case JSON_SERIES:
                return DataTypes.SERIES;
            case JSON_XML:
                return DataTypes.XML;
            case JSON_ANSI:
                return DataTypes.ANSI_PAGE;
            default:
                return DataTypes.NO_DATA;
        }
    }

    private boolean checkContainerValueType(int dataType, JsonNode jsonNode) {
        switch (dataType) {
            case DataTypes.XML:
            case DataTypes.OPAQUE:
                return jsonNode.isTextual() || jsonNode.isNull();
            case DataTypes.JSON:
            case DataTypes.MAP:
            case DataTypes.SERIES:
            case DataTypes.VECTOR:
            case DataTypes.MSG:
            case DataTypes.ELEMENT_LIST:
            case DataTypes.FIELD_LIST:
            case DataTypes.FILTER_LIST:
                return jsonNode.isObject() ||  jsonNode.isNull();
            default:
                return false;
        }
    }

    private int estimateJsonLength(int rwfLength) {

        return rwfLength * 6 + 300;
    }

    private void prepareJsonMsgToDecode(JsonMsg jsonMsg) {
        final Buffer currentBuffer = currentState.get().getCurrentBufferData();
        if (jsonMsg.jsonMsgData().length() <= 0) {
            jsonMsg.jsonMsgData().data(ByteBuffer.allocate(currentBuffer.length()));
        }
        if (jsonMsg.rwfMsg().encodedMsgBuffer().length() <= 0) {
            jsonMsg.rwfMsg().encodedMsgBuffer().data(ByteBuffer.allocate(currentBuffer.length()));
        }
        currentBuffer.copy(jsonMsg.jsonMsgData());
    }

    private String getMsgClassString(int msgClass) {
        switch (msgClass) {
            case MsgClasses.ACK:
                return MC_ACK;
            case MsgClasses.CLOSE:
                return MC_CLOSE;
            case MsgClasses.GENERIC:
                return MC_GENERIC;
            case MsgClasses.POST:
                return MC_POST;
            case MsgClasses.REQUEST:
                return MC_REQUEST;
            case MsgClasses.REFRESH:
                return MC_REFRESH;
            case MsgClasses.STATUS:
                return MC_STATUS;
            case MsgClasses.UPDATE:
                return MC_UPDATE;
            default:
                return null;
        }
    }

    private String getDomainString(int domainType) {
        switch (domainType) {
            case DomainTypes.LOGIN:
                return DOMAIN_STR_LOGIN;
            case DomainTypes.SOURCE:
                return DOMAIN_STR_SOURCE;
            case DomainTypes.DICTIONARY:
                return DOMAIN_STR_DICTIONARY;
            case DomainTypes.MARKET_PRICE:
                return DOMAIN_STR_MARKET_PRICE;
            case DomainTypes.MARKET_BY_ORDER:
                return DOMAIN_STR_MARKET_BY_ORDER;
            case DomainTypes.MARKET_BY_PRICE:
                return DOMAIN_STR_MARKET_BY_PRICE;
            case DomainTypes.MARKET_MAKER:
                return DOMAIN_STR_MARKET_MAKER;
            case DomainTypes.SYMBOL_LIST:
                return DOMAIN_STR_SYMBOL_LIST;
            case DomainTypes.SERVICE_PROVIDER_STATUS:
                return DOMAIN_STR_SERVICE_PROVIDER_STATUS;
            case DomainTypes.HISTORY:
                return DOMAIN_STR_HISTORY;
            case DomainTypes.HEADLINE:
                return DOMAIN_STR_HEADLINE;
            case DomainTypes.STORY:
                return DOMAIN_STR_STORY;
            case DomainTypes.REPLAYHEADLINE:
                return DOMAIN_STR_REPLAYHEADLINE;
            case DomainTypes.REPLAYSTORY:
                return DOMAIN_STR_REPLAYSTORY;
            case DomainTypes.TRANSACTION:
                return DOMAIN_STR_TRANSACTION;
            case DomainTypes.YIELD_CURVE:
                return DOMAIN_STR_YIELD_CURVE;
            case DomainTypes.CONTRIBUTION:
                return DOMAIN_STR_CONTRIBUTION;
            case DomainTypes.PROVIDER_ADMIN:
                return DOMAIN_STR_PROVIDER_ADMIN;
            case DomainTypes.ANALYTICS:
                return DOMAIN_STR_ANALYTICS;
            case DomainTypes.REFERENCE:
                return DOMAIN_STR_REFERENCE;
            case DomainTypes.NEWS_TEXT_ANALYTICS:
                return DOMAIN_STR_NEWS_TEXT_ANALYTICS;
            case DomainTypes.ECONOMIC_INDICATOR:
                return DOMAIN_STR_ECONOMIC_INDICATOR;
            case DomainTypes.POLL:
                return DOMAIN_STR_POLL;
            case DomainTypes.FORECAST:
                return DOMAIN_STR_FORECAST;
            case DomainTypes.MARKET_BY_TIME:
                return DOMAIN_STR_MARKET_BY_TIME;
            case DomainTypes.SYSTEM:
                return DOMAIN_STR_SYSTEM;
            default:
                return null;

        }
    }

    @Override
    synchronized EnumTableDefinition getEnumTableDefinition(EnumTypeTable enumTypeTable)
    {
    	EnumTableDefinition enumTableDefinition = enumTableDefinitionMap.get(enumTypeTable);
    	if(Objects.isNull(enumTableDefinition))
		{
    		enumTableDefinition = new EnumTableDefinition(enumTypeTable.maxValue());
    		enumTableDefinitionMap.put(enumTypeTable, enumTableDefinition);
		}

    	return enumTableDefinition;
    }

    @Override
    ObjectMapper getMapper() {
        return mapper.get();
    }

    @Override
    DictionaryEntry dictionaryEntry() {
        return dictionaryEntry.get();
    }

    @Override
    void dictionaryEntry(DictionaryEntry entry) {
        dictionaryEntry.set(entry);
    }

    @Override
    ServiceNameIdConverter getServiceNameIdConverter() {
        return serviceNameIdConverter;
    }

    @Override
    DataDictionary getDictionary() {
        return dictionary;
    }
    void setDictionary(DataDictionary dictionary) {
        this.dictionary = dictionary;
    }

    @Override
    boolean catchUnexpectedKeys() {
        return catchUnexpectedKeys;
    }
    void catchUnexpectedKeys(boolean enabled) {
        catchUnexpectedKeys = enabled;
    }

    @Override
    boolean catchUnexpectedFids() {
        return catchUnexpectedFids;
    }
    void catchUnexpectedFids(boolean enabled) {
        catchUnexpectedFids = enabled;
    }

    @Override
    boolean allowEnumDisplayStrings() {
        return allowEnumDisplayStrings;
    }
    void allowEnumDisplayStrings(boolean enabled) {
        allowEnumDisplayStrings = enabled;
    }

    @Override
    boolean useDefaultDynamicQoS() {
        return useDefaultQoS;
    }
    void useDefaultDynamicQoS(boolean enabled) {
        useDefaultQoS = enabled;
    }

    @Override
    boolean expandEnumFields() {
        return expandEnumFields;
    }
    void expandEnumFields(boolean enabled) {
        expandEnumFields = enabled;
    }

    @Override
    int getDefaultServiceId() {
        return defaultServiceId;
    }

    @Override
    boolean hasDefaultServiceId() {
        return hasDefaultServiceId;
    }

    @Override
    void setHasDefaultServiceId(boolean value) {
        hasDefaultServiceId = value;
    }

    void setDefaultServiceId(int id) {
        defaultServiceId = id;
    }

    private int parseJsonBuffer(byte[] data, JsonConverterError error) {
        try {
            final JsonConverterState jsonConverterState = Optional
                    .ofNullable(currentState.get())
                    .orElseGet(JsonConverterState::new);
            jsonConverterState.clear();
            currentState.set(jsonConverterState);
            jsonConverterState.setCurrentRoot(mapper.get().readTree(data));
            jsonConverterState.getCurrentBufferData().data(ByteBuffer.wrap(data));
        } catch (IOException e) {
            currentState.get().setFailedMessage(data);
            return error.setError(JsonConverterErrorCodes.JSON_ERROR_PARSE_ERROR, e.getMessage());
        }
        return SUCCESS;
    }

    private int parseJsonBuffer(TransportBuffer buffer, JsonConverterError error) {
        try {
            final JsonConverterState jsonConverterState = Optional
                    .ofNullable(currentState.get())
                    .orElseGet(JsonConverterState::new);
            jsonConverterState.clear();
            currentState.set(jsonConverterState);
            ByteBuffer data = buffer.data();
            ByteBufferInputStream stream = inputStream.get();
            stream.setByteBuffer(data, buffer.dataStartPosition(), data.limit());
            ObjectMapper objectMapper = mapper.get();
            objectMapper.enable(JsonReadFeature.ALLOW_LEADING_ZEROS_FOR_NUMBERS.mappedFeature());
            jsonConverterState.setCurrentRoot(objectMapper.readTree(stream));
            jsonConverterState.getCurrentBufferData().data(data);
        } catch (IOException e) {
            byte[] data = new byte[buffer.length()];
            ByteBuffer inData = buffer.data();
            for (int i = 0; i < buffer.length(); i++)
                data[i] = inData.get(i + buffer.dataStartPosition());
            currentState.get().setFailedMessage(data);
            return error.setError(JsonConverterErrorCodes.JSON_ERROR_PARSE_ERROR, e.getMessage());
        }
        return SUCCESS;
    }

    @Override
    public void decodeRsslMessage(int msgClassTypeId, JsonNode path, Object msg, JsonConverterError error, EncodeIterator encodeIterator) {
        AbstractRsslMessageTypeConverter subParser = getRsslMessageHandler(msgClassTypeId, error);
        if (error.isFailed())
            return;

        subParser.decodeJson(path, msg, error);
        if (error.isFailed()) {
            return;
        }
        subParser.encodeRWF(path, "root", (Msg) msg, encodeIterator, error);
    }

    @Override
    public void decodeChunk(int dataType, JsonNode path, Object msg, JsonConverterError error) {
        AbstractTypeConverter subParser = getHandler(dataType, error);
        if (error.isFailed())
            return;

        subParser.decodeJson(path, msg, error);
    }

    protected void decodeChunk(RsslMsgChunkType rsslMsgChunkType, JsonNode path, Object msg, JsonConverterError error) {
        AbstractTypeConverter subParser = getHandler(rsslMsgChunkType, error);
        if (error.isFailed())
            return;

        subParser.decodeJson(path, msg, error);
    }

    @Override
    public void decodeChunk(int dataType, JsonNode dataNode, String key, EncodeIterator iterator, JsonConverterError error) {
        AbstractTypeConverter subParser = getHandler(dataType, error);
        if (subParser == null) {
            error.setError(JsonConverterErrorCodes.JSON_ERROR_UNSUPPORTED_MESSAGE,"Unknown datatype to parse: [" + dataType + "]");
            return;
        }
        if (subParser instanceof AbstractPrimitiveTypeConverter) {
            if (!((AbstractPrimitiveTypeConverter) subParser).isInRange(dataType, dataNode)) {
                error.setError(JsonConverterErrorCodes.JSON_ERROR_UNEXPECTED_VALUE,"Value outside the range: [" + dataType + "]");
                return;
            }
        }
        subParser.encodeRWF(dataNode, key, iterator, error);
    }

    @Override
    public int getRwfMsgTypeFromJson(JsonNode type, JsonConverterError error) {
        if (type.isInt()) {
            return type.intValue();
        }
        return stringToMsgClass(type.textValue(), error);
    }

    private int stringToMsgClass(String type, JsonConverterError error) {
        if (!STRING_TO_RWF_MSG_CLASS.containsKey(type)) {
            currentState.get().setFailedNode(currentState.get().getWorkingNode());
            return error.setError(JsonConverterErrorCodes.JSON_ERROR_UNSUPPORTED_MESSAGE, type, "root");
        }
        return STRING_TO_RWF_MSG_CLASS.get(type);
    }

    @Override
    public int getJsonMsgType(JsonMsg jsonMsg, JsonNode rootNode, JsonConverterError error) {
        int jsonTypeMsg = 0;
        int rwfTypeMsg = 0;
        JsonNode typeNode = null;
        if (!rootNode.isMissingNode()) {
            typeNode = rootNode.path(JSON_TYPE);
            if (!typeNode.isMissingNode()) {
                if (typeNode.isInt()) {
                    jsonTypeMsg = JsonMsgClasses.RSSL_MESSAGE;
                    rwfTypeMsg = typeNode.intValue();
                } else {
                    if (STRING_TO_RWF_MSG_CLASS.containsKey(typeNode.textValue())) {
                        jsonTypeMsg = JsonMsgClasses.RSSL_MESSAGE;
                        rwfTypeMsg = STRING_TO_RWF_MSG_CLASS.get(typeNode.textValue());
                    } else if (STRING_TO_JSON_MSG_CLASS.containsKey(typeNode.textValue())) {
                        jsonTypeMsg = STRING_TO_JSON_MSG_CLASS.get(typeNode.textValue());
                    }
                }
            } else {
                jsonTypeMsg = JsonMsgClasses.RSSL_MESSAGE;
                rwfTypeMsg = MsgClasses.REQUEST;
            }
        }
        if (jsonTypeMsg == 0 || (jsonTypeMsg == JsonMsgClasses.RSSL_MESSAGE && rwfTypeMsg == 0)) {
            currentState.get().setFailedNode(rootNode);
            return error.setError(JsonConverterErrorCodes.JSON_ERROR_UNSUPPORTED_MSG_TYPE, "Message type is not supported: " + typeNode);
        }
        jsonMsg.jsonMsgClass(jsonTypeMsg);
        if (jsonTypeMsg == JsonMsgClasses.RSSL_MESSAGE) {
            jsonMsg.rwfMsg().msgClass(rwfTypeMsg);
        }
        return SUCCESS;
    }

    @Override
    int getDataType(JsonNode dataType) {

        if (dataType.isInt()) {
            int type = dataType.asInt() + DataTypes.CONTAINER_TYPE_MIN;
            if (type >= DataTypes.UNKNOWN && type <= DataTypes.CONTAINER_TYPE_MAX)
                return type;
            else return FAILURE;
        } else if (dataType.isTextual()) {
            switch (dataType.asText()) {
                case DATA_TYPE_STR_UNKNOWN:
                    return DataTypes.UNKNOWN;
                case DATA_TYPE_STR_INT:
                    return DataTypes.INT;
                case DATA_TYPE_STR_UINT:
                    return DataTypes.UINT;
                case DATA_TYPE_STR_FLOAT:
                    return DataTypes.FLOAT;
                case DATA_TYPE_STR_DOUBLE:
                    return DataTypes.DOUBLE;
                case DATA_TYPE_STR_REAL:
                    return DataTypes.REAL;
                case DATA_TYPE_STR_DATE:
                    return DataTypes.DATE;
                case DATA_TYPE_STR_TIME:
                    return DataTypes.TIME;
                case DATA_TYPE_STR_DATE_TIME:
                    return DataTypes.DATETIME;
                case DATA_TYPE_STR_QOS:
                    return DataTypes.QOS;
                case DATA_TYPE_STR_STATE:
                    return DataTypes.STATE;
                case DATA_TYPE_STR_ENUM:
                    return DataTypes.ENUM;
                case DATA_TYPE_STR_ARRAY:
                    return DataTypes.ARRAY;
                case DATA_TYPE_STR_BUFFER:
                    return DataTypes.BUFFER;
                case DATA_TYPE_STR_ASCII_STRING:
                    return DataTypes.ASCII_STRING;
                case DATA_TYPE_STR_UTF_8_STRING:
                    return DataTypes.UTF8_STRING;
                case DATA_TYPE_STR_RMTES_STRING:
                    return DataTypes.RMTES_STRING;
                case DATA_TYPE_STR_INT_1:
                    return DataTypes.INT_1;
                case DATA_TYPE_STR_UINT_1:
                    return DataTypes.UINT_1;
                case DATA_TYPE_STR_INT_2:
                    return DataTypes.INT_2;
                case DATA_TYPE_STR_UINT_2:
                    return DataTypes.UINT_2;
                case DATA_TYPE_STR_INT_4:
                    return DataTypes.INT_4;
                case DATA_TYPE_STR_UINT_4:
                    return DataTypes.UINT_4;
                case DATA_TYPE_STR_INT_8:
                    return DataTypes.INT_8;
                case DATA_TYPE_STR_UINT_8:
                    return DataTypes.UINT_8;
                case DATA_TYPE_STR_FLOAT_4:
                    return DataTypes.FLOAT_4;
                case DATA_TYPE_STR_DOUBLE_8:
                    return DataTypes.DOUBLE_8;
                case DATA_TYPE_STR_REAL_4_RB:
                    return DataTypes.REAL_4RB;
                case DATA_TYPE_STR_REAL_8_RB:
                    return DataTypes.REAL_8RB;
                case DATA_TYPE_STR_DATE_4:
                    return DataTypes.DATE_4;
                case DATA_TYPE_STR_TIME_3:
                    return DataTypes.TIME_3;
                case DATA_TYPE_STR_TIME_5:
                    return DataTypes.TIME_5;
                case DATA_TYPE_STR_DATE_TIME_7:
                    return DataTypes.DATETIME_7;
                case DATA_TYPE_STR_DATE_TIME_9:
                    return DataTypes.DATETIME_9;
                case DATA_TYPE_STR_DATE_TIME_11:
                    return DataTypes.DATETIME_11;
                case DATA_TYPE_STR_DATE_TIME_12:
                    return DataTypes.DATETIME_12;
                case DATA_TYPE_STR_TIME_7:
                    return DataTypes.TIME_7;
                case DATA_TYPE_STR_TIME_8:
                    return DataTypes.TIME_8;
                case DATA_TYPE_STR_NO_DATA:
                    return DataTypes.NO_DATA;
                case DATA_TYPE_STR_OPAQUE:
                    return DataTypes.OPAQUE;
                case DATA_TYPE_STR_XML:
                    return DataTypes.XML;
                case DATA_TYPE_STR_FIELD_LIST:
                    return DataTypes.FIELD_LIST;
                case DATA_TYPE_STR_ELEMENT_LIST:
                    return DataTypes.ELEMENT_LIST;
                case DATA_TYPE_STR_ANSI_PAGE:
                    return DataTypes.ANSI_PAGE;
                case DATA_TYPE_STR_FILTER_LIST:
                    return DataTypes.FILTER_LIST;
                case DATA_TYPE_STR_VECTOR:
                    return DataTypes.VECTOR;
                case DATA_TYPE_STR_MAP:
                    return DataTypes.MAP;
                case DATA_TYPE_STR_SERIES:
                    return DataTypes.SERIES;
                case DATA_TYPE_STR_MSG:
                    return DataTypes.MSG;
                case DATA_TYPE_STR_JSON:
                    return DataTypes.JSON;
                default:
                    return FAILURE;
            }
        }

        return FAILURE;
    }

    @Override
    int getPayloadType(JsonNode node) {
        JsonNode curr;
        for (int i = 0; i < node.size(); i++) {
            curr = node.get(i);
            for (Iterator it = curr.fieldNames(); it.hasNext(); ) {
                String name = it.next();
                switch (name) {
                    case ConstCharArrays.JSON_FIELDLIST:
                    case ConstCharArrays.JSON_FIELDS:
                        return DataTypes.FIELD_LIST;
                    case ConstCharArrays.JSON_FILTERLIST:
                        return DataTypes.FILTER_LIST;
                    case ConstCharArrays.JSON_ELEMENTS:
                    case ConstCharArrays.JSON_ELEMENTLIST:
                        return DataTypes.ELEMENT_LIST;
                    case ConstCharArrays.JSON_JSON:
                        return DataTypes.JSON;
                    case ConstCharArrays.JSON_MAP:
                        return DataTypes.MAP;
                    case ConstCharArrays.JSON_MESSAGE:
                        return DataTypes.MSG;
                    case ConstCharArrays.JSON_OPAQUE:
                        return DataTypes.OPAQUE;
                    case ConstCharArrays.JSON_VECTOR:
                        return DataTypes.VECTOR;
                    case ConstCharArrays.JSON_SERIES:
                        return DataTypes.SERIES;
                    case ConstCharArrays.JSON_XML:
                        return DataTypes.XML;
                    case ConstCharArrays.JSON_ANSI:
                        return DataTypes.ANSI_PAGE;
                    default:
                        break;
                }
            }
        }

        return DataTypes.NO_DATA;
    }

    @Override
    public String getDataType(int dataType) {

        if (dataType >= DataTypes.UNKNOWN && dataType <= DataTypes.RMTES_STRING)
            return ConstCharArrays.dataTypeStrings[dataType];
        else if (dataType >= DataTypes.INT_1 && dataType <= DataTypes.TIME_8)
            return ConstCharArrays.dataTypeStrings[dataType - INT_1_SHIFT];
        else if (dataType >= DataTypes.OPAQUE && dataType <= DataTypes.CONTAINER_TYPE_MAX)
            return ConstCharArrays.dataTypeStrings[dataType - OPAQUE_SHIFT];
        else if (dataType == DataTypes.NO_DATA)
            return ConstCharArrays.dataTypeStrings[NO_DATA_POSITION];
        return null;
    }

    @Override
    boolean processMsgKey(DecodeIterator decIter, Object msgKey, JsonBuffer outBuffer, int domain, boolean wantServiceName, JsonConverterError error) {
        return ((JsonMsgKeyConverter)rsslMsgChunkHandlerMap.get(RsslMsgChunkType.MSG_KEY_CHUNK)).encodeJson(decIter, msgKey, outBuffer, domain, wantServiceName, error);
    }

    @Override
    boolean processPostUserInfo(PostUserInfo info, JsonBuffer outBuffer, JsonConverterError error) {
        return ((JsonPostUserInfoConverter) rsslMsgChunkHandlerMap.get(RsslMsgChunkType.POST_USER_INFO_CHUNK)).writeToJson(outBuffer, info, error);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy