blob: 8cb3b6995bcdc457c7f48a828a7f548342ea8aed [file] [log] [blame]
Mark Slee844ac122007-11-27 08:38:52 +00001// Copyright (c) 2006- Facebook
2// Distributed under the Thrift Software License
3//
4// See accompanying file LICENSE or visit the Thrift site at:
5// http://developers.facebook.com/thrift/
6
7package com.facebook.thrift.protocol;
8
Mark Sleed602f822007-11-28 02:45:13 +00009import com.facebook.thrift.TByteArrayOutputStream;
Mark Slee844ac122007-11-27 08:38:52 +000010import com.facebook.thrift.TException;
11import com.facebook.thrift.transport.TTransport;
Mark Sleed602f822007-11-28 02:45:13 +000012import java.io.ByteArrayOutputStream;
Mark Slee844ac122007-11-27 08:38:52 +000013import java.io.UnsupportedEncodingException;
14import java.util.Stack;
15
16/**
17 * JSON protocol implementation for thrift.
18 *
David Reiss132cc462008-02-21 22:49:58 +000019 * This protocol is write-only and produces a simple output format
20 * suitable for parsing by scripting languages. It should not be
21 * confused with the full-featured TJSONProtocol.
22 *
Mark Slee844ac122007-11-27 08:38:52 +000023 * @author Joydeep Sen Sarma <jssarma@facebook.com>
24 * @author Mark Slee <mcslee@facebook.com>
25 */
David Reiss132cc462008-02-21 22:49:58 +000026public class TSimpleJSONProtocol extends TProtocol {
Mark Slee844ac122007-11-27 08:38:52 +000027
28 /**
29 * Factory
30 */
31 public static class Factory implements TProtocolFactory {
32 public TProtocol getProtocol(TTransport trans) {
David Reiss132cc462008-02-21 22:49:58 +000033 return new TSimpleJSONProtocol(trans);
Mark Slee844ac122007-11-27 08:38:52 +000034 }
35 }
36
37 public static final byte[] COMMA = new byte[] {','};
38 public static final byte[] COLON = new byte[] {':'};
Mark Slee844ac122007-11-27 08:38:52 +000039 public static final byte[] LBRACE = new byte[] {'{'};
40 public static final byte[] RBRACE = new byte[] {'}'};
41 public static final byte[] LBRACKET = new byte[] {'['};
42 public static final byte[] RBRACKET = new byte[] {']'};
Mark Sleed602f822007-11-28 02:45:13 +000043 public static final char QUOTE = '"';
Mark Slee844ac122007-11-27 08:38:52 +000044
45 protected class Context {
46 protected void write() throws TException {}
47 }
48
49 protected class ListContext extends Context {
50 protected boolean first_ = true;
51
52 protected void write() throws TException {
53 if (first_) {
54 first_ = false;
55 } else {
56 trans_.write(COMMA);
57 }
58 }
59 }
60
61 protected class StructContext extends Context {
62 protected boolean first_ = true;
63 protected boolean colon_ = true;
64
65 protected void write() throws TException {
66 if (first_) {
67 first_ = false;
68 colon_ = true;
69 } else {
70 trans_.write(colon_ ? COLON : COMMA);
71 colon_ = !colon_;
72 }
73 }
74 }
75
76 protected final Context BASE_CONTEXT = new Context();
77
78 /**
79 * Stack of nested contexts that we may be in.
80 */
81 protected Stack<Context> writeContextStack_ = new Stack<Context>();
82
83 /**
84 * Current context that we are in
85 */
86 protected Context writeContext_ = BASE_CONTEXT;
87
88 /**
89 * Push a new write context onto the stack.
90 */
91 protected void pushWriteContext(Context c) {
92 writeContextStack_.push(writeContext_);
93 writeContext_ = c;
94 }
95
96 /**
97 * Pop the last write context off the stack
98 */
99 protected void popWriteContext() {
100 writeContext_ = writeContextStack_.pop();
101 }
102
103 /**
104 * Constructor
105 */
David Reiss132cc462008-02-21 22:49:58 +0000106 public TSimpleJSONProtocol(TTransport trans) {
Mark Slee844ac122007-11-27 08:38:52 +0000107 super(trans);
108 }
109
110 public void writeMessageBegin(TMessage message) throws TException {
111 trans_.write(LBRACKET);
112 pushWriteContext(new ListContext());
113 writeString(message.name);
114 writeByte(message.type);
115 writeI32(message.seqid);
116 }
117
118 public void writeMessageEnd() throws TException {
119 popWriteContext();
120 trans_.write(RBRACKET);
121 }
122
123 public void writeStructBegin(TStruct struct) throws TException {
124 writeContext_.write();
125 trans_.write(LBRACE);
126 pushWriteContext(new StructContext());
127 }
128
129 public void writeStructEnd() throws TException {
130 popWriteContext();
131 trans_.write(RBRACE);
132 }
133
134 public void writeFieldBegin(TField field) throws TException {
135 // Note that extra type information is omitted in JSON!
136 writeString(field.name);
137 }
138
139 public void writeFieldEnd() {}
140
141 public void writeFieldStop() {}
142
143 public void writeMapBegin(TMap map) throws TException {
144 writeContext_.write();
145 trans_.write(LBRACE);
146 pushWriteContext(new StructContext());
147 // No metadata!
148 }
149
150 public void writeMapEnd() throws TException {
151 popWriteContext();
152 trans_.write(RBRACE);
153 }
154
155 public void writeListBegin(TList list) throws TException {
156 writeContext_.write();
157 trans_.write(LBRACKET);
158 pushWriteContext(new ListContext());
159 // No metadata!
160 }
161
162 public void writeListEnd() throws TException {
163 popWriteContext();
164 trans_.write(RBRACKET);
165 }
166
167 public void writeSetBegin(TSet set) throws TException {
168 writeContext_.write();
169 trans_.write(LBRACKET);
170 pushWriteContext(new ListContext());
171 // No metadata!
172 }
173
174 public void writeSetEnd() throws TException {
175 popWriteContext();
176 trans_.write(RBRACKET);
177 }
178
179 public void writeBool(boolean b) throws TException {
180 writeByte(b ? (byte)1 : (byte)0);
181 }
182
183 public void writeByte(byte b) throws TException {
184 writeI32(b);
185 }
186
187 public void writeI16(short i16) throws TException {
188 writeI32(i16);
189 }
190
191 public void writeI32(int i32) throws TException {
192 writeContext_.write();
193 _writeStringData(Integer.toString(i32));
194 }
195
196 public void _writeStringData(String s) throws TException {
197 try {
198 byte[] b = s.getBytes("UTF-8");
199 trans_.write(b);
200 } catch (UnsupportedEncodingException uex) {
201 throw new TException("JVM DOES NOT SUPPORT UTF-8");
202 }
203 }
204
205 public void writeI64(long i64) throws TException {
206 writeContext_.write();
207 _writeStringData(Long.toString(i64));
208 }
209
210 public void writeDouble(double dub) throws TException {
211 writeContext_.write();
212 _writeStringData(Double.toString(dub));
213 }
214
215 public void writeString(String str) throws TException {
216 writeContext_.write();
Mark Slee844ac122007-11-27 08:38:52 +0000217 int length = str.length();
Mark Sleed602f822007-11-28 02:45:13 +0000218 StringBuffer escape = new StringBuffer(length + 16);
219 escape.append(QUOTE);
Mark Slee844ac122007-11-27 08:38:52 +0000220 for (int i = 0; i < length; ++i) {
Mark Sleed602f822007-11-28 02:45:13 +0000221 char c = str.charAt(i);
Mark Slee844ac122007-11-27 08:38:52 +0000222 switch (c) {
223 case '"':
224 case '\\':
225 escape.append('\\');
226 escape.append(c);
227 break;
Mark Sleed602f822007-11-28 02:45:13 +0000228 case '\b':
229 escape.append('\\');
230 escape.append('b');
231 break;
232 case '\f':
233 escape.append('\\');
234 escape.append('f');
235 break;
236 case '\n':
237 escape.append('\\');
238 escape.append('n');
239 break;
240 case '\r':
241 escape.append('\\');
242 escape.append('r');
243 break;
244 case '\t':
245 escape.append('\\');
246 escape.append('t');
247 break;
Mark Slee844ac122007-11-27 08:38:52 +0000248 default:
Mark Sleed602f822007-11-28 02:45:13 +0000249 // Control characeters! According to JSON RFC u0020 (space)
250 if (c < ' ') {
251 String hex = Integer.toHexString(c);
252 escape.append('\\');
253 escape.append('u');
254 for (int j = 4; j > hex.length(); --j) {
255 escape.append('0');
256 }
257 escape.append(hex);
258 } else {
259 escape.append(c);
260 }
Mark Slee844ac122007-11-27 08:38:52 +0000261 break;
262 }
263 }
Mark Sleed602f822007-11-28 02:45:13 +0000264 escape.append(QUOTE);
Mark Slee844ac122007-11-27 08:38:52 +0000265 _writeStringData(escape.toString());
Mark Slee844ac122007-11-27 08:38:52 +0000266 }
267
268 public void writeBinary(byte[] bin) throws TException {
269 try {
270 // TODO(mcslee): Fix this
271 writeString(new String(bin, "UTF-8"));
272 } catch (UnsupportedEncodingException uex) {
273 throw new TException("JVM DOES NOT SUPPORT UTF-8");
274 }
275 }
276
277 /**
278 * Reading methods.
279 */
280
281 public TMessage readMessageBegin() throws TException {
282 TMessage message = new TMessage();
283 // TODO(mcslee): implement
284 return message;
285 }
286
287 public void readMessageEnd() {}
288
289 public TStruct readStructBegin() {
290 // TODO(mcslee): implement
291 return new TStruct();
292 }
293
294 public void readStructEnd() {}
295
296 public TField readFieldBegin() throws TException {
297 TField field = new TField();
298 // TODO(mcslee): implement
299 return field;
300 }
301
302 public void readFieldEnd() {}
303
304 public TMap readMapBegin() throws TException {
305 TMap map = new TMap();
306 // TODO(mcslee): implement
307 return map;
308 }
309
310 public void readMapEnd() {}
311
312 public TList readListBegin() throws TException {
313 TList list = new TList();
314 // TODO(mcslee): implement
315 return list;
316 }
317
318 public void readListEnd() {}
319
320 public TSet readSetBegin() throws TException {
321 TSet set = new TSet();
322 // TODO(mcslee): implement
323 return set;
324 }
325
326 public void readSetEnd() {}
327
328 public boolean readBool() throws TException {
329 return (readByte() == 1);
330 }
331
332 public byte readByte() throws TException {
333 // TODO(mcslee): implement
334 return 0;
335 }
336
337 public short readI16() throws TException {
338 // TODO(mcslee): implement
339 return 0;
340 }
341
342 public int readI32() throws TException {
343 // TODO(mcslee): implement
344 return 0;
345 }
346
347 public long readI64() throws TException {
348 // TODO(mcslee): implement
349 return 0;
350 }
351
352 public double readDouble() throws TException {
353 // TODO(mcslee): implement
354 return 0;
355 }
356
357 public String readString() throws TException {
358 // TODO(mcslee): implement
359 return "";
360 }
361
362 public String readStringBody(int size) throws TException {
363 // TODO(mcslee): implement
364 return "";
365 }
366
367 public byte[] readBinary() throws TException {
368 // TODO(mcslee): implement
369 return new byte[0];
370 }
371
372}