Subversion Repositories SmartDukaan

Rev

Rev 30 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
30 ashish 1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one
3
 * or more contributor license agreements. See the NOTICE file
4
 * distributed with this work for additional information
5
 * regarding copyright ownership. The ASF licenses this file
6
 * to you under the Apache License, Version 2.0 (the
7
 * "License"); you may not use this file except in compliance
8
 * with the License. You may obtain a copy of the License at
9
 *
10
 *   http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing,
13
 * software distributed under the License is distributed on an
14
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
 * KIND, either express or implied. See the License for the
16
 * specific language governing permissions and limitations
17
 * under the License.
18
 */
19
 
20
package org.apache.thrift.protocol;
21
 
22
import org.apache.thrift.TException;
23
import org.apache.thrift.TByteArrayOutputStream;
24
import org.apache.thrift.transport.TTransport;
25
import java.io.UnsupportedEncodingException;
26
import java.util.Stack;
27
 
28
/**
29
 * JSON protocol implementation for thrift.
30
 *
31
 * This is a full-featured protocol supporting write and read.
32
 *
33
 * Please see the C++ class header for a detailed description of the
34
 * protocol's wire format.
35
 *
36
 */
37
public class TJSONProtocol extends TProtocol {
38
 
39
  /**
40
   * Factory for JSON protocol objects
41
   */
42
  public static class Factory implements TProtocolFactory {
43
 
44
    public TProtocol getProtocol(TTransport trans) {
45
      return new TJSONProtocol(trans);
46
    }
47
 
48
  }
49
 
50
  private static final byte[] COMMA = new byte[] {','};
51
  private static final byte[] COLON = new byte[] {':'};
52
  private static final byte[] LBRACE = new byte[] {'{'};
53
  private static final byte[] RBRACE = new byte[] {'}'};
54
  private static final byte[] LBRACKET = new byte[] {'['};
55
  private static final byte[] RBRACKET = new byte[] {']'};
56
  private static final byte[] QUOTE = new byte[] {'"'};
57
  private static final byte[] BACKSLASH = new byte[] {'\\'};
58
  private static final byte[] ZERO = new byte[] {'0'};
59
 
60
  private static final byte[] ESCSEQ = new byte[] {'\\','u','0','0'};
61
 
62
  private static final long  VERSION = 1;
63
 
64
  private static final byte[] JSON_CHAR_TABLE = {
65
    /*  0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F */
66
    0,  0,  0,  0,  0,  0,  0,  0,'b','t','n',  0,'f','r',  0,  0, // 0
67
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 1
68
    1,  1,'"',  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, // 2
69
  };
70
 
71
  private static final String ESCAPE_CHARS = "\"\\bfnrt";
72
 
73
  private static final byte[] ESCAPE_CHAR_VALS = {
74
    '"', '\\', '\b', '\f', '\n', '\r', '\t',
75
  };
76
 
77
  private static final int  DEF_STRING_SIZE = 16;
78
 
79
  private static final byte[] NAME_BOOL = new byte[] {'t', 'f'};
80
  private static final byte[] NAME_BYTE = new byte[] {'i','8'};
81
  private static final byte[] NAME_I16 = new byte[] {'i','1','6'};
82
  private static final byte[] NAME_I32 = new byte[] {'i','3','2'};
83
  private static final byte[] NAME_I64 = new byte[] {'i','6','4'};
84
  private static final byte[] NAME_DOUBLE = new byte[] {'d','b','l'};
85
  private static final byte[] NAME_STRUCT = new byte[] {'r','e','c'};
86
  private static final byte[] NAME_STRING = new byte[] {'s','t','r'};
87
  private static final byte[] NAME_MAP = new byte[] {'m','a','p'};
88
  private static final byte[] NAME_LIST = new byte[] {'l','s','t'};
89
  private static final byte[] NAME_SET = new byte[] {'s','e','t'};
90
 
91
  private static final TStruct ANONYMOUS_STRUCT = new TStruct();
92
 
93
  private static final byte[] getTypeNameForTypeID(byte typeID)
94
    throws TException {
95
    switch (typeID) {
96
    case TType.BOOL:
97
      return NAME_BOOL;
98
    case TType.BYTE:
99
      return NAME_BYTE;
100
    case TType.I16:
101
      return NAME_I16;
102
    case TType.I32:
103
      return NAME_I32;
104
    case TType.I64:
105
      return NAME_I64;
106
    case TType.DOUBLE:
107
      return NAME_DOUBLE;
108
    case TType.STRING:
109
      return NAME_STRING;
110
    case TType.STRUCT:
111
      return NAME_STRUCT;
112
    case TType.MAP:
113
      return NAME_MAP;
114
    case TType.SET:
115
      return NAME_SET;
116
    case TType.LIST:
117
      return NAME_LIST;
118
    default:
119
      throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
120
                                   "Unrecognized type");
121
    }
122
  }
