Merge "Example as code, documentation template for sphinx build"
[domino.git] / lib / thrift / protocol / TJSONProtocol.py
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 from TProtocol import TType, TProtocolBase, TProtocolException, \
21     checkIntegerLimits
22 import base64
23 import json
24 import math
25
26 __all__ = ['TJSONProtocol',
27            'TJSONProtocolFactory',
28            'TSimpleJSONProtocol',
29            'TSimpleJSONProtocolFactory']
30
31 VERSION = 1
32
33 COMMA = ','
34 COLON = ':'
35 LBRACE = '{'
36 RBRACE = '}'
37 LBRACKET = '['
38 RBRACKET = ']'
39 QUOTE = '"'
40 BACKSLASH = '\\'
41 ZERO = '0'
42
43 ESCSEQ = '\\u00'
44 ESCAPE_CHAR = '"\\bfnrt'
45 ESCAPE_CHAR_VALS = ['"', '\\', '\b', '\f', '\n', '\r', '\t']
46 NUMERIC_CHAR = '+-.0123456789Ee'
47
48 CTYPES = {TType.BOOL:       'tf',
49           TType.BYTE:       'i8',
50           TType.I16:        'i16',
51           TType.I32:        'i32',
52           TType.I64:        'i64',
53           TType.DOUBLE:     'dbl',
54           TType.STRING:     'str',
55           TType.STRUCT:     'rec',
56           TType.LIST:       'lst',
57           TType.SET:        'set',
58           TType.MAP:        'map'}
59
60 JTYPES = {}
61 for key in CTYPES.keys():
62   JTYPES[CTYPES[key]] = key
63
64
65 class JSONBaseContext(object):
66
67   def __init__(self, protocol):
68     self.protocol = protocol
69     self.first = True
70
71   def doIO(self, function):
72     pass
73   
74   def write(self):
75     pass
76
77   def read(self):
78     pass
79
80   def escapeNum(self):
81     return False
82
83   def __str__(self):
84     return self.__class__.__name__
85
86
87 class JSONListContext(JSONBaseContext):
88     
89   def doIO(self, function):
90     if self.first is True:
91       self.first = False
92     else:
93       function(COMMA)
94
95   def write(self):
96     self.doIO(self.protocol.trans.write)
97
98   def read(self):
99     self.doIO(self.protocol.readJSONSyntaxChar)
100
101
102 class JSONPairContext(JSONBaseContext):
103   
104   def __init__(self, protocol):
105     super(JSONPairContext, self).__init__(protocol)
106     self.colon = True
107
108   def doIO(self, function):
109     if self.first:
110       self.first = False
111       self.colon = True
112     else:
113       function(COLON if self.colon else COMMA)
114       self.colon = not self.colon
115
116   def write(self):
117     self.doIO(self.protocol.trans.write)
118
119   def read(self):
120     self.doIO(self.protocol.readJSONSyntaxChar)
121
122   def escapeNum(self):
123     return self.colon
124
125   def __str__(self):
126     return '%s, colon=%s' % (self.__class__.__name__, self.colon)
127
128
129 class LookaheadReader():
130   hasData = False
131   data = ''
132
133   def __init__(self, protocol):
134     self.protocol = protocol
135
136   def read(self):
137     if self.hasData is True:
138       self.hasData = False
139     else:
140       self.data = self.protocol.trans.read(1)
141     return self.data
142
143   def peek(self):
144     if self.hasData is False:
145       self.data = self.protocol.trans.read(1)
146     self.hasData = True
147     return self.data
148
149 class TJSONProtocolBase(TProtocolBase):
150
151   def __init__(self, trans):
152     TProtocolBase.__init__(self, trans)
153     self.resetWriteContext()
154     self.resetReadContext()
155
156   def resetWriteContext(self):
157     self.context = JSONBaseContext(self)
158     self.contextStack = [self.context]
159
160   def resetReadContext(self):
161     self.resetWriteContext()
162     self.reader = LookaheadReader(self)
163
164   def pushContext(self, ctx):
165     self.contextStack.append(ctx)
166     self.context = ctx
167
168   def popContext(self):
169     self.contextStack.pop()
170     if self.contextStack:
171       self.context = self.contextStack[-1]
172     else:
173       self.context = JSONBaseContext(self)
174
175   def writeJSONString(self, string):
176     self.context.write()
177     self.trans.write(json.dumps(string))
178
179   def writeJSONNumber(self, number):
180     self.context.write()
181     jsNumber = str(number)
182     if self.context.escapeNum():
183       jsNumber = "%s%s%s" % (QUOTE, jsNumber,  QUOTE)
184     self.trans.write(jsNumber)
185
186   def writeJSONBase64(self, binary):
187     self.context.write()
188     self.trans.write(QUOTE)
189     self.trans.write(base64.b64encode(binary))
190     self.trans.write(QUOTE)
191
192   def writeJSONObjectStart(self):
193     self.context.write()
194     self.trans.write(LBRACE)
195     self.pushContext(JSONPairContext(self))
196
197   def writeJSONObjectEnd(self):
198     self.popContext()
199     self.trans.write(RBRACE)
200
201   def writeJSONArrayStart(self):
202     self.context.write()
203     self.trans.write(LBRACKET)
204     self.pushContext(JSONListContext(self))
205
206   def writeJSONArrayEnd(self):
207     self.popContext()
208     self.trans.write(RBRACKET)
209
210   def readJSONSyntaxChar(self, character):
211     current = self.reader.read()
212     if character != current:
213       raise TProtocolException(TProtocolException.INVALID_DATA,
214                                "Unexpected character: %s" % current)
215
216   def readJSONString(self, skipContext):
217     string = []
218     if skipContext is False:
219       self.context.read()
220     self.readJSONSyntaxChar(QUOTE)
221     while True:
222       character = self.reader.read()
223       if character == QUOTE:
224         break
225       if character == ESCSEQ[0]:
226         character = self.reader.read()
227         if character == ESCSEQ[1]:
228           self.readJSONSyntaxChar(ZERO)
229           self.readJSONSyntaxChar(ZERO)
230           character = json.JSONDecoder().decode('"\u00%s"' % self.trans.read(2))
231         else:
232           off = ESCAPE_CHAR.find(character)
233           if off == -1:
234             raise TProtocolException(TProtocolException.INVALID_DATA,
235                                      "Expected control char")
236           character = ESCAPE_CHAR_VALS[off]
237       string.append(character)
238     return ''.join(string)
239
240   def isJSONNumeric(self, character):
241     return (True if NUMERIC_CHAR.find(character) != - 1 else False)
242
243   def readJSONQuotes(self):
244     if (self.context.escapeNum()):
245       self.readJSONSyntaxChar(QUOTE)
246
247   def readJSONNumericChars(self):
248     numeric = []
249     while True:
250       character = self.reader.peek()
251       if self.isJSONNumeric(character) is False:
252         break
253       numeric.append(self.reader.read())
254     return ''.join(numeric)
255
256   def readJSONInteger(self):
257     self.context.read()
258     self.readJSONQuotes()
259     numeric = self.readJSONNumericChars()
260     self.readJSONQuotes()
261     try:
262       return int(numeric)
263     except ValueError:
264       raise TProtocolException(TProtocolException.INVALID_DATA,
265                                "Bad data encounted in numeric data")
266
267   def readJSONDouble(self):
268     self.context.read()
269     if self.reader.peek() == QUOTE:
270       string  = self.readJSONString(True)
271       try:
272         double = float(string)
273         if (self.context.escapeNum is False and
274             not math.isinf(double) and
275             not math.isnan(double)):
276           raise TProtocolException(TProtocolException.INVALID_DATA,
277                                    "Numeric data unexpectedly quoted")
278         return double
279       except ValueError:
280         raise TProtocolException(TProtocolException.INVALID_DATA,
281                                  "Bad data encounted in numeric data")
282     else:
283       if self.context.escapeNum() is True:
284         self.readJSONSyntaxChar(QUOTE)
285       try:
286         return float(self.readJSONNumericChars())
287       except ValueError:
288         raise TProtocolException(TProtocolException.INVALID_DATA,
289                                  "Bad data encounted in numeric data")
290
291   def readJSONBase64(self):
292     string = self.readJSONString(False)
293     return base64.b64decode(string)
294
295   def readJSONObjectStart(self):
296     self.context.read()
297     self.readJSONSyntaxChar(LBRACE)
298     self.pushContext(JSONPairContext(self))
299
300   def readJSONObjectEnd(self):
301     self.readJSONSyntaxChar(RBRACE)
302     self.popContext()
303
304   def readJSONArrayStart(self):
305     self.context.read()
306     self.readJSONSyntaxChar(LBRACKET)
307     self.pushContext(JSONListContext(self))
308
309   def readJSONArrayEnd(self):
310     self.readJSONSyntaxChar(RBRACKET)
311     self.popContext()
312
313
314 class TJSONProtocol(TJSONProtocolBase):
315
316   def readMessageBegin(self):
317     self.resetReadContext()
318     self.readJSONArrayStart()
319     if self.readJSONInteger() != VERSION:
320       raise TProtocolException(TProtocolException.BAD_VERSION,
321                                "Message contained bad version.")
322     name = self.readJSONString(False)
323     typen = self.readJSONInteger()
324     seqid = self.readJSONInteger()
325     return (name, typen, seqid)
326
327   def readMessageEnd(self):
328     self.readJSONArrayEnd()
329
330   def readStructBegin(self):
331     self.readJSONObjectStart()
332
333   def readStructEnd(self):
334     self.readJSONObjectEnd()
335
336   def readFieldBegin(self):
337     character = self.reader.peek()
338     ttype = 0
339     id = 0
340     if character == RBRACE:
341       ttype = TType.STOP
342     else:
343       id = self.readJSONInteger()
344       self.readJSONObjectStart()
345       ttype = JTYPES[self.readJSONString(False)]
346     return (None, ttype, id)
347
348   def readFieldEnd(self):
349     self.readJSONObjectEnd()
350
351   def readMapBegin(self):
352     self.readJSONArrayStart()
353     keyType = JTYPES[self.readJSONString(False)]
354     valueType = JTYPES[self.readJSONString(False)]
355     size = self.readJSONInteger()
356     self.readJSONObjectStart()
357     return (keyType, valueType, size)
358
359   def readMapEnd(self):
360     self.readJSONObjectEnd()
361     self.readJSONArrayEnd()
362
363   def readCollectionBegin(self):
364     self.readJSONArrayStart()
365     elemType = JTYPES[self.readJSONString(False)]
366     size = self.readJSONInteger()
367     return (elemType, size)
368   readListBegin = readCollectionBegin
369   readSetBegin = readCollectionBegin
370
371   def readCollectionEnd(self):
372     self.readJSONArrayEnd()
373   readSetEnd = readCollectionEnd
374   readListEnd = readCollectionEnd
375
376   def readBool(self):
377     return (False if self.readJSONInteger() == 0 else True)
378
379   def readNumber(self):
380     return self.readJSONInteger()
381   readByte = readNumber
382   readI16 = readNumber
383   readI32 = readNumber
384   readI64 = readNumber
385
386   def readDouble(self):
387     return self.readJSONDouble()
388
389   def readString(self):
390     return self.readJSONString(False)
391
392   def readBinary(self):
393     return self.readJSONBase64()
394
395   def writeMessageBegin(self, name, request_type, seqid):
396     self.resetWriteContext()
397     self.writeJSONArrayStart()
398     self.writeJSONNumber(VERSION)
399     self.writeJSONString(name)
400     self.writeJSONNumber(request_type)
401     self.writeJSONNumber(seqid)
402
403   def writeMessageEnd(self):
404     self.writeJSONArrayEnd()
405
406   def writeStructBegin(self, name):
407     self.writeJSONObjectStart()
408
409   def writeStructEnd(self):
410     self.writeJSONObjectEnd()
411
412   def writeFieldBegin(self, name, ttype, id):
413     self.writeJSONNumber(id)
414     self.writeJSONObjectStart()
415     self.writeJSONString(CTYPES[ttype])
416
417   def writeFieldEnd(self):
418     self.writeJSONObjectEnd()
419
420   def writeFieldStop(self):
421     pass
422
423   def writeMapBegin(self, ktype, vtype, size):
424     self.writeJSONArrayStart()
425     self.writeJSONString(CTYPES[ktype])
426     self.writeJSONString(CTYPES[vtype])
427     self.writeJSONNumber(size)
428     self.writeJSONObjectStart()
429
430   def writeMapEnd(self):
431     self.writeJSONObjectEnd()
432     self.writeJSONArrayEnd()
433     
434   def writeListBegin(self, etype, size):
435     self.writeJSONArrayStart()
436     self.writeJSONString(CTYPES[etype])
437     self.writeJSONNumber(size)
438     
439   def writeListEnd(self):
440     self.writeJSONArrayEnd()
441
442   def writeSetBegin(self, etype, size):
443     self.writeJSONArrayStart()
444     self.writeJSONString(CTYPES[etype])
445     self.writeJSONNumber(size)
446     
447   def writeSetEnd(self):
448     self.writeJSONArrayEnd()
449
450   def writeBool(self, boolean):
451     self.writeJSONNumber(1 if boolean is True else 0)
452
453   def writeByte(self, byte):
454     checkIntegerLimits(byte, 8)
455     self.writeJSONNumber(byte)
456
457   def writeI16(self, i16):
458     checkIntegerLimits(i16, 16)
459     self.writeJSONNumber(i16)
460
461   def writeI32(self, i32):
462     checkIntegerLimits(i32, 32)
463     self.writeJSONNumber(i32)
464
465   def writeI64(self, i64):
466     checkIntegerLimits(i64, 64)
467     self.writeJSONNumber(i64)
468
469   def writeDouble(self, dbl):
470     self.writeJSONNumber(dbl)
471
472   def writeString(self, string):
473     self.writeJSONString(string)
474     
475   def writeBinary(self, binary):
476     self.writeJSONBase64(binary)
477
478
479 class TJSONProtocolFactory:
480
481   def getProtocol(self, trans):
482     return TJSONProtocol(trans)
483
484
485 class TSimpleJSONProtocol(TJSONProtocolBase):
486     """Simple, readable, write-only JSON protocol.
487     
488     Useful for interacting with scripting languages.
489     """
490
491     def readMessageBegin(self):
492         raise NotImplementedError()
493     
494     def readMessageEnd(self):
495         raise NotImplementedError()
496     
497     def readStructBegin(self):
498         raise NotImplementedError()
499     
500     def readStructEnd(self):
501         raise NotImplementedError()
502     
503     def writeMessageBegin(self, name, request_type, seqid):
504         self.resetWriteContext()
505     
506     def writeMessageEnd(self):
507         pass
508     
509     def writeStructBegin(self, name):
510         self.writeJSONObjectStart()
511     
512     def writeStructEnd(self):
513         self.writeJSONObjectEnd()
514       
515     def writeFieldBegin(self, name, ttype, fid):
516         self.writeJSONString(name)
517     
518     def writeFieldEnd(self):
519         pass
520     
521     def writeMapBegin(self, ktype, vtype, size):
522         self.writeJSONObjectStart()
523     
524     def writeMapEnd(self):
525         self.writeJSONObjectEnd()
526     
527     def _writeCollectionBegin(self, etype, size):
528         self.writeJSONArrayStart()
529     
530     def _writeCollectionEnd(self):
531         self.writeJSONArrayEnd()
532     writeListBegin = _writeCollectionBegin
533     writeListEnd = _writeCollectionEnd
534     writeSetBegin = _writeCollectionBegin
535     writeSetEnd = _writeCollectionEnd
536
537     def writeByte(self, byte):
538         checkIntegerLimits(byte, 8)
539         self.writeJSONNumber(byte)
540
541     def writeI16(self, i16):
542         checkIntegerLimits(i16, 16)
543         self.writeJSONNumber(i16)
544
545     def writeI32(self, i32):
546         checkIntegerLimits(i32, 32)
547         self.writeJSONNumber(i32)
548
549     def writeI64(self, i64):
550         checkIntegerLimits(i64, 64)
551         self.writeJSONNumber(i64)
552     
553     def writeBool(self, boolean):
554         self.writeJSONNumber(1 if boolean is True else 0)
555
556     def writeDouble(self, dbl):
557         self.writeJSONNumber(dbl)
558     
559     def writeString(self, string):
560         self.writeJSONString(string)
561       
562     def writeBinary(self, binary):
563         self.writeJSONBase64(binary)
564
565
566 class TSimpleJSONProtocolFactory(object):
567
568     def getProtocol(self, trans):
569         return TSimpleJSONProtocol(trans)