org.apache.thrift.protocol.TSimpleJSONProtocol.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.thrift.protocol.TSimpleJSONProtocol.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.thrift.protocol;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Stack;

import org.apache.thrift.TException;
import org.apache.thrift.transport.TTransport;

/**
 * JSON protocol implementation for thrift.
 *
 * This protocol is write-only and produces a simple output format
 * suitable for parsing by scripting languages.  It should not be
 * confused with the full-featured TJSONProtocol.
 *
 */
public class TSimpleJSONProtocol extends TProtocol {

    /**
     * Factory
     */
    public static class Factory implements TProtocolFactory {
        public TProtocol getProtocol(TTransport trans) {
            return new TSimpleJSONProtocol(trans);
        }
    }

    private static final byte[] COMMA = new byte[] { ',' };
    private static final byte[] COLON = new byte[] { ':' };
    private static final byte[] LBRACE = new byte[] { '{' };
    private static final byte[] RBRACE = new byte[] { '}' };
    private static final byte[] LBRACKET = new byte[] { '[' };
    private static final byte[] RBRACKET = new byte[] { ']' };
    private static final char QUOTE = '"';

    private static final TStruct ANONYMOUS_STRUCT = new TStruct();
    private static final TField ANONYMOUS_FIELD = new TField();
    private static final TMessage EMPTY_MESSAGE = new TMessage();
    private static final TSet EMPTY_SET = new TSet();
    private static final TList EMPTY_LIST = new TList();
    private static final TMap EMPTY_MAP = new TMap();
    private static final String LIST = "list";
    private static final String SET = "set";
    private static final String MAP = "map";

    protected class Context {
        protected void write() throws TException {
        }

        /**
         * Returns whether the current value is a key in a map
         */
        protected boolean isMapKey() {
            return false;
        }
    }

    protected class ListContext extends Context {
        protected boolean first_ = true;

        protected void write() throws TException {
            if (first_) {
                first_ = false;
            } else {
                trans_.write(COMMA);
            }
        }
    }

    protected class StructContext extends Context {
        protected boolean first_ = true;
        protected boolean colon_ = true;

        protected void write() throws TException {
            if (first_) {
                first_ = false;
                colon_ = true;
            } else {
                trans_.write(colon_ ? COLON : COMMA);
                colon_ = !colon_;
            }
        }
    }

    protected class MapContext extends StructContext {
        protected boolean isKey = true;

        @Override
        protected void write() throws TException {
            super.write();
            isKey = !isKey;
        }

        protected boolean isMapKey() {
            // we want to coerce map keys to json strings regardless
            // of their type
            return isKey;
        }
    }

    protected final Context BASE_CONTEXT = new Context();

    /**
     * Stack of nested contexts that we may be in.
     */
    protected Stack<Context> writeContextStack_ = new Stack<Context>();

    /**
     * Current context that we are in
     */
    protected Context writeContext_ = BASE_CONTEXT;

    /**
     * Push a new write context onto the stack.
     */
    protected void pushWriteContext(Context c) {
        writeContextStack_.push(writeContext_);
        writeContext_ = c;
    }

    /**
     * Pop the last write context off the stack
     */
    protected void popWriteContext() {
        writeContext_ = writeContextStack_.pop();
    }

    /**
     * Reset the write context stack to its initial state.
     */
    protected void resetWriteContext() {
        while (!writeContextStack_.isEmpty()) {
            popWriteContext();
        }
    }

    /**
     * Used to make sure that we are not encountering a map whose keys are containers
     */
    protected void assertContextIsNotMapKey(String invalidKeyType) throws CollectionMapKeyException {
        if (writeContext_.isMapKey()) {
            throw new CollectionMapKeyException(
                    "Cannot serialize a map with keys that are of type " + invalidKeyType);
        }
    }

    /**
     * Constructor
     */
    public TSimpleJSONProtocol(TTransport trans) {
        super(trans);
    }

    @Override
    public void writeMessageBegin(TMessage message) throws TException {
        resetWriteContext(); // THRIFT-3743
        trans_.write(LBRACKET);
        pushWriteContext(new ListContext());
        writeString(message.name);
        writeByte(message.type);
        writeI32(message.seqid);
    }

    @Override
    public void writeMessageEnd() throws TException {
        popWriteContext();
        trans_.write(RBRACKET);
    }

    @Override
    public void writeStructBegin(TStruct struct) throws TException {
        writeContext_.write();
        trans_.write(LBRACE);
        pushWriteContext(new StructContext());
    }

    @Override
    public void writeStructEnd() throws TException {
        popWriteContext();
        trans_.write(RBRACE);
    }

    @Override
    public void writeFieldBegin(TField field) throws TException {
        // Note that extra type information is omitted in JSON!
        writeString(field.name);
    }

    @Override
    public void writeFieldEnd() throws TException {
    }

    @Override
    public void writeFieldStop() throws TException {
    }

    @Override
    public void writeMapBegin(TMap map) throws TException {
        assertContextIsNotMapKey(MAP);
        writeContext_.write();
        trans_.write(LBRACE);
        pushWriteContext(new MapContext());
        // No metadata!
    }

    @Override
    public void writeMapEnd() throws TException {
        popWriteContext();
        trans_.write(RBRACE);
    }

    @Override
    public void writeListBegin(TList list) throws TException {
        assertContextIsNotMapKey(LIST);
        writeContext_.write();
        trans_.write(LBRACKET);
        pushWriteContext(new ListContext());
        // No metadata!
    }