123
 
124
  private static final byte getTypeIDForTypeName(byte[] name)
125
    throws TException {
126
    byte result = TType.STOP;
127
    if (name.length > 1) {
128
      switch (name[0]) {
129
      case 'd':
130
        result = TType.DOUBLE;
131
        break;
132
      case 'i':
133
        switch (name[1]) {
134
        case '8':
135
          result = TType.BYTE;
136
          break;
137
        case '1':
138
          result = TType.I16;
139
          break;
140
        case '3':
141
          result = TType.I32;
142
          break;
143
        case '6':
144
          result = TType.I64;
145
          break;
146
        }
147
        break;
148
      case 'l':
149
        result = TType.LIST;
150
        break;
151
      case 'm':
152
        result = TType.MAP;
153
        break;
154
      case 'r':
155
        result = TType.STRUCT;
156
        break;
157
      case 's':
158
        if (name[1] == 't') {
159
          result = TType.STRING;
160
        }
161
        else if (name[1] == 'e') {
162
          result = TType.SET;
163
        }
164
        break;
165
      case 't':
166
        result = TType.BOOL;
167
        break;
168
      }
169
    }
170
    if (result == TType.STOP) {
171
      throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
172
                                   "Unrecognized type");
173
    }
174
    return result;
175
  }
176
 
177
  // Base class for tracking JSON contexts that may require inserting/reading
178
  // additional JSON syntax characters
179
  // This base context does nothing.
180
  protected class JSONBaseContext {
181
    protected void write() throws TException {}
182
 
183
    protected void read() throws TException {}
184
 
185
    protected boolean escapeNum() { return false; }
186
  }
187
 
188
  // Context for JSON lists. Will insert/read commas before each item except
189
  // for the first one
190
  protected class JSONListContext extends JSONBaseContext {
191
    private boolean first_ = true;
192
 
193
    @Override
194
    protected void write() throws TException {
195
      if (first_) {
196
        first_ = false;
197
      } else {
198
        trans_.write(COMMA);
199
      }
200
    }
201
 
202
    @Override
203
    protected void read() throws TException {
204
      if (first_) {
205
        first_ = false;
206
      } else {
207
        readJSONSyntaxChar(COMMA);
208
      }
209
    }
210
  }
211
 
212
  // Context for JSON records. Will insert/read colons before the value portion
213
  // of each record pair, and commas before each key except the first. In
214
  // addition, will indicate that numbers in the key position need to be
215
  // escaped in quotes (since JSON keys must be strings).
216
  protected class JSONPairContext extends JSONBaseContext {
217
    private boolean first_ = true;
218
    private boolean colon_ = true;
219
 
220
    @Override
221
    protected void write() throws TException {
222
      if (first_) {
223
        first_ = false;
224
        colon_ = true;
225
      } else {
226
        trans_.write(colon_ ? COLON : COMMA);
227
        colon_ = !colon_;
228
      }
229
    }
230
 
231
    @Override
232
    protected void read() throws TException {
233
      if (first_) {
234
        first_ = false;
235
        colon_ = true;
236
      } else {
237
        readJSONSyntaxChar(colon_ ? COLON : COMMA);
238
        colon_ = !colon_;
239
      }
240
    }
241
 
242
    @Override
243
    protected boolean escapeNum() {
244
      return colon_;
245
    }
246
  }
247
 
248
  // Holds up to one byte from the transport
249
  protected class LookaheadReader {
250
 
251
    private boolean hasData_;
252
    private byte[] data_ = new byte[1];
253
 
254
    // Return and consume the next byte to be read, either taking it from the
255
    // data buffer if present or getting it from the transport otherwise.
256
    protected byte read() throws TException {
257
      if (hasData_) {
258
        hasData_ = false;
259
      }
260
      else {
261
        trans_.readAll(data_, 0, 1);
262
      }
263
      return data_[0];
264
    }
265
 
266
    // Return the next byte to be read without consuming, filling the data
267
    // buffer if it has not been filled already.
268
    protected byte peek() throws TException {
269
      if (!hasData_) {
270
        trans_.readAll(data_, 0, 1);
271
      }
272
      hasData_ = true;
273
      return data_[0];
274
    }
275
  }
