blob: e20ff33c50a9c73db370484aea809fa8804358ca [file] [log] [blame]
/*
* 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 haxe.io.Bytes;
import haxe.io.BytesInput;
import haxe.io.BytesOutput;
import haxe.io.BytesBuffer;
import haxe.ds.GenericStack;
import haxe.Utf8;
import haxe.crypto.Base64;
import haxe.Int64;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TMessage;
import org.apache.thrift.protocol.TField;
import org.apache.thrift.protocol.TMap;
import org.apache.thrift.protocol.TSet;
import org.apache.thrift.protocol.TList;
import org.apache.thrift.transport.TTransport;
/* 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 wire format.
*
* Adapted from the Java version.
*/
class TJSONProtocol extends TRecursionTracker implements TProtocol {
public var trans(default,null) : TTransport;
// Stack of nested contexts that we may be in
private var contextStack : GenericStack<JSONBaseContext> = new GenericStack<JSONBaseContext>();
// Current context that we are in
private var context : JSONBaseContext;
// Reader that manages a 1-byte buffer
private var reader : LookaheadReader;
// whether the underlying system holds Strings as UTF-8
// http://old.haxe.org/manual/encoding
private static var utf8Strings = haxe.Utf8.validate("Ç-ß-Æ-Ю-Ш");
// TJSONProtocol Constructor
public function new( trans : TTransport)
{
this.trans = trans;
this.context = new JSONBaseContext(this);
this.reader = new LookaheadReader(this);
}
public function getTransport() : TTransport {
return trans;
}
public function writeMessageBegin(message:TMessage) : Void {
WriteJSONArrayStart();
WriteJSONInteger( JSONConstants.VERSION);
WriteJSONString( BytesFromString(message.name));
WriteJSONInteger( message.type);
WriteJSONInteger( message.seqid);
}
public function writeMessageEnd() : Void {
WriteJSONArrayEnd();
}
public function writeStructBegin(struct:TStruct) : Void {
WriteJSONObjectStart();
}
public function writeStructEnd() : Void {
WriteJSONObjectEnd();
}
public function writeFieldBegin(field:TField) : Void {
WriteJSONInteger( field.id );
WriteJSONObjectStart();
WriteJSONString( BytesFromString( JSONConstants.GetTypeNameForTypeID( field.type)));
}
public function writeFieldEnd() : Void {
WriteJSONObjectEnd();
}
public function writeFieldStop() : Void { }
public function writeMapBegin(map:TMap) : Void {
WriteJSONArrayStart();
WriteJSONString( BytesFromString( JSONConstants.GetTypeNameForTypeID( map.keyType)));
WriteJSONString( BytesFromString( JSONConstants.GetTypeNameForTypeID( map.valueType)));
WriteJSONInteger( map.size);
WriteJSONObjectStart();
}
public function writeMapEnd() : Void {
WriteJSONObjectEnd();
WriteJSONArrayEnd();
}
public function writeListBegin(list:TList) : Void {
WriteJSONArrayStart();
WriteJSONString( BytesFromString( JSONConstants.GetTypeNameForTypeID( list.elemType )));
WriteJSONInteger( list.size);
}
public function writeListEnd() : Void {
WriteJSONArrayEnd();
}
public function writeSetBegin(set:TSet) : Void {
WriteJSONArrayStart();
WriteJSONString( BytesFromString( JSONConstants.GetTypeNameForTypeID( set.elemType)));
WriteJSONInteger( set.size);
}
public function writeSetEnd() : Void {
WriteJSONArrayEnd();
}
public function writeBool(b : Bool) : Void {
if( b)
WriteJSONInteger( 1);
else
WriteJSONInteger( 0);
}
public function writeByte(b : Int) : Void {
WriteJSONInteger( b);
}
public function writeI16(i16 : Int) : Void {
WriteJSONInteger( i16);
}
public function writeI32(i32 : Int) : Void {
WriteJSONInteger( i32);
}
public function writeI64(i64 : haxe.Int64) : Void {
WriteJSONInt64( i64);
}
public function writeDouble(dub:Float) : Void {
WriteJSONDouble(dub);
}
public function writeString(str : String) : Void {
WriteJSONString( BytesFromString(str));
}
public function writeBinary(bin:Bytes) : Void {
WriteJSONBase64(bin);
}
public function readMessageBegin():TMessage {
var message : TMessage = new TMessage();
ReadJSONArrayStart();
if (ReadJSONInteger() != JSONConstants.VERSION)
{
throw new TProtocolException(TProtocolException.BAD_VERSION,
"Message contained bad version.");
}
message.name = ReadJSONString(false);
message.type = ReadJSONInteger();
message.seqid = ReadJSONInteger();
return message;
}
public function readMessageEnd() : Void {
ReadJSONArrayEnd();
}
public function readStructBegin():TStruct {
ReadJSONObjectStart();
return new TStruct();
}
public function readStructEnd() : Void {
ReadJSONObjectEnd();
}
public function readFieldBegin() : TField {
var field : TField = new TField();
var ch = reader.Peek();
if (StringFromBytes(ch) == JSONConstants.RBRACE)
{
field.type = TType.STOP;
}
else
{
field.id = ReadJSONInteger();
ReadJSONObjectStart();
field.type = JSONConstants.GetTypeIDForTypeName( ReadJSONString(false));
}
return field;
}
public function readFieldEnd() : Void {
ReadJSONObjectEnd();
}
public function readMapBegin() : TMap {
ReadJSONArrayStart();
var KeyType = JSONConstants.GetTypeIDForTypeName( ReadJSONString(false));
var ValueType = JSONConstants.GetTypeIDForTypeName( ReadJSONString(false));
var Count : Int = ReadJSONInteger();
ReadJSONObjectStart();
var map = new TMap( KeyType, ValueType, Count);
return map;
}
public function readMapEnd() : Void {
ReadJSONObjectEnd();
ReadJSONArrayEnd();
}
public function readListBegin():TList {
ReadJSONArrayStart();
var ElementType = JSONConstants.GetTypeIDForTypeName( ReadJSONString(false));
var Count : Int = ReadJSONInteger();
var list = new TList( ElementType, Count);
return list;
}
public function readListEnd() : Void {
ReadJSONArrayEnd();
}
public function readSetBegin() : TSet {
ReadJSONArrayStart();
var ElementType = JSONConstants.GetTypeIDForTypeName( ReadJSONString(false));
var Count : Int = ReadJSONInteger();
var set = new TSet( ElementType, Count);
return set;
}
public function readSetEnd() : Void {
ReadJSONArrayEnd();
}
public function readBool() : Bool {
return (ReadJSONInteger() != 0);
}
public function readByte() : Int {
return ReadJSONInteger();
}
public function readI16() : Int {
return ReadJSONInteger();
}
public function readI32() : Int {
return ReadJSONInteger();
}
public function readI64() : haxe.Int64 {
return ReadJSONInt64();
}
public function readDouble():Float {
return ReadJSONDouble();
}
public function readString() : String {
return ReadJSONString(false);
}
public function readBinary() : Bytes {
return ReadJSONBase64();
}
// Push a new JSON context onto the stack.
private function PushContext(c : JSONBaseContext) : Void {
contextStack.add(context);
context = c;
}
// Pop the last JSON context off the stack
private function PopContext() : Void {
context = contextStack.pop();
}
// Write the bytes in array buf as a JSON characters, escaping as needed
private function WriteJSONString( b : Bytes) : Void {
context.Write();
var tmp = BytesFromString( JSONConstants.QUOTE);
trans.write( tmp, 0, tmp.length);
for (i in 0 ... b.length) {
var value = b.get(i);
if ((value & 0x00FF) >= 0x30)
{
if (String.fromCharCode(value) == JSONConstants.BACKSLASH.charAt(0))
{
tmp = BytesFromString( JSONConstants.BACKSLASH + JSONConstants.BACKSLASH);
trans.write( tmp, 0, tmp.length);
}
else
{
trans.write( b, i, 1);
}
}
else
{
var num = JSONConstants.JSON_CHAR_TABLE[value];
if (num == 1)
{
trans.write( b, i, 1);
}
else if (num > 1)
{
var buf = new BytesBuffer();
buf.addString( JSONConstants.BACKSLASH);
buf.addByte( num);
tmp = buf.getBytes();
trans.write( tmp, 0, tmp.length);
}
else
{
var buf = new BytesBuffer();
buf.addString( JSONConstants.ESCSEQ);
buf.addString( HexChar( (value & 0xFF000000) >> 12));
buf.addString( HexChar( (value & 0x00FF0000) >> 8));
buf.addString( HexChar( (value & 0x0000FF00) >> 4));
buf.addString( HexChar( value & 0x000000FF));
tmp = buf.getBytes();
trans.write( tmp, 0, tmp.length);
}
}
}
tmp = BytesFromString( JSONConstants.QUOTE);
trans.write( tmp, 0, tmp.length);
}
// 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 function WriteJSONInteger( num : Int) : Void {
context.Write();
var str : String = "";
var escapeNum : Bool = context.EscapeNumbers();
if (escapeNum) {
str += JSONConstants.QUOTE;
}
str += Std.string(num);
if (escapeNum) {
str += JSONConstants.QUOTE;
}
var tmp = BytesFromString( str);
trans.write( tmp, 0, tmp.length);
}
// 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 function WriteJSONInt64( num : Int64) : Void {
context.Write();
var str : String = "";
var escapeNum : Bool = context.EscapeNumbers();
if (escapeNum) {
str += JSONConstants.QUOTE;
}
str += Std.string(num);
if (escapeNum) {
str += JSONConstants.QUOTE;
}
var tmp = BytesFromString( str);
trans.write( tmp, 0, tmp.length);
}
// 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 function WriteJSONDouble(num : Float) : Void {
context.Write();
var special : Bool = false;
var rendered : String = "";
if( Math.isNaN(num)) {
special = true;
rendered = JSONConstants.FLOAT_IS_NAN;
} else if (! Math.isFinite(num)) {
special = true;
if( num > 0) {
rendered = JSONConstants.FLOAT_IS_POS_INF;
} else {
rendered = JSONConstants.FLOAT_IS_NEG_INF;
}
} else {
rendered = Std.string(num); // plain and simple float number
}
// compose output
var escapeNum : Bool = special || context.EscapeNumbers();
var str : String = "";
if (escapeNum) {
str += JSONConstants.QUOTE;
}
str += rendered;
if (escapeNum) {
str += JSONConstants.QUOTE;
}
var tmp = BytesFromString( str);
trans.write( tmp, 0, tmp.length);
}
// Write out contents of byte array b as a JSON string with base-64 encoded data
private function WriteJSONBase64( b : Bytes) : Void {
context.Write();
var buf = new BytesBuffer();
buf.addString( JSONConstants.QUOTE);
buf.addString( Base64.encode(b));
buf.addString( JSONConstants.QUOTE);
var tmp = buf.getBytes();
trans.write( tmp, 0, tmp.length);
}
private function WriteJSONObjectStart() : Void {
context.Write();
var tmp = BytesFromString( JSONConstants.LBRACE);
trans.write( tmp, 0, tmp.length);
PushContext( new JSONPairContext(this));
}
private function WriteJSONObjectEnd() : Void {
PopContext();
var tmp = BytesFromString( JSONConstants.RBRACE);
trans.write( tmp, 0, tmp.length);
}
private function WriteJSONArrayStart() : Void {
context.Write();
var tmp = BytesFromString( JSONConstants.LBRACKET);
trans.write( tmp, 0, tmp.length);
PushContext( new JSONListContext(this));
}
private function WriteJSONArrayEnd() : Void {
PopContext();
var tmp = BytesFromString( JSONConstants.RBRACKET);
trans.write( tmp, 0, tmp.length);
}
/**
* Reading methods.
*/
// Read a byte that must match char, otherwise an exception is thrown.
public function ReadJSONSyntaxChar( char : String) : Void {
var b = BytesFromString( char);
var ch = reader.Read();
if (ch.get(0) != b.get(0))
{
throw new TProtocolException(TProtocolException.INVALID_DATA,
'Unexpected character: $ch');
}
}
// Read in a JSON string, unescaping as appropriate.
// Skip Reading from the context if skipContext is true.
private function ReadJSONString(skipContext : Bool) : String
{
if (!skipContext)
{
context.Read();
}
var buffer : BytesBuffer = new BytesBuffer();
ReadJSONSyntaxChar( JSONConstants.QUOTE);
while (true)
{
var ch = reader.Read();
// end of string?
if (StringFromBytes(ch) == JSONConstants.QUOTE)
{
break;
}
// escaped?
if (StringFromBytes(ch) != JSONConstants.ESCSEQ.charAt(0))
{
buffer.addByte( ch.get(0));
continue;
}
// distinguish between \uXXXX (hex unicode) and \X (control chars)
ch = reader.Read();
if (StringFromBytes(ch) != JSONConstants.ESCSEQ.charAt(1))
{
var value = JSONConstants.ESCAPE_CHARS_TO_VALUES[ch.get(0)];
if( value == null)
{
throw new TProtocolException( TProtocolException.INVALID_DATA, "Expected control char");
}
buffer.addByte( value);
continue;
}
// it's \uXXXX
var hexbuf = new BytesBuffer();
var hexlen = trans.readAll( hexbuf, 0, 4);
if( hexlen != 4)
{
throw new TProtocolException( TProtocolException.INVALID_DATA, "Not enough data for \\uNNNN sequence");
}
var hexdigits = hexbuf.getBytes();
var charcode = 0;
charcode = (charcode << 4) + HexVal( String.fromCharCode(hexdigits.get(0)));
charcode = (charcode << 4) + HexVal( String.fromCharCode(hexdigits.get(1)));
charcode = (charcode << 4) + HexVal( String.fromCharCode(hexdigits.get(2)));
charcode = (charcode << 4) + HexVal( String.fromCharCode(hexdigits.get(3)));
buffer.addString( String.fromCharCode(charcode));
}
return StringFromBytes( buffer.getBytes());
}
// Return true if the given byte could be a valid part of a JSON number.
private function IsJSONNumeric(b : Int) : Bool {
switch (b)
{
case "+".code: return true;
case "-".code: return true;
case ".".code: return true;
case "0".code: return true;
case "1".code: return true;
case "2".code: return true;
case "3".code: return true;
case "4".code: return true;
case "5".code: return true;
case "6".code: return true;
case "7".code: return true;
case "8".code: return true;
case "9".code: return true;
case "E".code: return true;
case "e".code: 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 function ReadJSONNumericChars() : String
{
var buffer : BytesBuffer = new BytesBuffer();
while (true)
{
var ch = reader.Peek();
if( ! IsJSONNumeric( ch.get(0)))
{
break;
}
buffer.addByte( reader.Read().get(0));
}
return StringFromBytes( buffer.getBytes());
}
// Read in a JSON number. If the context dictates, Read in enclosing quotes.
private function ReadJSONInteger() : Int {
context.Read();
if (context.EscapeNumbers()) {
ReadJSONSyntaxChar( JSONConstants.QUOTE);
}
var str : String = ReadJSONNumericChars();
if (context.EscapeNumbers()) {
ReadJSONSyntaxChar( JSONConstants.QUOTE);
}
var value = Std.parseInt(str);
if( value == null) {
throw new TProtocolException(TProtocolException.INVALID_DATA, 'Bad numeric data: $str');
}
return value;
}
// Read in a JSON number. If the context dictates, Read in enclosing quotes.
private function ReadJSONInt64() : haxe.Int64 {
context.Read();
if (context.EscapeNumbers()) {
ReadJSONSyntaxChar( JSONConstants.QUOTE);
}
var str : String = ReadJSONNumericChars();
if( str.length == 0) {
throw new TProtocolException(TProtocolException.INVALID_DATA, 'Bad numeric data: $str');
}
if (context.EscapeNumbers()) {
ReadJSONSyntaxChar( JSONConstants.QUOTE);
}
// process sign
var bMinus = false;
var startAt = 0;
if( (str.charAt(0) == "+") || (str.charAt(0) == "-")) {
bMinus = (str.charAt(0) == "-");
startAt++;
}
// process digits
var value : Int64 = Int64.make(0,0);
var bGotDigits = false;
for( i in startAt ... str.length) {
var ch = str.charAt(i);
var digit = JSONConstants.DECIMAL_DIGITS[ch];
if( digit == null) {
throw new TProtocolException(TProtocolException.INVALID_DATA, 'Bad numeric data: $str');
}
bGotDigits = true;
// these are decimal digits
value = Int64.mul( value, Int64.make(0,10));
value = Int64.add( value, Int64.make(0,digit));
}
// process pending minus sign, if applicable
// this should also handle the edge case MIN_INT64 correctly
if( bMinus && (Int64.compare(value,Int64.make(0,0)) > 0)) {
value = Int64.neg( value);
bMinus = false;
}
if( ! bGotDigits) {
throw new TProtocolException(TProtocolException.INVALID_DATA, 'Bad numeric data: $str');
}
return value;
}
// 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 function ReadJSONDouble() : Float {
context.Read();
var str : String = "";
if (StringFromBytes(reader.Peek()) == JSONConstants.QUOTE) {
str = ReadJSONString(true);
// special cases
if( str == JSONConstants.FLOAT_IS_NAN) {
return Math.NaN;
}
if( str == JSONConstants.FLOAT_IS_POS_INF) {
return Math.POSITIVE_INFINITY;
}
if( str == JSONConstants.FLOAT_IS_NEG_INF) {
return Math.NEGATIVE_INFINITY;
}
if( ! context.EscapeNumbers()) {
// throw - we should not be in a string in this case
throw new TProtocolException(TProtocolException.INVALID_DATA, "Numeric data unexpectedly quoted");
}
}
else
{
if( context.EscapeNumbers()) {
// This will throw - we should have had a quote if EscapeNumbers() == true
ReadJSONSyntaxChar( JSONConstants.QUOTE);
}
str = ReadJSONNumericChars();
}
// parse and check - we should have at least one valid digit
var dub = Std.parseFloat( str);
if( (str.length == 0) || Math.isNaN(dub)) {
throw new TProtocolException(TProtocolException.INVALID_DATA, 'Bad numeric data: $str');
}
return dub;
}
// Read in a JSON string containing base-64 encoded data and decode it.
private function ReadJSONBase64() : Bytes
{
var str = ReadJSONString(false);
return Base64.decode( str);
}
private function ReadJSONObjectStart() : Void {
context.Read();
ReadJSONSyntaxChar( JSONConstants.LBRACE);
PushContext(new JSONPairContext(this));
}
private function ReadJSONObjectEnd() : Void {
ReadJSONSyntaxChar( JSONConstants.RBRACE);
PopContext();
}
private function ReadJSONArrayStart() : Void {
context.Read();
ReadJSONSyntaxChar( JSONConstants.LBRACKET);
PushContext(new JSONListContext(this));
}
private function ReadJSONArrayEnd() : Void {
ReadJSONSyntaxChar( JSONConstants.RBRACKET);
PopContext();
}
public static function BytesFromString( str : String) : Bytes {
var buf = new BytesBuffer();
if( utf8Strings)
buf.addString( str); // no need to encode on UTF8 targets, the string is just fine
else
buf.addString( Utf8.encode( str));
return buf.getBytes();
}
public static function StringFromBytes( buf : Bytes) : String {
var inp = new BytesInput( buf);
if( buf.length == 0)
return ""; // readString() would return null in that case, which is wrong
var str = inp.readString( buf.length);
if( utf8Strings)
return str; // no need to decode on UTF8 targets, the string is just fine
else
return Utf8.decode( str);
}
// Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its corresponding hex value
private static function HexVal(char : String) : Int {
var value = JSONConstants.HEX_DIGITS[char];
if( value == null) {
throw new TProtocolException(TProtocolException.INVALID_DATA, 'Expected hex character: $char');
}
return value;
}
// Convert a byte containing a hex nibble to its corresponding hex character
private static function HexChar(nibble : Int) : String
{
return "0123456789abcdef".charAt(nibble & 0x0F);
}
}
@:allow(TJSONProtocol)
class JSONConstants {
public static var COMMA = ",";
public static var COLON = ":";
public static var LBRACE = "{";
public static var RBRACE = "}";
public static var LBRACKET = "[";
public static var RBRACKET = "]";
public static var QUOTE = "\"";
public static var BACKSLASH = "\\";
public static var ESCSEQ = "\\u";
public static var FLOAT_IS_NAN = "NaN";
public static var FLOAT_IS_POS_INF = "Infinity";
public static var FLOAT_IS_NEG_INF = "-Infinity";
public static var VERSION = 1;
public static var JSON_CHAR_TABLE = [
0, 0, 0, 0, 0, 0, 0, 0,
"b".code, "t".code, "n".code, 0, "f".code, "r".code, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, "\"".code, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
];
public static var ESCAPE_CHARS = ['"','\\','/','b','f','n','r','t'];
public static var ESCAPE_CHARS_TO_VALUES = [
"\"".code => 0x22,
"\\".code => 0x5C,
"/".code => 0x2F,
"b".code => 0x08,
"f".code => 0x0C,
"n".code => 0x0A,
"r".code => 0x0D,
"t".code => 0x09
];
public static var DECIMAL_DIGITS = [
"0" => 0,
"1" => 1,
"2" => 2,
"3" => 3,
"4" => 4,
"5" => 5,
"6" => 6,
"7" => 7,
"8" => 8,
"9" => 9
];
public static var HEX_DIGITS = [
"0" => 0,
"1" => 1,
"2" => 2,
"3" => 3,
"4" => 4,
"5" => 5,
"6" => 6,
"7" => 7,
"8" => 8,
"9" => 9,
"A" => 10,
"a" => 10,
"B" => 11,
"b" => 11,
"C" => 12,
"c" => 12,
"D" => 13,
"d" => 13,
"E" => 14,
"e" => 14,
"F" => 15,
"f" => 15
];
public static var DEF_STRING_SIZE = 16;
public static var NAME_BOOL = 'tf';
public static var NAME_BYTE = 'i8';
public static var NAME_I16 = 'i16';
public static var NAME_I32 = 'i32';
public static var NAME_I64 = 'i64';
public static var NAME_DOUBLE = 'dbl';
public static var NAME_STRUCT = 'rec';
public static var NAME_STRING = 'str';
public static var NAME_MAP = 'map';
public static var NAME_LIST = 'lst';
public static var NAME_SET = 'set';
public static function GetTypeNameForTypeID(typeID : Int) : String {
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;
}
throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, "Unrecognized type");
}
private static var NAMES_TO_TYPES = [
NAME_BOOL => TType.BOOL,
NAME_BYTE => TType.BYTE,
NAME_I16 => TType.I16,
NAME_I32 => TType.I32,
NAME_I64 => TType.I64,
NAME_DOUBLE => TType.DOUBLE,
NAME_STRING => TType.STRING,
NAME_STRUCT => TType.STRUCT,
NAME_MAP => TType.MAP,
NAME_SET => TType.SET,
NAME_LIST => TType.LIST
];
public static function GetTypeIDForTypeName(name : String) : Int
{
var type = NAMES_TO_TYPES[name];
if( null != type) {
return type;
}
throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, "Unrecognized type");
}
}
// Base class for tracking JSON contexts that may require inserting/Reading
// additional JSON syntax characters. This base context does nothing.
@:allow(TJSONProtocol)
class JSONBaseContext
{
private var proto : TJSONProtocol;
public function new(proto : TJSONProtocol )
{
this.proto = proto;
}
public function Write() : Void { }
public function Read() : Void { }
public function EscapeNumbers() : Bool {
return false;
}
}
// Context for JSON lists.
// Will insert/Read commas before each item except for the first one
@:allow(TJSONProtocol)
class JSONListContext extends JSONBaseContext
{
public function new( proto : TJSONProtocol) {
super(proto);
}
private var first : Bool = true;
public override function Write() : Void {
if (first)
{
first = false;
}
else
{
var buf = new BytesBuffer();
buf.addString( JSONConstants.COMMA);
var tmp = buf.getBytes();
proto.trans.write( tmp, 0, tmp.length);
}
}
public override function Read() : Void {
if (first)
{
first = false;
}
else
{
proto.ReadJSONSyntaxChar( JSONConstants.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).
@:allow(TJSONProtocol)
class JSONPairContext extends JSONBaseContext
{
public function new( proto : TJSONProtocol ) {
super( proto);
}
private var first : Bool = true;
private var colon : Bool = true;
public override function Write() : Void {
if (first)
{
first = false;
colon = true;
}
else
{
var buf = new BytesBuffer();
buf.addString( colon ? JSONConstants.COLON : JSONConstants.COMMA);
var tmp = buf.getBytes();
proto.trans.write( tmp, 0, tmp.length);
colon = !colon;
}
}
public override function Read() : Void {
if (first)
{
first = false;
colon = true;
}
else
{
proto.ReadJSONSyntaxChar( colon ? JSONConstants.COLON : JSONConstants.COMMA);
colon = !colon;
}
}
public override function EscapeNumbers() : Bool
{
return colon;
}
}
// Holds up to one byte from the transport
@:allow(TJSONProtocol)
class LookaheadReader {
private var proto : TJSONProtocol;
private var data : Bytes;
public function new( proto : TJSONProtocol ) {
this.proto = proto;
data = null;
}
// 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.
public function Read() : Bytes {
var retval = Peek();
data = null;
return retval;
}
// Return the next byte to be Read without consuming, filling the data
// buffer if it has not been filled alReady.
public function Peek() : Bytes {
if (data == null) {
var buf = new BytesBuffer();
proto.trans.readAll(buf, 0, 1);
data = buf.getBytes();
}
return data;
}
}