as3corelib 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. data/Gemfile +5 -0
  2. data/Gemfile.lock +28 -0
  3. data/README.textile +79 -0
  4. data/Rakefile +3 -0
  5. data/as3corelib.gemspec +23 -0
  6. data/build/build.properties +48 -0
  7. data/build/build.xml +104 -0
  8. data/examples/JSONExample/JSONExample.mxml +73 -0
  9. data/lib/as3corelib.rb +15 -0
  10. data/src/com/adobe/air/crypto/EncryptionKeyGenerator.as +313 -0
  11. data/src/com/adobe/air/filesystem/FileMonitor.as +245 -0
  12. data/src/com/adobe/air/filesystem/FileUtil.as +63 -0
  13. data/src/com/adobe/air/filesystem/VolumeMonitor.as +184 -0
  14. data/src/com/adobe/air/filesystem/events/FileMonitorEvent.as +61 -0
  15. data/src/com/adobe/air/logging/FileTarget.as +95 -0
  16. data/src/com/adobe/air/net/ResourceCache.as +165 -0
  17. data/src/com/adobe/air/net/events/ResourceCacheEvent.as +70 -0
  18. data/src/com/adobe/crypto/HMAC.as +127 -0
  19. data/src/com/adobe/crypto/MD5.as +281 -0
  20. data/src/com/adobe/crypto/MD5Stream.as +402 -0
  21. data/src/com/adobe/crypto/SHA1.as +289 -0
  22. data/src/com/adobe/crypto/SHA224.as +257 -0
  23. data/src/com/adobe/crypto/SHA256.as +261 -0
  24. data/src/com/adobe/crypto/WSSEUsernameToken.as +114 -0
  25. data/src/com/adobe/errors/IllegalStateError.as +63 -0
  26. data/src/com/adobe/fileformats/vcard/Address.as +47 -0
  27. data/src/com/adobe/fileformats/vcard/Email.as +39 -0
  28. data/src/com/adobe/fileformats/vcard/Phone.as +39 -0
  29. data/src/com/adobe/fileformats/vcard/VCard.as +54 -0
  30. data/src/com/adobe/fileformats/vcard/VCardParser.as +246 -0
  31. data/src/com/adobe/images/BitString.as +39 -0
  32. data/src/com/adobe/images/JPGEncoder.as +648 -0
  33. data/src/com/adobe/images/PNGEncoder.as +141 -0
  34. data/src/com/adobe/net/DynamicURLLoader.as +55 -0
  35. data/src/com/adobe/net/IURIResolver.as +76 -0
  36. data/src/com/adobe/net/MimeTypeMap.as +200 -0
  37. data/src/com/adobe/net/URI.as +2466 -0
  38. data/src/com/adobe/net/URIEncodingBitmap.as +139 -0
  39. data/src/com/adobe/net/proxies/RFC2817Socket.as +198 -0
  40. data/src/com/adobe/protocols/dict/Database.as +66 -0
  41. data/src/com/adobe/protocols/dict/Definition.as +71 -0
  42. data/src/com/adobe/protocols/dict/Dict.as +360 -0
  43. data/src/com/adobe/protocols/dict/DictionaryServer.as +60 -0
  44. data/src/com/adobe/protocols/dict/MatchStrategy.as +66 -0
  45. data/src/com/adobe/protocols/dict/Response.as +71 -0
  46. data/src/com/adobe/protocols/dict/events/ConnectedEvent.as +53 -0
  47. data/src/com/adobe/protocols/dict/events/DatabaseEvent.as +67 -0
  48. data/src/com/adobe/protocols/dict/events/DefinitionEvent.as +70 -0
  49. data/src/com/adobe/protocols/dict/events/DefinitionHeaderEvent.as +69 -0
  50. data/src/com/adobe/protocols/dict/events/DictionaryServerEvent.as +69 -0
  51. data/src/com/adobe/protocols/dict/events/DisconnectedEvent.as +55 -0
  52. data/src/com/adobe/protocols/dict/events/ErrorEvent.as +80 -0
  53. data/src/com/adobe/protocols/dict/events/MatchEvent.as +67 -0
  54. data/src/com/adobe/protocols/dict/events/MatchStrategiesEvent.as +70 -0
  55. data/src/com/adobe/protocols/dict/events/NoMatchEvent.as +54 -0
  56. data/src/com/adobe/protocols/dict/util/CompleteResponseEvent.as +68 -0
  57. data/src/com/adobe/protocols/dict/util/SocketHelper.as +81 -0
  58. data/src/com/adobe/serialization/json/JSON.as +86 -0
  59. data/src/com/adobe/serialization/json/JSONDecoder.as +327 -0
  60. data/src/com/adobe/serialization/json/JSONEncoder.as +312 -0
  61. data/src/com/adobe/serialization/json/JSONParseError.as +87 -0
  62. data/src/com/adobe/serialization/json/JSONToken.as +104 -0
  63. data/src/com/adobe/serialization/json/JSONTokenType.as +69 -0
  64. data/src/com/adobe/serialization/json/JSONTokenizer.as +702 -0
  65. data/src/com/adobe/utils/ArrayUtil.as +187 -0
  66. data/src/com/adobe/utils/DateUtil.as +701 -0
  67. data/src/com/adobe/utils/DictionaryUtil.as +87 -0
  68. data/src/com/adobe/utils/IntUtil.as +99 -0
  69. data/src/com/adobe/utils/NumberFormatter.as +74 -0
  70. data/src/com/adobe/utils/StringUtil.as +239 -0
  71. data/src/com/adobe/utils/XMLUtil.as +168 -0
  72. data/src/com/adobe/webapis/ServiceBase.as +48 -0
  73. data/src/com/adobe/webapis/URLLoaderBase.as +108 -0
  74. data/src/com/adobe/webapis/events/ServiceEvent.as +82 -0
  75. data/tests/src/CoreLibTestRunner-app.xml +135 -0
  76. data/tests/src/CoreLibTestRunner.as +138 -0
  77. data/tests/src/CoreLibTestRunner.mxml +46 -0
  78. data/tests/src/com/adobe/air/crypto/EncryptionKeyGeneratorTest.as +176 -0
  79. data/tests/src/com/adobe/air/filesystem/FileMonitorTest.as +63 -0
  80. data/tests/src/com/adobe/air/filesystem/VolumeMonitorTest.as +57 -0
  81. data/tests/src/com/adobe/air/filesystem/events/FileMonitorEventTest.as +59 -0
  82. data/tests/src/com/adobe/air/net/events/ResourceCacheEventTest.as +72 -0
  83. data/tests/src/com/adobe/crypto/HMACMD5Test.as +134 -0
  84. data/tests/src/com/adobe/crypto/HMACSHA1Test.as +138 -0
  85. data/tests/src/com/adobe/crypto/MD5Test.as +82 -0
  86. data/tests/src/com/adobe/crypto/SHA1Test.as +151 -0
  87. data/tests/src/com/adobe/crypto/SHA224Test.as +104 -0
  88. data/tests/src/com/adobe/crypto/SHA256Test.as +116 -0
  89. data/tests/src/com/adobe/crypto/WSSEUsernameTokenTest.as +87 -0
  90. data/tests/src/com/adobe/images/JPGEncoderTest.as +54 -0
  91. data/tests/src/com/adobe/images/PNGEncoderTest.as +54 -0
  92. data/tests/src/com/adobe/net/URITest.as +589 -0
  93. data/tests/src/com/adobe/protocols/events/ConnectedEventTest.as +59 -0
  94. data/tests/src/com/adobe/protocols/events/DatabaseEventTest.as +62 -0
  95. data/tests/src/com/adobe/protocols/events/DefinitionEventTest.as +61 -0
  96. data/tests/src/com/adobe/protocols/events/DefinitionHeaderEventTest.as +61 -0
  97. data/tests/src/com/adobe/protocols/events/DictionaryServerEventTest.as +61 -0
  98. data/tests/src/com/adobe/protocols/events/DisconnectedEventTest.as +59 -0
  99. data/tests/src/com/adobe/protocols/events/ErrorEventTest.as +63 -0
  100. data/tests/src/com/adobe/protocols/events/MatchEventTest.as +61 -0
  101. data/tests/src/com/adobe/protocols/events/MatchStrategiesEventTest.as +61 -0
  102. data/tests/src/com/adobe/protocols/events/NoMatchEventTest.as +58 -0
  103. data/tests/src/com/adobe/protocols/util/CompletedResponseEventTest.as +60 -0
  104. data/tests/src/com/adobe/serialization/json/JSONTest.as +522 -0
  105. data/tests/src/com/adobe/serialization/json/SimpleClass.as +85 -0
  106. data/tests/src/com/adobe/utils/ArrayUtilTest.as +173 -0
  107. data/tests/src/com/adobe/utils/DateUtilTest.as +436 -0
  108. data/tests/src/com/adobe/utils/DictionaryUtilTest.as +93 -0
  109. data/tests/src/com/adobe/utils/IntUtilTest.as +73 -0
  110. data/tests/src/com/adobe/utils/NumberFormatterTest.as +70 -0
  111. data/tests/src/com/adobe/utils/StringUtilTest.as +304 -0
  112. data/tests/src/com/adobe/utils/XMLUtilTest.as +101 -0
  113. data/tests/src/com/adobe/webapis/events/ServiceEventTest.as +66 -0
  114. metadata +196 -0