276
 
277
  // Stack of nested contexts that we may be in
278
  private Stack<JSONBaseContext> contextStack_ = new Stack<JSONBaseContext>();
279
 
280
  // Current context that we are in
281
  private JSONBaseContext context_ = new JSONBaseContext();
282
 
283
  // Reader that manages a 1-byte buffer
284
  private LookaheadReader reader_ = new LookaheadReader();
285
 
286
  // Push a new JSON context onto the stack.
287
  private void pushContext(JSONBaseContext c) {
288
    contextStack_.push(context_);
289
    context_ = c;
290
  }
291
 
292
  // Pop the last JSON context off the stack
293
  private void popContext() {
294
    context_ = contextStack_.pop();
295
  }
296
 
297
  /**
298
   * Constructor
299
   */
300
  public TJSONProtocol(TTransport trans) {
301
    super(trans);
302
  }
303
 
304
  // Temporary buffer used by several methods
305
  private byte[] tmpbuf_ = new byte[4];
306
 
307
  // Read a byte that must match b[0]; otherwise an excpetion is thrown.
308
  // Marked protected to avoid synthetic accessor in JSONListContext.read
309
  // and JSONPairContext.read
310
  protected void readJSONSyntaxChar(byte[] b) throws TException {
311
    byte ch = reader_.read();
312
    if (ch != b[0]) {
313
      throw new TProtocolException(TProtocolException.INVALID_DATA,
314
                                   "Unexpected character:" + (char)ch);
315
    }
316
  }
317
 
318
  // Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its
319
  // corresponding hex value
320
  private static final byte hexVal(byte ch) throws TException {
321
    if ((ch >= '0') && (ch <= '9')) {
322
      return (byte)((char)ch - '0');
323
    }
324
    else if ((ch >= 'a') && (ch <= 'f')) {
325
      return (byte)((char)ch - 'a');
326
    }
327
    else {
328
      throw new TProtocolException(TProtocolException.INVALID_DATA,
329
                                   "Expected hex character");
330
    }
331
  }
332
 
333
  // Convert a byte containing a hex value to its corresponding hex character
334
  private static final byte hexChar(byte val) {
335
    val &= 0x0F;
336
    if (val < 10) {
337
      return (byte)((char)val + '0');
338
    }
339
    else {
340
      return (byte)((char)val + 'a');
341
    }
342
  }
343
 
344
  // Write the bytes in array buf as a JSON characters, escaping as needed
345
  private void writeJSONString(byte[] b) throws TException {
346
    context_.write();
347
    trans_.write(QUOTE);
348
    int len = b.length;
349
    for (int i = 0; i < len; i++) {
350
      if ((b[i] & 0x00FF) >= 0x30) {
351
        if (b[i] == BACKSLASH[0]) {
352
          trans_.write(BACKSLASH);
353
          trans_.write(BACKSLASH);
354
        }
355
        else {
356
          trans_.write(b, i, 1);
357
        }
358
      }
359
      else {
360
        tmpbuf_[0] = JSON_CHAR_TABLE[b[i]];
361
        if (tmpbuf_[0] == 1) {
362
          trans_.write(b, i, 1);
363
        }
364
        else if (tmpbuf_[0] > 1) {
365
          trans_.write(BACKSLASH);
366
          trans_.write(tmpbuf_, 0, 1);
367
        }
368
        else {
369
          trans_.write(ESCSEQ);
370
          tmpbuf_[0] = hexChar((byte)(b[i] >> 4));
371
          tmpbuf_[1] = hexChar(b[i]);
372
          trans_.write(tmpbuf_, 0, 2);
373
        }
374
      }
375
    }
376
    trans_.write(QUOTE);
377
  }
378
 
379
  // Write out number as a JSON value. If the context dictates so, it will be
380
  // wrapped in quotes to output as a JSON string.
381
  private void writeJSONInteger(long num) throws TException {
382
    context_.write();
383
    String str = Long.toString(num);
384
    boolean escapeNum = context_.escapeNum();
385
    if (escapeNum) {
386
      trans_.write(QUOTE);
387
    }
388
    try {
389
      byte[] buf = str.getBytes("UTF-8");
390
      trans_.write(buf);
391
    } catch (UnsupportedEncodingException uex) {
392
      throw new TException("JVM DOES NOT SUPPORT UTF-8");
393
    }
394
    if (escapeNum) {
395
      trans_.write(QUOTE);
396
    }
397
  }
