~ubuntu-branches/ubuntu/trusty/sugar-jigsawpuzzle-activity/trusty-proposed

« back to all changes in this revision

Viewing changes to mmm_modules/json.py

  • Committer: Bazaar Package Importer
  • Author(s): Luke Faraone
  • Date: 2010-07-29 17:47:23 UTC
  • Revision ID: james.westby@ubuntu.com-20100729174723-5gvrue5dnddqoou0
Tags: upstream-8
ImportĀ upstreamĀ versionĀ 8

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import string
 
2
import types
 
3
 
 
4
##    json.py implements a JSON (http://json.org) reader and writer.
 
5
##    Copyright (C) 2005  Patrick D. Logan
 
6
##    Contact mailto:patrickdlogan@stardecisions.com
 
7
##
 
8
##    This library is free software; you can redistribute it and/or
 
9
##    modify it under the terms of the GNU Lesser General Public
 
10
##    License as published by the Free Software Foundation; either
 
11
##    version 2.1 of the License, or (at your option) any later version.
 
12
##
 
13
##    This library is distributed in the hope that it will be useful,
 
14
##    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
##    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
16
##    Lesser General Public License for more details.
 
17
##
 
18
##    You should have received a copy of the GNU Lesser General Public
 
19
##    License along with this library; if not, write to the Free Software
 
20
##    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
21
 
 
22
 
 
23
class _StringGenerator(object):
 
24
        def __init__(self, string):
 
25
                self.string = string
 
26
                self.index = -1
 
27
        def peek(self):
 
28
                i = self.index + 1
 
29
                if i < len(self.string):
 
30
                        return self.string[i]
 
31
                else:
 
32
                        return None
 
33
        def next(self):
 
34
                self.index += 1
 
35
                if self.index < len(self.string):
 
36
                        return self.string[self.index]
 
37
                else:
 
38
                        raise StopIteration
 
39
        def all(self):
 
40
                return self.string
 
41
 
 
42
class WriteException(Exception):
 
43
    pass
 
44
 
 
45
class ReadException(Exception):
 
46
    pass
 
47
 
 
48
class JsonReader(object):
 
49
    hex_digits = {'A': 10,'B': 11,'C': 12,'D': 13,'E': 14,'F':15}
 
50
    escapes = {'t':'\t','n':'\n','f':'\f','r':'\r','b':'\b'}
 
51
 
 
52
    def read(self, s):
 
53
        self._generator = _StringGenerator(s)
 
54
        result = self._read()
 
55
        return result
 
56
 
 
57
    def _read(self):
 
58
        self._eatWhitespace()
 
59
        peek = self._peek()
 
60
        if peek is None:
 
61
            raise ReadException, "Nothing to read: '%s'" % self._generator.all()
 
62
        if peek == '{':
 
63
            return self._readObject()
 
64
        elif peek == '[':
 
65
            return self._readArray()            
 
66
        elif peek == '"':
 
67
            return self._readString()
 
68
        elif peek == '-' or peek.isdigit():
 
69
            return self._readNumber()
 
70
        elif peek == 't':
 
71
            return self._readTrue()
 
72
        elif peek == 'f':
 
73
            return self._readFalse()
 
74
        elif peek == 'n':
 
75
            return self._readNull()
 
76
        elif peek == '/':
 
77
            self._readComment()
 
78
            return self._read()
 
79
        else:
 
80
            raise ReadException, "Input is not valid JSON: '%s'" % self._generator.all()
 
81
 
 
82
    def _readTrue(self):
 
83
        self._assertNext('t', "true")
 
84
        self._assertNext('r', "true")
 
85
        self._assertNext('u', "true")
 
86
        self._assertNext('e', "true")
 
87
        return True
 
88
 
 
89
    def _readFalse(self):
 
90
        self._assertNext('f', "false")
 
91
        self._assertNext('a', "false")
 
92
        self._assertNext('l', "false")
 
93
        self._assertNext('s', "false")
 
94
        self._assertNext('e', "false")
 
95
        return False
 
96
 
 
97
    def _readNull(self):
 
98
        self._assertNext('n', "null")
 
99
        self._assertNext('u', "null")
 
100
        self._assertNext('l', "null")
 
