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