398
 
399
  // Write out a double as a JSON value. If it is NaN or infinity or if the
400
  // context dictates escaping, write out as JSON string.
401
  private void writeJSONDouble(double num) throws TException {
402
    context_.write();
403
    String str = Double.toString(num);
404
    boolean special = false;
405
    switch (str.charAt(0)) {
406
    case 'N': // NaN
407
    case 'I': // Infinity
408
      special = true;
409
      break;
410
    case '-':
411
      if (str.charAt(1) == 'I') { // -Infinity
412
        special = true;
413
      }
414
      break;
415
    }
416
 
417
    boolean escapeNum = special || context_.escapeNum();
418
    if (escapeNum) {
419
      trans_.write(QUOTE);
420
    }
421
    try {
422
      byte[] b = str.getBytes("UTF-8");
423
      trans_.write(b, 0, b.length);
424
    } catch (UnsupportedEncodingException uex) {
425
      throw new TException("JVM DOES NOT SUPPORT UTF-8");
426
    }
427
    if (escapeNum) {
428
      trans_.write(QUOTE);
429
    }
430
  }
431
 
432
  // Write out contents of byte array b as a JSON string with base-64 encoded
433
  // data
434
  private void writeJSONBase64(byte[] b) throws TException {
435
    context_.write();
436
    trans_.write(QUOTE);
437
    int len = b.length;
438
    int off = 0;
439
    while (len >= 3) {
440
      // Encode 3 bytes at a time
441
      TBase64Utils.encode(b, off, 3, tmpbuf_, 0);
442
      trans_.write(tmpbuf_, 0, 4);
443
      off += 3;
444
      len -= 3;
445
    }
446
    if (len > 0) {
447
      // Encode remainder
448
      TBase64Utils.encode(b, off, len, tmpbuf_, 0);
449
      trans_.write(tmpbuf_, 0, len + 1);
450
    }
451
    trans_.write(QUOTE);
452
  }
453
 
454
  private void writeJSONObjectStart() throws TException {
455
    context_.write();
456
    trans_.write(LBRACE);
457
    pushContext(new JSONPairContext());
458
  }
459
 
460
  private void writeJSONObjectEnd() throws TException {
461
    popContext();
462
    trans_.write(RBRACE);
463
  }
464
 
465
  private void writeJSONArrayStart() throws TException {
466
    context_.write();
467
    trans_.write(LBRACKET);
468
    pushContext(new JSONListContext());
469
  }
470
 
471
  private void writeJSONArrayEnd() throws TException {
472
    popContext();
473
    trans_.write(RBRACKET);
474
  }
475
 
476
  @Override
477
  public void writeMessageBegin(TMessage message) throws TException {
478
    writeJSONArrayStart();
479
    writeJSONInteger(VERSION);
480
    try {
481
      byte[] b = message.name.getBytes("UTF-8");
482
      writeJSONString(b);
483
    } catch (UnsupportedEncodingException uex) {
484
      throw new TException("JVM DOES NOT SUPPORT UTF-8");
485
    }
486
    writeJSONInteger(message.type);
487
    writeJSONInteger(message.seqid);
488
  }
489
 
490
  @Override
491
  public void writeMessageEnd() throws TException {
492
    writeJSONArrayEnd();
493
  }
494
 
495
  @Override
496
  public void writeStructBegin(TStruct struct) throws TException {
497
    writeJSONObjectStart();
498
  }
499
 
500
  @Override
501
  public void writeStructEnd() throws TException {
502
    writeJSONObjectEnd();
503
  }
504
 
505
  @Override
506
  public void writeFieldBegin(TField field) throws TException {
507
    writeJSONInteger(field.id);
508
    writeJSONObjectStart();
509
    writeJSONString(getTypeNameForTypeID(field.type));
510
  }
511
 
512
  @Override
513
  public void writeFieldEnd() throws TException {
514
    writeJSONObjectEnd();
515
  }
516
 
517
  @Override
518
  public void writeFieldStop() {}
519
 
520
  @Override
