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