101
        self._assertNext('l', "null")
 
102
        return None
 
103
 
 
104
    def _assertNext(self, ch, target):
 
105
        if self._next() != ch:
 
106
            raise ReadException, "Trying to read %s: '%s'" % (target, self._generator.all())
 
107
 
 
108
    def _readNumber(self):
 
109
        isfloat = False
 
110
        result = self._next()
 
111
        peek = self._peek()
 
112
        while peek is not None and (peek.isdigit() or peek == "."):
 
113
            isfloat = isfloat or peek == "."
 
114
            result = result + self._next()
 
115
            peek = self._peek()
 
116
        try:
 
117
            if isfloat:
 
118
                return float(result)
 
119
            else:
 
120
                return int(result)
 
121
        except ValueError:
 
122
            raise ReadException, "Not a valid JSON number: '%s'" % result
 
123
 
 
124
    def _readString(self):
 
125
        result = ""
 
126
        assert self._next() == '"'
 
127
        try:
 
128
            while self._peek() != '"':
 
129
                ch = self._next()
 
130
                if ch == "\\":
 
131
                    ch = self._next()
 
132
                    if ch in 'brnft':
 
133
                        ch = self.escapes[ch]
 
134
                    elif ch == "u":
 
135
                        ch4096 = self._next()
 
136
                        ch256  = self._next()
 
137
                        ch16   = self._next()
 
138
                        ch1    = self._next()
 
139
                        n = 4096 * self._hexDigitToInt(ch4096)
 
140
                        n += 256 * self._hexDigitToInt(ch256)
 
141
                        n += 16  * self._hexDigitToInt(ch16)
 
142
                        n += self._hexDigitToInt(ch1)
 
143
                        ch = unichr(n)
 
144
                    elif ch not in '"/\\':
 
145
                        raise ReadException, "Not a valid escaped JSON character: '%s' in %s" % (ch, self._generator.all())
 
146
                result = result + ch
 
147
        except StopIteration:
 
148
            raise ReadException, "Not a valid JSON string: '%s'" % self._generator.all()
 
149
        assert self._next() == '"'
 
150
        return result
 
151
 
 
152
    def _hexDigitToInt(self, ch):
 
153
        try:
 
154
            result = self.hex_digits[ch.upper()]
 
155
        except KeyError:
 
156
            try:
 
157
                result = int(ch)
 
158
            except ValueError:
 
159
                 raise ReadException, "The character %s is not a hex digit." % ch
 
160
        return result
 
161
 
 
162
    def _readComment(self):
 
163
        assert self._next() == "/"
 
164
        second = self._next()
 
165
        if second == "/":
 
166
            self._readDoubleSolidusComment()
 
167
        elif second == '*':
 
168
            self._readCStyleComment()
 
169
        else:
 
170
            raise ReadException, "Not a valid JSON comment: %s" % self._generator.all()
 
171
 
 
172
    def _readCStyleComment(self):
 
173
        try:
 
174
            done = False
 
175
            while not done:
 
176
                ch = self._next()
 
177
                done = (ch == "*" and self._peek() == "/")
 
178
                if not done and ch == "/" and self._peek() == "*":
 
179
                    raise ReadException, "Not a valid JSON comment: %s, '/*' cannot be embedded in the comment." % self._generator.all()
 
180
            self._next()
 
181
        except StopIteration:
 
182
            raise ReadException, "Not a valid JSON comment: %s, expected */" % self._generator.all()
 
183
 
 
184
    def _readDoubleSolidusComment(self):
 
185
        try:
 
186
            ch = self._next()
 
187
            while ch != "\r" and ch != "\n":
 
188
                ch = self._next()
 
189
        except StopIteration:
 
190
            pass
 
191
 
 
192
    def _readArray(self):
 
193
        result = []
 
194
        assert self._next() == '['
 
195
        done = self._peek() == ']'
 
196
        while not done:
 
197
            item = self._read()
 
198
            result.append(item)
 
199
            self._eatWhitespace()
 
200
            done = self._peek() == ']'
 
201
            if not done:
 
202
                ch = self._next()
 
203
                if ch != ",":
 