521
  public void writeMapBegin(TMap map) throws TException {
522
    writeJSONArrayStart();
523
    writeJSONString(getTypeNameForTypeID(map.keyType));
524
    writeJSONString(getTypeNameForTypeID(map.valueType));
525
    writeJSONInteger(map.size);
526
    writeJSONObjectStart();
527
  }
528
 
529
  @Override
530
  public void writeMapEnd() throws TException {
531
    writeJSONObjectEnd();
532
    writeJSONArrayEnd();
533
  }
534
 
535
  @Override
536
  public void writeListBegin(TList list) throws TException {
537
    writeJSONArrayStart();
538
    writeJSONString(getTypeNameForTypeID(list.elemType));
539
    writeJSONInteger(list.size);
540
  }
541
 
542
  @Override
543
  public void writeListEnd() throws TException {
544
    writeJSONArrayEnd();
545
  }
546
 
547
  @Override
548
  public void writeSetBegin(TSet set) throws TException {
549
    writeJSONArrayStart();
550
    writeJSONString(getTypeNameForTypeID(set.elemType));
551
    writeJSONInteger(set.size);
552
  }
553
 
554
  @Override
555
  public void writeSetEnd() throws TException {
556
    writeJSONArrayEnd();
557
  }
558
 
559
  @Override
560
  public void writeBool(boolean b) throws TException {
561
    writeJSONInteger(b ? (long)1 : (long)0);
562
  }
563
 
564
  @Override
565
  public void writeByte(byte b) throws TException {
566
    writeJSONInteger((long)b);
567
  }
568
 
569
  @Override
570
  public void writeI16(short i16) throws TException {
571
    writeJSONInteger((long)i16);
572
  }
573
 
574
  @Override
575
  public void writeI32(int i32) throws TException {
576
    writeJSONInteger((long)i32);
577
  }
578
 
579
  @Override
580
  public void writeI64(long i64) throws TException {
581
    writeJSONInteger(i64);
582
  }
583
 
584
  @Override
585
  public void writeDouble(double dub) throws TException {
586
    writeJSONDouble(dub);
587
  }
588
 
589
  @Override
590
  public void writeString(String str) throws TException {
591
    try {
592
      byte[] b = str.getBytes("UTF-8");
593
      writeJSONString(b);
594
    } catch (UnsupportedEncodingException uex) {
595
      throw new TException("JVM DOES NOT SUPPORT UTF-8");
596
    }
597
  }
598
 
599
  @Override
600
  public void writeBinary(byte[] bin) throws TException {
601
    writeJSONBase64(bin);
602
  }
603
 
604
  /**
605
   * Reading methods.
606
   */
607
 
608
  // Read in a JSON string, unescaping as appropriate.. Skip reading from the
609
  // context if skipContext is true.
610
  private TByteArrayOutputStream readJSONString(boolean skipContext)
611
    throws TException {
612
    TByteArrayOutputStream arr = new TByteArrayOutputStream(DEF_STRING_SIZE);
613
    if (!skipContext) {
614
      context_.read();
615
    }
616
    readJSONSyntaxChar(QUOTE);
617
    while (true) {
618
      byte ch = reader_.read();
619
      if (ch == QUOTE[0]) {
620
        break;
621
      }
622
      if (ch == ESCSEQ[0]) {
623
        ch = reader_.read();
624
        if (ch == ESCSEQ[1]) {
625
          readJSONSyntaxChar(ZERO);
626
          readJSONSyntaxChar(ZERO);
627
          trans_.readAll(tmpbuf_, 0, 2);
628
          ch = (byte)((hexVal((byte)tmpbuf_[0]) << 4) + hexVal(tmpbuf_[1]));
629
        }
630
        else {
631
          int off = ESCAPE_CHARS.indexOf(ch);
632
          if (off == -1) {
633
            throw new TProtocolException(TProtocolException.INVALID_DATA,
634
                                         "Expected control char");
635
          }
636
          ch = ESCAPE_CHAR_VALS[off];
637
        }
638
      }
639
      arr.write(ch);
640
    }
641
    return arr;
642
  }
643
 
644
  // Return true if the given byte could be a valid part of a JSON number.
645
  private boolean isJSONNumeric(byte b) {
646
    switch (b) {
647
    case '+':
648
    case '-':
649
    case '.':
650
    case '0':
651
    case '1':
652
    case '2':
653
    case '3':
654
    case '4':
655
    case '5':
656
    case '6':
657
    case '7':
658
    case '8':
659
    case '9':
660
    case 'E':
661
    case 'e':
662
      return true;
663
    }
664
    return false;
665
  }
