org.apache.thrift.protocol.TJSONProtocolXthrift Maven / Gradle / Ivy
The newest version!
package org.apache.thrift.protocol;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Set;
import java.util.Stack;
import org.apache.thrift.TByteArrayOutputStream;
import org.apache.thrift.TException;
import org.apache.thrift.transport.TTransport;
import com.facebook.swift.codec.ThriftProtocolType;
import com.gitee.l0km.aocache.guava.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.BaseEncoding;
/**
* 复制自 {@link org.apache.thrift.protocol.TJSONProtocol},修改 {@link #readFieldBegin()}方法,
* 解析时支持JSON的Key为字段名,
* 参见 {@link #readJSONStringOrInteger()}
* JSON protocol implementation for thrift.
*
* This is a full-featured protocol supporting write and read.
*
* Please see the C++ class header for a detailed description of the
* protocol's wire format.
* @since 1.3.0
*/
public class TJSONProtocolXthrift extends TProtocol {
/**
* Factory for JSON protocol objects
*/
public static class Factory implements TProtocolFactory {
private static final long serialVersionUID = 7487656103720718481L;
protected boolean fieldNamesAsString_ = false;
/** Write out enum value as a string instead of the default integer value */
protected boolean enumValueAsString_ = false;
/** WITH TYPE MODE IF TRUE*/
protected boolean fieldValueWithType_ = true;
public Factory() {}
public Factory(boolean fieldNamesAsString, boolean enumValueAsString, boolean fieldValueWithType) {
fieldNamesAsString_ = fieldNamesAsString;
enumValueAsString_ = enumValueAsString;
fieldValueWithType_ = fieldValueWithType;
}
public TProtocol getProtocol(TTransport trans) {
return new TJSONProtocolXthrift(trans, fieldNamesAsString_, fieldValueWithType_, enumValueAsString_);
}
}
private static final byte[] COMMA = new byte[] {','};
private static final byte[] COLON = new byte[] {':'};
private static final byte[] LBRACE = new byte[] {'{'};
static final byte[] RBRACE = new byte[] {'}'};
private static final byte[] LBRACKET = new byte[] {'['};
static final byte[] RBRACKET = new byte[] {']'};
private static final byte[] QUOTE = new byte[] {'"'};
private static final byte[] BACKSLASH = new byte[] {'\\'};
private static final byte[] ZERO = new byte[] {'0'};
private static final byte[] ESCSEQ = new byte[] {'\\','u','0','0'};
private static final long VERSION = 1;
private static final byte[] JSON_CHAR_TABLE = {
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
0, 0, 0, 0, 0, 0, 0, 0,'b','t','n', 0,'f','r', 0, 0, // 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
1, 1,'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2
};
private static final String ESCAPE_CHARS = "\"\\/bfnrt";
private static final byte[] ESCAPE_CHAR_VALS = {
'"', '\\', '/', '\b', '\f', '\n', '\r', '\t',
};
private static final int DEF_STRING_SIZE = 16;
private static final byte[] NAME_BOOL = new byte[] {'t', 'f'};
private static final byte[] NAME_BYTE = new byte[] {'i','8'};
private static final byte[] NAME_I16 = new byte[] {'i','1','6'};
private static final byte[] NAME_I32 = new byte[] {'i','3','2'};
private static final byte[] NAME_I64 = new byte[] {'i','6','4'};
private static final byte[] NAME_DOUBLE = new byte[] {'d','b','l'};
private static final byte[] NAME_STRUCT = new byte[] {'r','e','c'};
private static final byte[] NAME_STRING = new byte[] {'s','t','r'};
private static final byte[] NAME_MAP = new byte[] {'m','a','p'};
private static final byte[] NAME_LIST = new byte[] {'l','s','t'};
private static final byte[] NAME_SET = new byte[] {'s','e','t'};
private static final TStruct ANONYMOUS_STRUCT = new TStruct();
private static final byte[] getTypeNameForTypeID(byte typeID)
throws TException {
switch (typeID) {
case TType.BOOL:
return NAME_BOOL;
case TType.BYTE:
return NAME_BYTE;
case TType.I16:
return NAME_I16;
case TType.I32:
return NAME_I32;
case TType.I64:
return NAME_I64;
case TType.DOUBLE:
return NAME_DOUBLE;
case TType.STRING:
return NAME_STRING;
case TType.STRUCT:
return NAME_STRUCT;
case TType.MAP:
return NAME_MAP;
case TType.SET:
return NAME_SET;
case TType.LIST:
return NAME_LIST;
default:
throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
"Unrecognized type");
}
}
private static final byte getTypeIDForTypeName(byte[] name)
throws TException {
byte result = TType.STOP;
if (name.length > 1) {
switch (name[0]) {
case 'd':
result = TType.DOUBLE;
break;
case 'i':
switch (name[1]) {
case '8':
result = TType.BYTE;
break;
case '1':
result = TType.I16;
break;
case '3':
result = TType.I32;
break;
case '6':
result = TType.I64;
break;
}
break;
case 'l':
result = TType.LIST;
break;
case 'm':
result = TType.MAP;
break;
case 'r':
result = TType.STRUCT;
break;
case 's':
if (name[1] == 't') {
result = TType.STRING;
}
else if (name[1] == 'e') {
result = TType.SET;
}
break;
case 't':
result = TType.BOOL;
break;
}
}
if (result == TType.STOP) {
throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
"Unrecognized type");
}
return result;
}
// Base class for tracking JSON contexts that may require inserting/reading
// additional JSON syntax characters
// This base context does nothing.
protected class JSONBaseContext {
protected void write() throws TException {}
protected void read() throws TException {}
protected boolean escapeNum() { return false; }
}
// Context for JSON lists. Will insert/read commas before each item except
// for the first one
protected class JSONListContext extends JSONBaseContext {
private boolean first_ = true;
@Override
protected void write() throws TException {
if (first_) {
first_ = false;
} else {
trans_.write(COMMA);
}
}
@Override
protected void read() throws TException {
if (first_) {
first_ = false;
} else {
readJSONSyntaxChar(COMMA);
}
}
}
// Context for JSON records. Will insert/read colons before the value portion
// of each record pair, and commas before each key except the first. In
// addition, will indicate that numbers in the key position need to be
// escaped in quotes (since JSON keys must be strings).
protected class JSONPairContext extends JSONBaseContext {
private boolean first_ = true;
private boolean colon_ = true;
@Override
protected void write() throws TException {
if (first_) {
first_ = false;
colon_ = true;
} else {
trans_.write(colon_ ? COLON : COMMA);
colon_ = !colon_;
}
}
@Override
protected void read() throws TException {
if (first_) {
first_ = false;
colon_ = true;
} else {
readJSONSyntaxChar(colon_ ? COLON : COMMA);
colon_ = !colon_;
}
}
@Override
protected boolean escapeNum() {
return colon_;
}
}
// Holds up to one byte from the transport
protected class LookaheadReader {
private boolean hasData_;
private byte[] data_ = new byte[1];
// Return and consume the next byte to be read, either taking it from the
// data buffer if present or getting it from the transport otherwise.
protected byte read() throws TException {
if (hasData_) {
hasData_ = false;
}
else {
trans_.readAll(data_, 0, 1);
}
return data_[0];
}
// Return the next byte to be read without consuming, filling the data
// buffer if it has not been filled already.
byte peek() throws TException {
if (!hasData_) {
trans_.readAll(data_, 0, 1);
}
hasData_ = true;
return data_[0];
}
}
// Stack of nested contexts that we may be in
private Stack contextStack_ = new Stack();
// Current context that we are in
private JSONBaseContext context_ = new JSONBaseContext();
// Reader that manages a 1-byte buffer
private LookaheadReader reader_ = new LookaheadReader();
// Write out the TField names as a string instead of the default integer value
private boolean fieldNamesAsString_ = false;
private boolean fieldValueWithType_ = true;
/** Write out enum value as a string instead of the default integer value */
private boolean enumValueAsString_ = false;
/** 当前解析字段对象堆栈 */
private final Stack currenTField = new Stack<>();
// Push a new JSON context onto the stack.
private void pushContext(JSONBaseContext c) {
contextStack_.push(context_);
context_ = c;
}
// Pop the last JSON context off the stack
private void popContext() {
context_ = contextStack_.pop();
}
// Reset the context stack to its initial state
private void resetContext() {
while (!contextStack_.isEmpty()) {
popContext();
}
}
/**
* Constructor
*/
public TJSONProtocolXthrift(TTransport trans) {
super(trans);
}
public TJSONProtocolXthrift(TTransport trans, boolean fieldNamesAsString, boolean fieldValueWithType, boolean enumValueAsString) {
super(trans);
fieldNamesAsString_ = fieldNamesAsString;
fieldValueWithType_ = fieldValueWithType;
enumValueAsString_ = enumValueAsString;
}
@Override
public void reset() {
contextStack_.clear();
context_ = new JSONBaseContext();
reader_ = new LookaheadReader();
}
// Temporary buffer used by several methods
private byte[] tmpbuf_ = new byte[4];
// Read a byte that must match b[0]; otherwise an exception is thrown.
// Marked protected to avoid synthetic accessor in JSONListContext.read
// and JSONPairContext.read
protected void readJSONSyntaxChar(byte[] b) throws TException {
byte ch = reader_.read();
if (ch != b[0]) {
throw new TProtocolException(TProtocolException.INVALID_DATA,
"Unexpected character:" + (char)ch);
}
}
// Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its
// corresponding hex value
private static final byte hexVal(byte ch) throws TException {
if ((ch >= '0') && (ch <= '9')) {
return (byte)((char)ch - '0');
}
else if ((ch >= 'a') && (ch <= 'f')) {
return (byte)((char)ch - 'a' + 10);
}
else {
throw new TProtocolException(TProtocolException.INVALID_DATA,
"Expected hex character");
}
}
// Convert a byte containing a hex value to its corresponding hex character
private static final byte hexChar(byte val) {
val &= 0x0F;
if (val < 10) {
return (byte)((char)val + '0');
}
else {
return (byte)((char)(val - 10) + 'a');
}
}
// Write the bytes in array buf as a JSON characters, escaping as needed
private void writeJSONString(byte[] b) throws TException {
context_.write();
trans_.write(QUOTE);
int len = b.length;
for (int i = 0; i < len; i++) {
if ((b[i] & 0x00FF) >= 0x30) {
if (b[i] == BACKSLASH[0]) {
trans_.write(BACKSLASH);
trans_.write(BACKSLASH);
}
else {
trans_.write(b, i, 1);
}
}
else {
tmpbuf_[0] = JSON_CHAR_TABLE[b[i]];
if (tmpbuf_[0] == 1) {
trans_.write(b, i, 1);
}
else if (tmpbuf_[0] > 1) {
trans_.write(BACKSLASH);
trans_.write(tmpbuf_, 0, 1);
}
else {
trans_.write(ESCSEQ);
tmpbuf_[0] = hexChar((byte)(b[i] >> 4));
tmpbuf_[1] = hexChar(b[i]);
trans_.write(tmpbuf_, 0, 2);
}
}
}
trans_.write(QUOTE);
}
// Write out number as a JSON value. If the context dictates so, it will be
// wrapped in quotes to output as a JSON string.
private void writeJSONInteger(long num) throws TException {
context_.write();
String str = Long.toString(num);
boolean escapeNum = context_.escapeNum();
if (escapeNum && !fieldNamesAsString_) {
trans_.write(QUOTE);
}
try {
byte[] buf = str.getBytes("UTF-8");
trans_.write(buf);
} catch (UnsupportedEncodingException uex) {
throw new TException("JVM DOES NOT SUPPORT UTF-8");
}
if (escapeNum && !fieldNamesAsString_) {
trans_.write(QUOTE);
}
}
// Write out a double as a JSON value. If it is NaN or infinity or if the
// context dictates escaping, write out as JSON string.
private void writeJSONDouble(double num) throws TException {
context_.write();
String str = Double.toString(num);
boolean special = false;
switch (str.charAt(0)) {
case 'N': // NaN
case 'I': // Infinity
special = true;
break;
case '-':
if (str.charAt(1) == 'I') { // -Infinity
special = true;
}
break;
default:
break;
}
boolean escapeNum = special || (context_.escapeNum() && !fieldNamesAsString_);
if (escapeNum) {
trans_.write(QUOTE);
}
try {
byte[] b = str.getBytes("UTF-8");
trans_.write(b, 0, b.length);
} catch (UnsupportedEncodingException uex) {
throw new TException("JVM DOES NOT SUPPORT UTF-8");
}
if (escapeNum) {
trans_.write(QUOTE);
}
}
// Write out contents of byte array b as a JSON string with base-64 encoded
// data
private void writeJSONBase64(byte[] b, int offset, int length) throws TException {
String s = BaseEncoding.base64().encode(b, offset, length);
writeString(s);
}
private void writeJSONObjectStart() throws TException {
context_.write();
trans_.write(LBRACE);
pushContext(new JSONPairContext());
}
private void writeJSONObjectEnd() throws TException {
popContext();
trans_.write(RBRACE);
}
private void writeJSONArrayStart() throws TException {
context_.write();
trans_.write(LBRACKET);
pushContext(new JSONListContext());
}
private void writeJSONArrayEnd() throws TException {
popContext();
trans_.write(RBRACKET);
}
@Override
public void writeMessageBegin(TMessage message) throws TException {
resetContext(); // THRIFT-3743
writeJSONArrayStart();
writeJSONInteger(VERSION);
try {
byte[] b = message.name.getBytes("UTF-8");
writeJSONString(b);
} catch (UnsupportedEncodingException uex) {
throw new TException("JVM DOES NOT SUPPORT UTF-8");
}
writeJSONInteger(message.type);
writeJSONInteger(message.seqid);
}
@Override
public void writeMessageEnd() throws TException {
writeJSONArrayEnd();
}
@Override
public void writeStructBegin(TStruct struct) throws TException {
writeJSONObjectStart();
}
@Override
public void writeStructEnd() throws TException {
writeJSONObjectEnd();
}
@Override
public void writeFieldBegin(TField field) throws TException {
if (fieldNamesAsString_) {
writeString(field.name);
} else {
writeJSONInteger(field.id);
}
if(fieldValueWithType_) {
writeJSONObjectStart();
writeJSONString(getTypeNameForTypeID(field.type));
}
}
@Override
public void writeFieldEnd() throws TException {
if(fieldValueWithType_) {
writeJSONObjectEnd();
}
}
@Override
public void writeFieldStop() {}
@Override
public void writeMapBegin(TMap map) throws TException {
if(fieldValueWithType_) {
writeJSONArrayStart();
writeJSONString(getTypeNameForTypeID(map.keyType));
writeJSONString(getTypeNameForTypeID(map.valueType));
writeJSONInteger(map.size);
}
writeJSONObjectStart();
}
@Override
public void writeMapEnd() throws TException {
writeJSONObjectEnd();
if(fieldValueWithType_) {
writeJSONArrayEnd();
}
}
@Override
public void writeListBegin(TList list) throws TException {
writeJSONArrayStart();
if(fieldValueWithType_) {
writeJSONString(getTypeNameForTypeID(list.elemType));
writeJSONInteger(list.size);
}
}
@Override
public void writeListEnd() throws TException {
writeJSONArrayEnd();
}
@Override
public void writeSetBegin(TSet set) throws TException {
writeJSONArrayStart();
if(fieldValueWithType_) {
writeJSONString(getTypeNameForTypeID(set.elemType));
writeJSONInteger(set.size);
}
}
@Override
public void writeSetEnd() throws TException {
writeJSONArrayEnd();
}
@Override
public void writeBool(boolean b) throws TException {
context_.write();
String str = Boolean.toString(b);
try {
byte[] buf = str.getBytes("UTF-8");
trans_.write(buf);
} catch (UnsupportedEncodingException uex) {
throw new TException("JVM DOES NOT SUPPORT UTF-8");
}
}
@Override
public void writeByte(byte b) throws TException {
writeJSONInteger((long)b);
}
@Override
public void writeI16(short i16) throws TException {
writeJSONInteger((long)i16);
}
@Override
public void writeI32(int i32) throws TException {
writeJSONInteger((long)i32);
}
@Override
public void writeI64(long i64) throws TException {
writeJSONInteger(i64);
}
@Override
public void writeDouble(double dub) throws TException {
writeJSONDouble(dub);
}
@Override
public void writeString(String str) throws TException {
try {
byte[] b = str.getBytes("UTF-8");
writeJSONString(b);
} catch (UnsupportedEncodingException uex) {
throw new TException("JVM DOES NOT SUPPORT UTF-8");
}
}
@Override
public void writeBinary(ByteBuffer bin) throws TException {
writeJSONBase64(bin.array(), bin.position() + bin.arrayOffset(), bin.limit() - bin.position() - bin.arrayOffset());
}
/**
* Reading methods.
*/
// Read in a JSON string, unescaping as appropriate.. Skip reading from the
// context if skipContext is true.
private TByteArrayOutputStream readJSONString(boolean skipContext)
throws TException {
TByteArrayOutputStream arr = new TByteArrayOutputStream(DEF_STRING_SIZE);
ArrayList codeunits = new ArrayList();
if (!skipContext) {
context_.read();
}
readJSONSyntaxChar(QUOTE);
while (true) {
byte ch = reader_.read();
if (ch == QUOTE[0]) {
break;
}
if (ch == ESCSEQ[0]) {
ch = reader_.read();
if (ch == ESCSEQ[1]) {
trans_.readAll(tmpbuf_, 0, 4);
short cu = (short)(
((short)hexVal(tmpbuf_[0]) << 12) +
((short)hexVal(tmpbuf_[1]) << 8) +
((short)hexVal(tmpbuf_[2]) << 4) +
(short)hexVal(tmpbuf_[3]));
try {
if (Character.isHighSurrogate((char)cu)) {
if (codeunits.size() > 0) {
throw new TProtocolException(TProtocolException.INVALID_DATA,
"Expected low surrogate char");
}
codeunits.add((char)cu);
}
else if (Character.isLowSurrogate((char)cu)) {
if (codeunits.size() == 0) {
throw new TProtocolException(TProtocolException.INVALID_DATA,
"Expected high surrogate char");
}
codeunits.add((char)cu);
arr.write((new String(new int[] { codeunits.get(0), codeunits.get(1) }, 0, 2)).getBytes("UTF-8"));
codeunits.clear();
}
else {
arr.write((new String(new int[] { cu }, 0, 1)).getBytes("UTF-8"));
}
continue;
}
catch (UnsupportedEncodingException ex) {
throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
"JVM does not support UTF-8");
}
catch (IOException ex) {
throw new TProtocolException(TProtocolException.INVALID_DATA,
"Invalid unicode sequence");
}
}
else {
int off = ESCAPE_CHARS.indexOf(ch);
if (off == -1) {
throw new TProtocolException(TProtocolException.INVALID_DATA,
"Expected control char");
}
ch = ESCAPE_CHAR_VALS[off];
}
}
arr.write(ch);
}
return arr;
}
// Return true if the given byte could be a valid part of a JSON number.
private boolean isJSONNumeric(byte b) {
switch (b) {
case '+':
case '-':
case '.':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case 'E':
case 'e':
return true;
}
return false;
}
// Read in a sequence of characters that are all valid in JSON numbers. Does
// not do a complete regex check to validate that this is actually a number.
private String readJSONNumericChars() throws TException {
StringBuilder strbld = new StringBuilder();
while (true) {
byte ch = reader_.peek();
if (!isJSONNumeric(ch)) {
break;
}
strbld.append((char)reader_.read());
}
return strbld.toString();
}
/**
* 读取单词{@code [A-Za-z0-9_]}
* @throws TException
*/
private String readJSONWordChars() throws TException {
StringBuilder strbld = new StringBuilder();
while (true) {
byte ch = reader_.peek();
if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_') {
strbld.append((char)reader_.read());
}else {
break;
}
}
return strbld.toString();
}
// Read in a JSON number. If the context dictates, read in enclosing quotes.
private long readJSONInteger() throws TException {
context_.read();
if (context_.escapeNum() && reader_.peek() == QUOTE[0]) {
readJSONSyntaxChar(QUOTE);
}
String str = readJSONNumericChars();
if (context_.escapeNum() && reader_.peek() == QUOTE[0]) {
readJSONSyntaxChar(QUOTE);
}
try {
return Long.valueOf(str);
}
catch (NumberFormatException ex) {
throw new TProtocolException(TProtocolException.INVALID_DATA,
"Bad data encounted in numeric data");
}
}
// Read in a JSON double value. Throw if the value is not wrapped in quotes
// when expected or if wrapped in quotes when not expected.
private double readJSONDouble() throws TException {
context_.read();
if (reader_.peek() == QUOTE[0]) {
TByteArrayOutputStream arr = readJSONString(true);
try {
double dub = Double.valueOf(arr.toString("UTF-8"));
if (!context_.escapeNum() && !Double.isNaN(dub) &&
!Double.isInfinite(dub)) {
// Throw exception -- we should not be in a string in this case
throw new TProtocolException(TProtocolException.INVALID_DATA,
"Numeric data unexpectedly quoted");
}
return dub;
}
catch (UnsupportedEncodingException ex) {
throw new TException("JVM DOES NOT SUPPORT UTF-8");
}
}
else {
if (context_.escapeNum() && reader_.peek() == QUOTE[0]) {
// This will throw - we should have had a quote if escapeNum == true
readJSONSyntaxChar(QUOTE);
}
String str = readJSONNumericChars();
if (context_.escapeNum() && reader_.peek() == QUOTE[0]) {
readJSONSyntaxChar(QUOTE);
}
try {
return Double.valueOf(str);
}
catch (NumberFormatException ex) {
throw new TProtocolException(TProtocolException.INVALID_DATA,
"Bad data encounted in numeric data");
}
}
}
// Read in a JSON string containing base-64 encoded data and decode it.
private byte[] readJSONBase64() throws TException {
TByteArrayOutputStream arr = readJSONString(false);
byte[] b = arr.get();
int len = arr.len();
int off = 0;
int size = 0;
// Ignore padding
int bound = len >= 2 ? len - 2 : 0;
for (int i = len - 1; i >= bound && b[i] == '='; --i) {
--len;
}
while (len >= 4) {
// Decode 4 bytes at a time
TBase64Utils.decode(b, off, 4, b, size); // NB: decoded in place
off += 4;
len -= 4;
size += 3;
}
// Don't decode if we hit the end or got a single leftover byte (invalid
// base64 but legal for skip of regular string type)
if (len > 1) {
// Decode remainder
TBase64Utils.decode(b, off, len, b, size); // NB: decoded in place
size += len - 1;
}
// Sadly we must copy the byte[] (any way around this?)
byte [] result = new byte[size];
System.arraycopy(b, 0, result, 0, size);
return result;
}
private void readJSONObjectStart() throws TException {
context_.read();
readJSONSyntaxChar(LBRACE);
pushContext(new JSONPairContext());
}
private void readJSONObjectEnd() throws TException {
readJSONSyntaxChar(RBRACE);
popContext();
}
private void readJSONArrayStart() throws TException {
context_.read();
readJSONSyntaxChar(LBRACKET);
pushContext(new JSONListContext());
}
private void readJSONArrayEnd() throws TException {
readJSONSyntaxChar(RBRACKET);
popContext();
}
/**
* 读取字段名(String)或字段ID(Integer)
* @throws TException
*/
public Object readJSONStringOrInteger() throws TException {
context_.read();
byte ch = reader_.peek();
if(ch != QUOTE[0]) {
return readJSONInteger();
}
String str;
try {
str = readJSONString(true).toString("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new TException("JVM DOES NOT SUPPORT UTF-8");
}
try {
return Long.valueOf(str);
}
catch (NumberFormatException ex) {
return str;
}
}
private static ImmutableMap TYPES = ImmutableMap.builder().put((byte) '{', TType.STRUCT)
.put((byte) '[', TType.LIST).put((byte) '"', TType.STRING).put((byte) 't', TType.BOOL)
.put((byte) 'f', TType.BOOL).put((byte) '+', TType.DOUBLE).put((byte) '-', TType.DOUBLE)
.put((byte) '0', TType.DOUBLE).put((byte) '1', TType.DOUBLE).put((byte) '2', TType.DOUBLE)
.put((byte) '3', TType.DOUBLE).put((byte) '4', TType.DOUBLE).put((byte) '5', TType.DOUBLE)
.put((byte) '6', TType.DOUBLE).put((byte) '7', TType.DOUBLE).put((byte) '8', TType.DOUBLE)
.put((byte) '9', TType.DOUBLE).build();
/** 目标类型 -- 可匹配的猜测类型的映射 */
private static ImmutableMap> PROTOCOL_TYPE_MATCHED_GUESS =
ImmutableMap.>builder()
.put(ThriftProtocolType.BOOL, ImmutableSet.of(TType.BOOL,TType.DOUBLE))
.put(ThriftProtocolType.BYTE, ImmutableSet.of(TType.BYTE,TType.DOUBLE))
.put(ThriftProtocolType.DOUBLE, ImmutableSet.of(TType.DOUBLE))
.put(ThriftProtocolType.I16, ImmutableSet.of(TType.DOUBLE))
.put(ThriftProtocolType.I32, ImmutableSet.of(TType.DOUBLE))
.put(ThriftProtocolType.I64, ImmutableSet.of(TType.DOUBLE))
.put(ThriftProtocolType.STRING, ImmutableSet.of(TType.STRING))
.put(ThriftProtocolType.STRUCT, ImmutableSet.of(TType.STRUCT))
.put(ThriftProtocolType.MAP, ImmutableSet.of(TType.STRUCT))
.put(ThriftProtocolType.SET, ImmutableSet.of(TType.LIST))
.put(ThriftProtocolType.LIST, ImmutableSet.of(TType.LIST))
.put(ThriftProtocolType.ENUM, ImmutableSet.of(TType.DOUBLE,TType.STRING))
.put(ThriftProtocolType.BINARY, ImmutableSet.of(TType.STRING))
.build();
/**
* 根据输入字段判断当前数据类型{@link TType}
*
* @param ch
*/
private static byte guessType(byte ch) {
Byte v = TYPES.get(ch);
return null == v ? (byte) -1 : v;
}
/**
* 判断当前猜测的类型是否匹配希望的类型{@code expectedType}
* @param expectedType
* @param guessType
* @throws TException
*/
private static boolean matchGuessType(ThriftProtocolType expectedType,byte guessType) throws TException {
if (guessType != -1) {
Set mayMatcheType = PROTOCOL_TYPE_MATCHED_GUESS.get(expectedType);
if (mayMatcheType != null) {
return mayMatcheType.contains(guessType);
}
}
return false;
}
/**
* 判断当前猜测的类型是否匹配希望的类型{@code protocolType}
* @param protocolType
* @throws TException
* @since 1.5.0
*/
public boolean matchedCurrentType(ThriftProtocolType protocolType) throws TException {
return matchGuessType(protocolType,guessType(reader_.peek()));
}
/**
* Skips over the next data element from the provided input TProtocol object.
*
* @param type the next value will be interpreted as this TType value.
*/
public void skip(byte type) throws TException {
skip(type, Integer.MAX_VALUE);
}
/**
* 在{@link org.apache.thrift.protocol.TProtocolUtil#skip(TProtocol, byte, int) }基础
* 上实现支持无类型定义的标准JSON字段的跳过动作,根据当前字符猜测当前字段类型, 参见 {@link #guessType(byte)}
* Skips over the next data element from the provided input TProtocol object.
*
* @param type the next value will be interpreted as this TType value.
* @param maxDepth this function will only skip complex objects to this
* recursive depth, to prevent Java stack overflow.
*/
private void skip(byte type, int maxDepth) throws TException {
if (maxDepth <= 0) {
throw new TException("Maximum skip depth exceeded");
}
if (type == -1) {
type = guessType(reader_.peek());
}
switch (type) {
case TType.BOOL:
readBool();
break;
case TType.BYTE:
readByte();
break;
case TType.I16:
readI16();
break;
case TType.I32:
readI32();
break;
case TType.I64:
readI64();
break;
case TType.DOUBLE:
readDouble();
break;
case TType.STRING:
readBinary();
break;
case TType.STRUCT:
readStructBegin();
while (true) {
TField field = readFieldBegin();
if (field.type == TType.STOP) {
break;
}
skip(field.type, maxDepth - 1);
readFieldEnd();
}
readStructEnd();
break;
case TType.MAP:
TMap map = readMapBegin();
if (map.size >= 0) {
for (int i = 0; i < map.size; i++) {
skip(map.keyType, maxDepth - 1);
skip(map.valueType, maxDepth - 1);
}
}else {
while (reader_.peek() != RBRACE[0]) {
skip(map.keyType, maxDepth - 1);
skip(map.valueType, maxDepth - 1);
}
}
readMapEnd();
break;
case TType.SET:
TSet set = readSetBegin();
if (set.size >= 0) {
for (int i = 0; i < set.size; i++) {
skip(set.elemType, maxDepth - 1);
}
}else {
while (reader_.peek() != RBRACKET[0]) {
skip(set.elemType, maxDepth - 1);
}
}
readSetEnd();
break;
case TType.LIST:
TList list = readListBegin();
if (list.size >= 0) {
for (int i = 0; i < list.size; i++) {
skip(list.elemType, maxDepth - 1);
}
}else {
while (reader_.peek() != RBRACKET[0]) {
skip(list.elemType, maxDepth - 1);
}
}
readListEnd();
break;
default:
throw new TProtocolException(TProtocolException.INVALID_DATA, "Unrecognized type " + type);
}
}
public byte peek() throws TException {
return reader_.peek();
}
public boolean enumValueAsString() {
return enumValueAsString_;
}
@Override
public TMessage readMessageBegin() throws TException {
resetContext(); // THRIFT-3743
readJSONArrayStart();
if (readJSONInteger() != VERSION) {
throw new TProtocolException(TProtocolException.BAD_VERSION,
"Message contained bad version.");
}
String name;
try {
name = readJSONString(false).toString("UTF-8");
}
catch (UnsupportedEncodingException ex) {
throw new TException("JVM DOES NOT SUPPORT UTF-8");
}
byte type = (byte) readJSONInteger();
int seqid = (int) readJSONInteger();
return new TMessage(name, type, seqid);
}
@Override
public void readMessageEnd() throws TException {
readJSONArrayEnd();
}
@Override
public TStruct readStructBegin() throws TException {
readJSONObjectStart();
return ANONYMOUS_STRUCT;
}
@Override
public void readStructEnd() throws TException {
readJSONObjectEnd();
}
@Override
public TField readFieldBegin() throws TException {
byte ch = reader_.peek();
byte type;
short id = 0;
String name = "";
if (ch == RBRACE[0]) {
type = TType.STOP;
}
else {
Object v = readJSONStringOrInteger();
if(v instanceof Number) {
id = ((Number)v).shortValue();
} else {
name = (String) v;
}
if(name.isEmpty()) {
/** WITH TYPE MODE*/
readJSONObjectStart();
type = getTypeIDForTypeName(readJSONString(false).get());
}else {
context_.read();
pushContext(new JSONPairContext());
/** 未知类型,假设与字段类型一致 */
type = -1;
}
}
return currenTField.push(new TField(name, type, id));
}
@Override
public void readFieldEnd() throws TException {
TField _currenTField = currenTField.pop();
if(!_currenTField.name.isEmpty() || _currenTField.type ==TType.STOP) {
popContext();
}else {
/** WITH TYPE MODE*/
/** 参见 readJSONFieldValueStart 逻辑 */
readJSONObjectEnd();
}
}
@Override
public TMap readMapBegin() throws TException {
if (reader_.peek() == LBRACKET[0]) {
readJSONArrayStart();
byte keyType = getTypeIDForTypeName(readJSONString(false).get());
byte valueType = getTypeIDForTypeName(readJSONString(false).get());
int size = (int)readJSONInteger();
readJSONObjectStart();
return new TMap(keyType, valueType, size);
}else {
readJSONObjectStart();
return new TMap((byte)-1,(byte) -1, 0);
}
}
@Override
public void readMapEnd() throws TException {
readJSONObjectEnd();
if (reader_.peek() == RBRACKET[0]) {
readJSONArrayEnd();
}
}
@Override
public TList readListBegin() throws TException {
readJSONArrayStart();
byte elemType;
int size;
byte ch = reader_.peek();
if (ch == QUOTE[0]) {
/** WITH TYPE MODE*/
elemType = getTypeIDForTypeName(readJSONString(false).get());
size = (int)readJSONInteger();
}else {
elemType = guessType(ch);
size = -1;
}
return new TList(elemType, size);
}
@Override
public void readListEnd() throws TException {
readJSONArrayEnd();
}
@Override
public TSet readSetBegin() throws TException {
readJSONArrayStart();
byte elemType = getTypeIDForTypeName(readJSONString(false).get());
int size = (int)readJSONInteger();
return new TSet(elemType, size);
}
@Override
public void readSetEnd() throws TException {
readJSONArrayEnd();
}
@Override
public boolean readBool() throws TException {
context_.read();
if (context_.escapeNum() && reader_.peek() == QUOTE[0]) {
readJSONSyntaxChar(QUOTE);
}
String str = readJSONWordChars();
if (context_.escapeNum() && reader_.peek() == QUOTE[0]) {
readJSONSyntaxChar(QUOTE);
}
try {
Long num = Long.valueOf(str);
return (num == 0 ? false : true);
}
catch (NumberFormatException ex) {
String lower = str.toLowerCase();
if("true".equals(lower)) {
return true;
}else if("false".equals(lower)) {
return false;
}
throw new TException("INVALID bool value:"+str);
}
}
@Override
public byte readByte() throws TException {
return (byte) readJSONInteger();
}
@Override
public short readI16() throws TException {
return (short) readJSONInteger();
}
@Override
public int readI32() throws TException {
return (int) readJSONInteger();
}
@Override
public long readI64() throws TException {
return (long) readJSONInteger();
}
@Override
public double readDouble() throws TException {
return readJSONDouble();
}
@Override
public String readString() throws TException {
try {
return readJSONString(false).toString("UTF-8");
}
catch (UnsupportedEncodingException ex) {
throw new TException("JVM DOES NOT SUPPORT UTF-8");
}
}
@Override
public ByteBuffer readBinary() throws TException {
return ByteBuffer.wrap(readJSONBase64());
}
}