204
                    raise ReadException, "Not a valid JSON array: '%s' due to: '%s'" % (self._generator.all(), ch)
 
205
        assert ']' == self._next()
 
206
        return result
 
207
 
 
208
    def _readObject(self):
 
209
        result = {}
 
210
        assert self._next() == '{'
 
211
        done = self._peek() == '}'
 
212
        while not done:
 
213
            key = self._read()
 
214
            if type(key) is not types.StringType:
 
215
                raise ReadException, "Not a valid JSON object key (should be a string): %s" % key
 
216
            self._eatWhitespace()
 
217
            ch = self._next()
 
218
            if ch != ":":
 
219
                raise ReadException, "Not a valid JSON object: '%s' due to: '%s'" % (self._generator.all(), ch)
 
220
            self._eatWhitespace()
 
221
            val = self._read()
 
222
            result[key] = val
 
223
            self._eatWhitespace()
 
224
            done = self._peek() == '}'
 
225
            if not done:
 
226
                ch = self._next()
 
227
                if ch != ",":
 
228
                    raise ReadException, "Not a valid JSON array: '%s' due to: '%s'" % (self._generator.all(), ch)
 
229
        assert self._next() == "}"
 
230
        return result
 
231
 
 
232
    def _eatWhitespace(self):
 
233
        p = self._peek()
 
234
        while p is not None and p in string.whitespace or p == '/':
 
235
            if p == '/':
 
236
                self._readComment()
 
237
            else:
 
238
                self._next()
 
239
            p = self._peek()
 
240
 
 
241
    def _peek(self):
 
242
        return self._generator.peek()
 
243
 
 
244
    def _next(self):
 
245
        return self._generator.next()
 
246
 
 
247
class JsonWriter(object):
 
248
        
 
249
    def _append(self, s):
 
250
        self._results.append(s)
 
251
 
 
252
    def write(self, obj, escaped_forward_slash=False):
 
253
        self._escaped_forward_slash = escaped_forward_slash
 
254
        self._results = []
 
255
        self._write(obj)
 
256
        return "".join(self._results)
 
257
 
 
258
    def _write(self, obj):
 
259
        ty = type(obj)
 
260
        if ty is types.DictType:
 
261
            n = len(obj)
 
262
            self._append("{")
 
263
            for k, v in obj.items():
 
264
                self._write(k)
 
265
                self._append(":")
 
266
                self._write(v)
 
267
                n = n - 1
 
268
                if n > 0:
 
269
                    self._append(",")
 
270
            self._append("}")
 
271
        elif ty is types.ListType or ty is types.TupleType:
 
272
            n = len(obj)
 
273
            self._append("[")
 
274
            for item in obj:
 
275
                self._write(item)
 
276
                n = n - 1
 
277
                if n > 0:
 
278
                    self._append(",")
 
279
            self._append("]")
 
280
        elif ty is types.StringType or ty is types.UnicodeType:
 
281
            self._append('"')
 
282
            obj = obj.replace('\\', r'\\')
 
283
            if self._escaped_forward_slash:
 
284
                obj = obj.replace('/', r'\/')
 
285
            obj = obj.replace('"', r'\"')
 
286
            obj = obj.replace('\b', r'\b')
 
287
            obj = obj.replace('\f', r'\f')
 
288
            obj = obj.replace('\n', r'\n')
 
289
            obj = obj.replace('\r', r'\r')
 
290
            obj = obj.replace('\t', r'\t')
 
291
            self._append(obj)
 
292
            self._append('"')
 
293
        elif ty is types.IntType or ty is types.LongType:
 
294
            self._append(str(obj))
 
295
        elif ty is types.FloatType:
 
296
            self._append("%f" % obj)
 
297
        elif obj is True:
 
298
            self._append("true")
 
299
        elif obj is False:
 
300
            self._append("false")
 
301
        elif obj is None:
 
302
            self._append("null")
 
303
        else:
 
304
            raise WriteException, "Cannot write in JSON: %s" % repr(obj)
 
305
 
 
306
def write(obj, escaped_forward_slash=False):
 
307
    return JsonWriter().write(obj, escaped_forward_slash)
 
308
 
 
309
def read(s):
 
310
    return JsonReader().read(s)