666
 
667
  // Read in a sequence of characters that are all valid in JSON numbers. Does
668
  // not do a complete regex check to validate that this is actually a number.
669
  private String readJSONNumericChars() throws TException {
670
    StringBuilder strbld = new StringBuilder();
671
    while (true) {
672
      byte ch = reader_.peek();
673
      if (!isJSONNumeric(ch)) {
674
        break;
675
      }
676
      strbld.append((char)reader_.read());
677
    }
678
    return strbld.toString();
679
  }
680
 
681
  // Read in a JSON number. If the context dictates, read in enclosing quotes.
682
  private long readJSONInteger() throws TException {
683
    context_.read();
684
    if (context_.escapeNum()) {
685
      readJSONSyntaxChar(QUOTE);
686
    }
687
    String str = readJSONNumericChars();
688
    if (context_.escapeNum()) {
689
      readJSONSyntaxChar(QUOTE);
690
    }
691
    try {
692
      return Long.valueOf(str);
693
    }
694
    catch (NumberFormatException ex) {
695
      throw new TProtocolException(TProtocolException.INVALID_DATA,
696
                                   "Bad data encounted in numeric data");
697
    }
698
  }
699
 
700
  // Read in a JSON double value. Throw if the value is not wrapped in quotes
701
  // when expected or if wrapped in quotes when not expected.
702
  private double readJSONDouble() throws TException {
703
    context_.read();
704
    if (reader_.peek() == QUOTE[0]) {
705
      TByteArrayOutputStream arr = readJSONString(true);
706
      try {
707
        double dub = Double.valueOf(arr.toString("UTF-8"));
708
        if (!context_.escapeNum() && !Double.isNaN(dub) &&
709
            !Double.isInfinite(dub)) {
710
          // Throw exception -- we should not be in a string in this case
711
          throw new TProtocolException(TProtocolException.INVALID_DATA,
712
                                       "Numeric data unexpectedly quoted");
713
        }
714
        return dub;
715
      }
716
      catch (UnsupportedEncodingException ex) {
717
        throw new TException("JVM DOES NOT SUPPORT UTF-8");
718
      }
719
    }
720
    else {
721
      if (context_.escapeNum()) {
722
        // This will throw - we should have had a quote if escapeNum == true
723
        readJSONSyntaxChar(QUOTE);
724
      }
725
      try {
726
        return Double.valueOf(readJSONNumericChars());
727
      }
728
      catch (NumberFormatException ex) {
729
        throw new TProtocolException(TProtocolException.INVALID_DATA,
730
                                     "Bad data encounted in numeric data");
731
      }
732
    }
733
  }
734
 
735
  // Read in a JSON string containing base-64 encoded data and decode it.
736
  private byte[] readJSONBase64() throws TException {
737
    TByteArrayOutputStream arr = readJSONString(false);
738
    byte[] b = arr.get();
739
    int len = arr.len();
740
    int off = 0;
741
    int size = 0;
742
    while (len >= 4) {
743
      // Decode 4 bytes at a time
744
      TBase64Utils.decode(b, off, 4, b, size); // NB: decoded in place
745
      off += 4;
746
      len -= 4;
747
      size += 3;
748
    }
749
    // Don't decode if we hit the end or got a single leftover byte (invalid
750
    // base64 but legal for skip of regular string type)
751
    if (len > 1) {
752
      // Decode remainder
753
      TBase64Utils.decode(b, off, len, b, size); // NB: decoded in place
754
      size += len - 1;
755
    }
756
    // Sadly we must copy the byte[] (any way around this?)
757
    byte [] result = new byte[size];
758
    System.arraycopy(b, 0, result, 0, size);
759
    return result;
760
  }
761
 
762
  private void readJSONObjectStart() throws TException {
763
    context_.read();
764
    readJSONSyntaxChar(LBRACE);
765
    pushContext(new JSONPairContext());
766
  }
767
 
768
  private void readJSONObjectEnd() throws TException {
769
    readJSONSyntaxChar(RBRACE);
770
    popContext();
771
  }
772
 
773
  private void readJSONArrayStart() throws TException {
774
    context_.read();
775
    readJSONSyntaxChar(LBRACKET);
776
    pushContext(new JSONListContext());
777
  }
778
 