@@ -0,0 +1,702 @@
1
+ /*
2
+ Copyright (c) 2008, Adobe Systems Incorporated
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are
7
+ met:
8
+
9
+ * Redistributions of source code must retain the above copyright notice,
10
+ this list of conditions and the following disclaimer.
11
+
12
+ * Redistributions in binary form must reproduce the above copyright
13
+ notice, this list of conditions and the following disclaimer in the
14
+ documentation and/or other materials provided with the distribution.
15
+
16
+ * Neither the name of Adobe Systems Incorporated nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
21
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+ */
32
+
33
+ package com.adobe.serialization.json {
34
+
35
+ public class JSONTokenizer {
36
+
37
+ /**
38
+ * Flag indicating if the tokenizer should only recognize
39
+ * standard JSON tokens. Setting to <code>false</code> allows
40
+ * tokens such as NaN and allows numbers to be formatted as
41
+ * hex, etc.
42
+ */
43
+ private var strict:Boolean;
44
+
45
+ /** The object that will get parsed from the JSON string */
46
+ private var obj:Object;
47
+
48
+ /** The JSON string to be parsed */
49
+ private var jsonString:String;
50
+
51
+ /** The current parsing location in the JSON string */
52
+ private var loc:int;
53
+
54
+ /** The current character in the JSON string during parsing */
55
+ private var ch:String;
56
+
57
+ /**
58
+ * The regular expression used to make sure the string does not
59
+ * contain invalid control characters.
60
+ */
61
+ private var controlCharsRegExp:RegExp = /[\x00-\x1F]/;
62
+
63
+ /**
64
+ * Constructs a new JSONDecoder to parse a JSON string
65
+ * into a native object.
66
+ *
67
+ * @param s The JSON string to be converted
68
+ * into a native object
69
+ */
70
+ public function JSONTokenizer( s:String, strict:Boolean )
71
+ {
72
+ jsonString = s;
73
+ this.strict = strict;
74
+ loc = 0;
75
+
76
+ // prime the pump by getting the first character
77
+ nextChar();
78
+ }
79
+
80
+ /**
81
+ * Gets the next token in the input sting and advances
82
+ * the character to the next character after the token
83
+ */
84
+ public function getNextToken():JSONToken
85
+ {
86
+ var token:JSONToken = new JSONToken();
87
+
88
+ // skip any whitespace / comments since the last
89
+ // token was read
90
+ skipIgnored();
91
+
92
+ // examine the new character and see what we have...
93
+ switch ( ch )
94
+ {
95
+ case '{':
96
+ token.type = JSONTokenType.LEFT_BRACE;
97
+ token.value = '{';
98
+ nextChar();
99
+ break
100
+
101
+ case '}':
102
+ token.type = JSONTokenType.RIGHT_BRACE;
103
+ token.value = '}';
104
+ nextChar();
105
+ break
106
+
107
+ case '[':
108
+ token.type = JSONTokenType.LEFT_BRACKET;
109
+ token.value = '[';
110
+ nextChar();
111
+ break
112
+
113
+ case ']':
114
+ token.type = JSONTokenType.RIGHT_BRACKET;
115
+ token.value = ']';
116
+ nextChar();
117
+ break
118
+
119
+ case ',':
120
+ token.type = JSONTokenType.COMMA;
121
+ token.value = ',';
122
+ nextChar();
123
+ break
124
+
125
+ case ':':
126
+ token.type = JSONTokenType.COLON;
127
+ token.value = ':';
128
+ nextChar();
129
+ break;
130
+
131
+ case 't': // attempt to read true
132
+ var possibleTrue:String = "t" + nextChar() + nextChar() + nextChar();
133
+
134
+ if ( possibleTrue == "true" )
135
+ {
136
+ token.type = JSONTokenType.TRUE;
137
+ token.value = true;
138
+ nextChar();
139
+ }
140
+ else
141
+ {
142
+ parseError( "Expecting 'true' but found " + possibleTrue );
143
+ }
144
+
145
+ break;
146
+
147
+ case 'f': // attempt to read false
148
+ var possibleFalse:String = "f" + nextChar() + nextChar() + nextChar() + nextChar();
149
+
150
+ if ( possibleFalse == "false" )
151
+ {
152
+ token.type = JSONTokenType.FALSE;
153
+ token.value = false;
154
+ nextChar();
155
+ }
156
+ else
157
+ {
158
+ parseError( "Expecting 'false' but found " + possibleFalse );
159
+ }
160
+
161
+ break;
162
+
163
+ case 'n': // attempt to read null
164
+ var possibleNull:String = "n" + nextChar() + nextChar() + nextChar();
165
+
166
+ if ( possibleNull == "null" )
167
+ {
168
+ token.type = JSONTokenType.NULL;
169
+ token.value = null;
170
+ nextChar();
171
+ }
172
+ else
173
+ {
174
+ parseError( "Expecting 'null' but found " + possibleNull );
175
+ }
176
+
177
+ break;
178
+
179
+ case 'N': // attempt to read NaN
180
+ var possibleNaN:String = "N" + nextChar() + nextChar();
181
+
182
+ if ( possibleNaN == "NaN" )
183
+ {
184
+ token.type = JSONTokenType.NAN;
185
+ token.value = NaN;
186
+ nextChar();
187
+ }
188
+ else
189
+ {
190
+ parseError( "Expecting 'NaN' but found " + possibleNaN );
191
+ }
192
+
193
+ break;
194
+
195
+ case '"': // the start of a string
196
+ token = readString();
197
+ break;
198
+
199
+ default:
200
+ // see if we can read a number
201
+ if ( isDigit( ch ) || ch == '-' )
202
+ {
203
+ token = readNumber();
204
+ }
205
+ else if ( ch == '' )
206
+ {
207
+ // check for reading past the end of the string
208
+ return null;
209
+ }
210
+ else
211
+ {
212
+ // not sure what was in the input string - it's not
213
+ // anything we expected
214
+ parseError( "Unexpected " + ch + " encountered" );
215
+ }
216
+ }
217
+
218
+ return token;
219
+ }
220
+
221
+ /**
222
+ * Attempts to read a string from the input string. Places
223
+ * the character location at the first character after the
224
+ * string. It is assumed that ch is " before this method is called.
225
+ *
226
+ * @return the JSONToken with the string value if a string could
227
+ * be read. Throws an error otherwise.
228
+ */
229
+ private function readString():JSONToken
230
+ {
231
+ // Rather than examine the string character-by-character, it's
232
+ // faster to use indexOf to try to and find the closing quote character
233
+ // and then replace escape sequences after the fact.
234
+
235
+ // Start at the current input stream position
236
+ var quoteIndex:int = loc;
237
+ do
238
+ {
239
+ // Find the next quote in the input stream
240
+ quoteIndex = jsonString.indexOf( "\"", quoteIndex );
241
+
242
+ if ( quoteIndex >= 0 )
243
+ {
244
+ // We found the next double quote character in the string, but we need
245
+ // to make sure it is not part of an escape sequence.
246
+
247
+ // Keep looping backwards while the previous character is a backslash
248
+ var backspaceCount:int = 0;
249
+ var backspaceIndex:int = quoteIndex - 1;
250
+ while ( jsonString.charAt( backspaceIndex ) == "\\" )
251
+ {
252
+ backspaceCount++;
253
+ backspaceIndex--;
254
+ }
255
+
256
+ // If we have an even number of backslashes, that means this is the ending quote
257
+ if ( backspaceCount % 2 == 0 )
258
+ {
259
+ break;
260
+ }
261
+
262
+ // At this point, the quote was determined to be part of an escape sequence
263
+ // so we need to move past the quote index to look for the next one
264
+ quoteIndex++;
265
+ }
266
+ else // There are no more quotes in the string and we haven't found the end yet
267
+ {
268
+ parseError( "Unterminated string literal" );
269
+ }
270
+ } while ( true );
271
+
272
+ // Unescape the string
273
+ // the token for the string we'll try to read
274
+ var token:JSONToken = new JSONToken();
275
+ token.type = JSONTokenType.STRING;
276
+ // Attach resulting string to the token to return it
277
+ token.value = unescapeString( jsonString.substr( loc, quoteIndex - loc ) );
278
+
279
+ // Move past the closing quote in the input string. This updates the next
280
+ // character in the input stream to be the character one after the closing quote
281
+ loc = quoteIndex + 1;
282
+ nextChar();
283
+
284
+ return token;
285
+ }
286
+
287
+ /**
288
+ * Convert all JavaScript escape characters into normal characters
289
+ *
290
+ * @param input The input string to convert
291
+ * @return Original string with escape characters replaced by real characters
292
+ */
293
+ public function unescapeString( input:String ):String
294
+ {
295
+ // Issue #104 - If the string contains any unescaped control characters, this
296
+ // is an error in strict mode
297
+ if ( strict && controlCharsRegExp.test( input ) )
298
+ {
299
+ parseError( "String contains unescaped control character (0x00-0x1F)" );
300
+ }
301
+
302
+ var result:String = "";
303
+ var backslashIndex:int = 0;
304
+ var nextSubstringStartPosition:int = 0;
305
+ var len:int = input.length;
306
+ do
307
+ {
308
+ // Find the next backslash in the input
309
+ backslashIndex = input.indexOf( '\\', nextSubstringStartPosition );
310
+
311
+ if ( backslashIndex >= 0 )
312
+ {
313
+ result += input.substr( nextSubstringStartPosition, backslashIndex - nextSubstringStartPosition );
314
+
315
+ // Move past the backslash and next character (all escape sequences are
316
+ // two characters, except for \u, which will advance this further)
317
+ nextSubstringStartPosition = backslashIndex + 2;
318
+
319
+ // Check the next character so we know what to escape
320
+ var afterBackslashIndex:int = backslashIndex + 1;
321
+ var escapedChar:String = input.charAt( afterBackslashIndex );
322
+ switch ( escapedChar )
323
+ {
324
+ // Try to list the most common expected cases first to improve performance
325
+
326
+ case '"': result += '"'; break; // quotation mark
327
+ case '\\': result += '\\'; break; // reverse solidus
328
+ case 'n': result += '\n'; break; // newline
329
+ case 'r': result += '\r'; break; // carriage return
330
+ case 't': result += '\t'; break; // horizontal tab
331
+
332
+ // Convert a unicode escape sequence to it's character value
333
+ case 'u':
334
+
335
+ // Save the characters as a string we'll convert to an int
336
+ var hexValue:String = "";
337
+
338
+ // Make sure there are enough characters in the string leftover
339
+ if ( nextSubstringStartPosition + 4 > len )
340
+ {
341
+ parseError( "Unexpected end of input. Expecting 4 hex digits after \\u." );
342
+ }
343
+
344
+ // Try to find 4 hex characters
345
+ for ( var i:int = nextSubstringStartPosition; i < nextSubstringStartPosition + 4; i++ )
346
+ {
347
+ // get the next character and determine
348
+ // if it's a valid hex digit or not
349
+ var possibleHexChar:String = input.charAt( i );
350
+ if ( !isHexDigit( possibleHexChar ) )
351
+ {
352
+ parseError( "Excepted a hex digit, but found: " + possibleHexChar );
353
+ }
354
+
355
+ // Valid hex digit, add it to the value
356
+ hexValue += possibleHexChar;
357
+ }
358
+
359
+ // Convert hexValue to an integer, and use that
360
+ // integer value to create a character to add
361
+ // to our string.
362
+ result += String.fromCharCode( parseInt( hexValue, 16 ) );
363
+ // Move past the 4 hex digits that we just read
364
+ nextSubstringStartPosition += 4;
365
+ break;
366
+
367
+ case 'f': result += '\f'; break; // form feed
368
+ case '/': result += '/'; break; // solidus
369
+ case 'b': result += '\b'; break; // bell
370
+ default: result += '\\' + escapedChar; // Couldn't unescape the sequence, so just pass it through
371
+ }
372
+ }
373
+ else
374
+ {
375
+ // No more backslashes to replace, append the rest of the string
376
+ result += input.substr( nextSubstringStartPosition );
377
+ break;
378
+ }
379
+
380
+ } while ( nextSubstringStartPosition < len );
381
+
382
+ return result;
383
+ }
384
+
385
+ /**
386
+ * Attempts to read a number from the input string. Places
387
+ * the character location at the first character after the
388
+ * number.
389
+ *
390
+ * @return The JSONToken with the number value if a number could
391
+ * be read. Throws an error otherwise.
392
+ */
393
+ private function readNumber():JSONToken
394
+ {
395
+ // the string to accumulate the number characters
396
+ // into that we'll convert to a number at the end
397
+ var input:String = "";
398
+
399
+ // check for a negative number
400
+ if ( ch == '-' )
401
+ {
402
+ input += '-';
403
+ nextChar();
404
+ }
405
+
406
+ // the number must start with a digit
407
+ if ( !isDigit( ch ) )
408
+ {
409
+ parseError( "Expecting a digit" );
410
+ }
411
+
412
+ // 0 can only be the first digit if it
413
+ // is followed by a decimal point
414
+ if ( ch == '0' )
415
+ {
416
+ input += ch;
417
+ nextChar();
418
+
419
+ // make sure no other digits come after 0
420
+ if ( isDigit( ch ) )
421
+ {
422
+ parseError( "A digit cannot immediately follow 0" );
423
+ }
424
+ // unless we have 0x which starts a hex number, but this
425
+ // doesn't match JSON spec so check for not strict mode.
426
+ else if ( !strict && ch == 'x' )
427
+ {
428
+ // include the x in the input
429
+ input += ch;
430
+ nextChar();
431
+
432
+ // need at least one hex digit after 0x to
433
+ // be valid
434
+ if ( isHexDigit( ch ) )
435
+ {
436
+ input += ch;
437
+ nextChar();
438
+ }
439
+ else
440
+ {
441
+ parseError( "Number in hex format require at least one hex digit after \"0x\"" );
442
+ }
443
+
444
+ // consume all of the hex values
445
+ while ( isHexDigit( ch ) )
446
+ {
447
+ input += ch;
448
+ nextChar();
449
+ }
450
+ }
451
+ }
452
+ else
453
+ {
454
+ // read numbers while we can
455
+ while ( isDigit( ch ) )
456
+ {
457
+ input += ch;
458
+ nextChar();
459
+ }
460
+ }
461
+
462
+ // check for a decimal value
463
+ if ( ch == '.' )
464
+ {
465
+ input += '.';
466
+ nextChar();
467
+
468
+ // after the decimal there has to be a digit
469
+ if ( !isDigit( ch ) )
470
+ {
471
+ parseError( "Expecting a digit" );
472
+ }
473
+
474
+ // read more numbers to get the decimal value
475
+ while ( isDigit( ch ) )
476
+ {
477
+ input += ch;
478
+ nextChar();
479
+ }
480
+ }
481
+
482
+ // check for scientific notation
483
+ if ( ch == 'e' || ch == 'E' )
484
+ {
485
+ input += "e"
486
+ nextChar();
487
+ // check for sign
488
+ if ( ch == '+' || ch == '-' )
489
+ {
490
+ input += ch;
491
+ nextChar();
492
+ }
493
+
494
+ // require at least one number for the exponent
495
+ // in this case
496
+ if ( !isDigit( ch ) )
497
+ {
498
+ parseError( "Scientific notation number needs exponent value" );
499
+ }
500
+
501
+ // read in the exponent
502
+ while ( isDigit( ch ) )
503
+ {
504
+ input += ch;
505
+ nextChar();
506
+ }
507
+ }
508
+
509
+ // convert the string to a number value
510
+ var num:Number = Number( input );
511
+
512
+ if ( isFinite( num ) && !isNaN( num ) )
513
+ {
514
+ // the token for the number that we've read
515
+ var token:JSONToken = new JSONToken();
516
+ token.type = JSONTokenType.NUMBER;
517
+ token.value = num;
518
+ return token;
519
+ }
520
+ else
521
+ {
522
+ parseError( "Number " + num + " is not valid!" );
523
+ }
524
+
525
+ return null;
526
+ }
527
+
528
+ /**
529
+ * Reads the next character in the input
530
+ * string and advances the character location.
531
+ *
532
+ * @return The next character in the input string, or
533
+ * null if we've read past the end.
534
+ */
535
+ private function nextChar():String
536
+ {
537
+ return ch = jsonString.charAt( loc++ );
538
+ }
539
+
540
+ /**
541
+ * Advances the character location past any
542
+ * sort of white space and comments
543
+ */
544
+ private function skipIgnored():void
545
+ {
546
+ var originalLoc:int;
547
+
548
+ // keep trying to skip whitespace and comments as long
549
+ // as we keep advancing past the original location
550
+ do
551
+ {
552
+ originalLoc = loc;
553
+ skipWhite();
554
+ skipComments();
555
+ }
556
+ while ( originalLoc != loc );
557
+ }
558
+
559
+ /**
560
+ * Skips comments in the input string, either
561
+ * single-line or multi-line. Advances the character
562
+ * to the first position after the end of the comment.
563
+ */
564
+ private function skipComments():void
565
+ {
566
+ if ( ch == '/' )
567
+ {
568
+ // Advance past the first / to find out what type of comment
569
+ nextChar();
570
+ switch ( ch )
571
+ {
572
+ case '/': // single-line comment, read through end of line
573
+
574
+ // Loop over the characters until we find
575
+ // a newline or until there's no more characters left
576
+ do
577
+ {
578
+ nextChar();
579
+ }
580
+ while ( ch != '\n' && ch != '' )
581
+
582
+ // move past the \n
583
+ nextChar();
584
+
585
+ break;
586
+
587
+ case '*': // multi-line comment, read until closing */
588
+
589
+ // move past the opening *
590
+ nextChar();
591
+
592
+ // try to find a trailing */
593
+ while ( true )
594
+ {
595
+ if ( ch == '*' )
596
+ {
597
+ // check to see if we have a closing /
598
+ nextChar();
599
+ if ( ch == '/')
600
+ {
601
+ // move past the end of the closing */
602
+ nextChar();
603
+ break;
604
+ }
605
+ }
606
+ else
607
+ {
608
+ // move along, looking if the next character is a *
609
+ nextChar();
610
+ }
611
+
612
+ // when we're here we've read past the end of
613
+ // the string without finding a closing */, so error
614
+ if ( ch == '' )
615
+ {
616
+ parseError( "Multi-line comment not closed" );
617
+ }
618
+ }
619
+
620
+ break;
621
+
622
+ // Can't match a comment after a /, so it's a parsing error
623
+ default:
624
+ parseError( "Unexpected " + ch + " encountered (expecting '/' or '*' )" );
625
+ }
626
+ }
627
+
628
+ }
629
+
630
+
631
+ /**
632
+ * Skip any whitespace in the input string and advances
633
+ * the character to the first character after any possible
634
+ * whitespace.
635
+ */
636
+ private function skipWhite():void
637
+ {
638
+ // As long as there are spaces in the input
639
+ // stream, advance the current location pointer
640
+ // past them
641
+ while ( isWhiteSpace( ch ) )
642
+ {
643
+ nextChar();
644
+ }
645
+
646
+ }
647
+
648
+ /**
649
+ * Determines if a character is whitespace or not.
650
+ *
651
+ * @return True if the character passed in is a whitespace
652
+ * character
653
+ */
654
+ private function isWhiteSpace( ch:String ):Boolean
655
+ {
656
+ // Check for the whitespace defined in the spec
657
+ if ( ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' )
658
+ {
659
+ return true;
660
+ }
661
+ // If we're not in strict mode, we also accept non-breaking space
662
+ else if ( !strict && ch.charCodeAt( 0 ) == 160 )
663
+ {
664
+ return true;
665
+ }
666
+
667
+ return false;
668
+ }
669
+
670
+ /**
671
+ * Determines if a character is a digit [0-9].
672
+ *
673
+ * @return True if the character passed in is a digit
674
+ */
675
+ private function isDigit( ch:String ):Boolean
676
+ {
677
+ return ( ch >= '0' && ch <= '9' );
678
+ }
679
+
680
+ /**
681
+ * Determines if a character is a hex digit [0-9A-Fa-f].
682
+ *
683
+ * @return True if the character passed in is a hex digit
684
+ */
685
+ private function isHexDigit( ch:String ):Boolean
686
+ {
687
+ return ( isDigit( ch ) || ( ch >= 'A' && ch <= 'F' ) || ( ch >= 'a' && ch <= 'f' ) );
688
+ }
689
+
690
+ /**
691
+ * Raises a parsing error with a specified message, tacking
692
+ * on the error location and the original string.
693
+ *
694
+ * @param message The message indicating why the error occurred
695
+ */
696
+ public function parseError( message:String ):void
697
+ {
698
+ throw new JSONParseError( message, loc, jsonString );
699
+ }
700
+ }
701
+
702
+ }