    @Override
    public void writeListEnd() throws TException {
        popWriteContext();
        trans_.write(RBRACKET);
    }

    @Override
    public void writeSetBegin(TSet set) throws TException {
        assertContextIsNotMapKey(SET);
        writeContext_.write();
        trans_.write(LBRACKET);
        pushWriteContext(new ListContext());
        // No metadata!
    }

    @Override
    public void writeSetEnd() throws TException {
        popWriteContext();
        trans_.write(RBRACKET);
    }

    @Override
    public void writeBool(boolean b) throws TException {
        writeByte(b ? (byte) 1 : (byte) 0);
    }

    @Override
    public void writeByte(byte b) throws TException {
        writeI32(b);
    }

    @Override
    public void writeI16(short i16) throws TException {
        writeI32(i16);
    }

    @Override
    public void writeI32(int i32) throws TException {
        if (writeContext_.isMapKey()) {
            writeString(Integer.toString(i32));
        } else {
            writeContext_.write();
            _writeStringData(Integer.toString(i32));
        }
    }

    public void _writeStringData(String s) throws TException {
        byte[] b = s.getBytes(StandardCharsets.UTF_8);
        trans_.write(b);
    }

    @Override
    public void writeI64(long i64) throws TException {
        if (writeContext_.isMapKey()) {
            writeString(Long.toString(i64));
        } else {
            writeContext_.write();
            _writeStringData(Long.toString(i64));
        }
    }

    @Override
    public void writeDouble(double dub) throws TException {
        if (writeContext_.isMapKey()) {
            writeString(Double.toString(dub));
        } else {
            writeContext_.write();
            _writeStringData(Double.toString(dub));
        }
    }

    @Override
    public void writeString(String str) throws TException {
        writeContext_.write();
        int length = str.length();
        StringBuffer escape = new StringBuffer(length + 16);
        escape.append(QUOTE);
        for (int i = 0; i < length; ++i) {
            char c = str.charAt(i);
            switch (c) {
            case '"':
            case '\\':
                escape.append('\\');
                escape.append(c);
                break;
            case '\b':
                escape.append('\\');
                escape.append('b');
                break;
            case '\f':
                escape.append('\\');
                escape.append('f');
                break;
            case '\n':
                escape.append('\\');
                escape.append('n');
                break;
            case '\r':
                escape.append('\\');
                escape.append('r');
                break;
            case '\t':
                escape.append('\\');
                escape.append('t');
                break;
            default:
                // Control characters! According to JSON RFC u0020 (space)
                if (c < ' ') {
                    String hex = Integer.toHexString(c);
                    escape.append('\\');
                    escape.append('u');
                    for (int j = 4; j > hex.length(); --j) {
                        escape.append('0');
                    }
                    escape.append(hex);
                } else {
                    escape.append(c);
                }
                break;
            }
        }
        escape.append(QUOTE);
        _writeStringData(escape.toString());
    }

    @Override
    public void writeBinary(ByteBuffer bin) throws TException {
        // TODO(mcslee): Fix this
        writeString(new String(bin.array(), bin.position() + bin.arrayOffset(),
                bin.limit() - bin.position() - bin.arrayOffset(), StandardCharsets.UTF_8));
    }

    /**
     * Reading methods.
     */

    @Override
    public TMessage readMessageBegin() throws TException {
        // TODO(mcslee): implement
        return EMPTY_MESSAGE;
    }

    @Override
    public void readMessageEnd() throws TException {
    }

    @Override
    public TStruct readStructBegin() throws TException {
        // TODO(mcslee): implement
        return ANONYMOUS_STRUCT;
    }

    @Override
    public void readStructEnd() throws TException {
    }

    @Override
    public TField readFieldBegin() throws TException {
        // TODO(mcslee): implement
        return ANONYMOUS_FIELD;
    }

    @Override
    public void readFieldEnd() throws TException {
    }

    @Override
    public TMap readMapBegin() throws TException {
        // TODO(mcslee): implement
        return EMPTY_MAP;
    }

    @Override
    public void readMapEnd() throws TException {
    }

    @Override
    public TList readListBegin() throws TException {
        // TODO(mcslee): implement
        return EMPTY_LIST;
    }

    @Override
    public void readListEnd() throws TException {
    }

    @Override
    public TSet readSetBegin() throws TException {
        // TODO(mcslee): implement
        return EMPTY_SET;
    }

    @Override
    public void readSetEnd() throws TException {
    }

    @Override
    public boolean readBool() throws TException {
        return (readByte() == 1);
    }

    @Override
    public byte readByte() throws TException {
        // TODO(mcslee): implement
        return 0;
    }

    @Override
    public short readI16() throws TException {
        // TODO(mcslee): implement
        return 0;
    }

    @Override
    public int readI32() throws TException {
        // TODO(mcslee): implement
        return 0;
    }

    @Override
    public long readI64() throws TException {
        // TODO(mcslee): implement
        return 0;
    }

    @Override
    public double readDouble() throws TException {
        // TODO(mcslee): implement
        return 0;
    }

    @Override
    public String readString() throws TException {
        // TODO(mcslee): implement
        return "";
    }

    public String readStringBody(int size) throws TException {
        // TODO(mcslee): implement
        return "";
    }

    @Override
    public ByteBuffer readBinary() throws TException {
        // TODO(mcslee): implement
        return ByteBuffer.wrap(new byte[0]);
    }

    public static class CollectionMapKeyException extends TException {
        public CollectionMapKeyException(String message) {
            super(message);
        }
    }
}