779
  private void readJSONArrayEnd() throws TException {
780
    readJSONSyntaxChar(RBRACKET);
781
    popContext();
782
  }
783
 
784
  @Override
785
  public TMessage readMessageBegin() throws TException {
786
    readJSONArrayStart();
787
    if (readJSONInteger() != VERSION) {
788
      throw new TProtocolException(TProtocolException.BAD_VERSION,
789
                                   "Message contained bad version.");
790
    }
791
    String name;
792
    try {
793
      name = readJSONString(false).toString("UTF-8");
794
    }
795
    catch (UnsupportedEncodingException ex) {
796
      throw new TException("JVM DOES NOT SUPPORT UTF-8");
797
    }
798
    byte type = (byte) readJSONInteger();
799
    int seqid = (int) readJSONInteger();
800
    return new TMessage(name, type, seqid);
801
  }
802
 
803
  @Override
804
  public void readMessageEnd() throws TException {
805
    readJSONArrayEnd();
806
  }
807
 
808
  @Override
809
  public TStruct readStructBegin() throws TException {
810
    readJSONObjectStart();
811
    return ANONYMOUS_STRUCT;
812
  }
813
 
814
  @Override
815
  public void readStructEnd() throws TException {
816
    readJSONObjectEnd();
817
  }
818
 
819
  @Override
820
  public TField readFieldBegin() throws TException {
821
    byte ch = reader_.peek();
822
    byte type;
823
    short id = 0;
824
    if (ch == RBRACE[0]) {
825
      type = TType.STOP;
826
    }
827
    else {
828
      id = (short) readJSONInteger();
829
      readJSONObjectStart();
830
      type = getTypeIDForTypeName(readJSONString(false).get());
831
    }
832
    return new TField("", type, id);
833
  }
834
 
835
  @Override
836
  public void readFieldEnd() throws TException {
837
    readJSONObjectEnd();
838
  }
839
 
840
  @Override
841
  public TMap readMapBegin() throws TException {
842
    readJSONArrayStart();
843
    byte keyType = getTypeIDForTypeName(readJSONString(false).get());
844
    byte valueType = getTypeIDForTypeName(readJSONString(false).get());
845
    int size = (int)readJSONInteger();
846
    readJSONObjectStart();
847
    return new TMap(keyType, valueType, size);
848
  }
849
 
850
  @Override
851
  public void readMapEnd() throws TException {
852
    readJSONObjectEnd();
853
    readJSONArrayEnd();
854
  }
855
 
856
  @Override
857
  public TList readListBegin() throws TException {
858
    readJSONArrayStart();
859
    byte elemType = getTypeIDForTypeName(readJSONString(false).get());
860
    int size = (int)readJSONInteger();
861
    return new TList(elemType, size);
862
  }
863
 
864
  @Override
865
  public void readListEnd() throws TException {
866
    readJSONArrayEnd();
867
  }
868
 
869
  @Override
870
  public TSet readSetBegin() throws TException {
871
    readJSONArrayStart();
872
    byte elemType = getTypeIDForTypeName(readJSONString(false).get());
873
    int size = (int)readJSONInteger();
874
    return new TSet(elemType, size);
875
  }
876
 
877
  @Override
878
  public void readSetEnd() throws TException {
879
    readJSONArrayEnd();
880
  }
881
 
882
  @Override
883
  public boolean readBool() throws TException {
884
    return (readJSONInteger() == 0 ? false : true);
885
  }
886
 
887
  @Override
888
  public byte readByte() throws TException {
889
    return (byte) readJSONInteger();
890
  }
891
 
892
  @Override
893
  public short readI16() throws TException {
894
    return (short) readJSONInteger();
895
  }
896
 
897
  @Override
898
  public int readI32() throws TException {
899
    return (int) readJSONInteger();
900
  }
901
 
902
  @Override
903
  public long readI64() throws TException {
904
    return (long) readJSONInteger();
905
  }
906
 
907
  @Override
908
  public double readDouble() throws TException {
909
    return readJSONDouble();
910
  }
911
 
912
  @Override
913
  public String readString() throws TException {
914
    try {
915
      return readJSONString(false).toString("UTF-8");
916
    }
917
    catch (UnsupportedEncodingException ex) {
918
      throw new TException("JVM DOES NOT SUPPORT UTF-8");
919
    }
920
  }
921
 
922
  @Override
923
  public byte[] readBinary() throws TException {
924
    return readJSONBase64();
925
  }
926
 
927
}