Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(301)

Side by Side Diff: test/generated_sdk/lib/convert/json.dart

Issue 1162723007: remove generated_sdk from checked in code (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 part of dart.convert;
6
7 /**
8 * Error thrown by JSON serialization if an object cannot be serialized.
9 *
10 * The [unsupportedObject] field holds that object that failed to be serialized.
11 *
12 * If an object isn't directly serializable, the serializer calls the 'toJson'
13 * method on the object. If that call fails, the error will be stored in the
14 * [cause] field. If the call returns an object that isn't directly
15 * serializable, the [cause] is be null.
16 */
17 class JsonUnsupportedObjectError extends Error {
18 /** The object that could not be serialized. */
19 final unsupportedObject;
20 /** The exception thrown when trying to convert the object. */
21 final cause;
22
23 JsonUnsupportedObjectError(this.unsupportedObject, { this.cause });
24
25 String toString() {
26 if (cause != null) {
27 return "Converting object to an encodable object failed.";
28 } else {
29 return "Converting object did not return an encodable object.";
30 }
31 }
32 }
33
34
35 /**
36 * Reports that an object could not be stringified due to cyclic references.
37 *
38 * An object that references itself cannot be serialized by [stringify].
39 * When the cycle is detected, a [JsonCyclicError] is thrown.
40 */
41 class JsonCyclicError extends JsonUnsupportedObjectError {
42 /** The first object that was detected as part of a cycle. */
43 JsonCyclicError(Object object): super(object);
44 String toString() => "Cyclic error in JSON stringify";
45 }
46
47
48 /**
49 * An instance of the default implementation of the [JsonCodec].
50 *
51 * This instance provides a convenient access to the most common JSON
52 * use cases.
53 *
54 * Examples:
55 *
56 * var encoded = JSON.encode([1, 2, { "a": null }]);
57 * var decoded = JSON.decode('["foo", { "bar": 499 }]');
58 */
59 const JsonCodec JSON = const JsonCodec();
60
61 typedef _Reviver(var key, var value);
62 typedef _ToEncodable(var o);
63
64
65 /**
66 * A [JsonCodec] encodes JSON objects to strings and decodes strings to
67 * JSON objects.
68 */
69 class JsonCodec extends Codec<Object, String> {
70 final _Reviver _reviver;
71 final _ToEncodable _toEncodable;
72
73 /**
74 * Creates a `JsonCodec` with the given reviver and encoding function.
75 *
76 * The [reviver] function is called during decoding. It is invoked
77 * once for each object or list property that has been parsed.
78 * The `key` argument is either the
79 * integer list index for a list property, the string map key for object
80 * properties, or `null` for the final result.
81 *
82 * If [reviver] is omitted, it defaults to returning the value argument.
83 *
84 * The [toEncodable] function is used during encoding. It is invoked for
85 * values that are not directly encodable to a JSON1toE
86 * string (a value that is not a number, boolean, string, null, list or a map
87 * with string keys). The function must return an object that is directly
88 * encodable. The elements of a returned list and values of a returned map
89 * do not need be directly encodable, and if they aren't, `toEncodable` will
90 * be used on them as well.
91 * Please notice that it is possible to cause an infinite recursive
92 * regress in this way, by effectively creating an infinite data structure
93 * through repeated call to `toEncodable`.
94 *
95 * If [toEncodable] is omitted, it defaults to a function that returns the
96 * result of calling `.toJson()` on the unencodable object.
97 */
98 const JsonCodec({reviver(var key, var value), toEncodable(var object)})
99 : _reviver = reviver,
100 _toEncodable = toEncodable;
101
102 /**
103 * Creates a `JsonCodec` with the given reviver.
104 *
105 * The [reviver] function is called once for each object or list property
106 * that has been parsed during decoding. The `key` argument is either the
107 * integer list index for a list property, the string map key for object
108 * properties, or `null` for the final result.
109 */
110 JsonCodec.withReviver(reviver(var key, var value)) : this(reviver: reviver);
111
112 /**
113 * Parses the string and returns the resulting Json object.
114 *
115 * The optional [reviver] function is called once for each object or list
116 * property that has been parsed during decoding. The `key` argument is either
117 * the integer list index for a list property, the string map key for object
118 * properties, or `null` for the final result.
119 *
120 * The default [reviver] (when not provided) is the identity function.
121 */
122 dynamic decode(String source, {reviver(var key, var value)}) {
123 if (reviver == null) reviver = _reviver;
124 if (reviver == null) return decoder.convert(source);
125 return new JsonDecoder(reviver).convert(source);
126 }
127
128 /**
129 * Converts [value] to a JSON string.
130 *
131 * If value contains objects that are not directly encodable to a JSON
132 * string (a value that is not a number, boolean, string, null, list or a map
133 * with string keys), the [toEncodable] function is used to convert it to an
134 * object that must be directly encodable.
135 *
136 * If [toEncodable] is omitted, it defaults to a function that returns the
137 * result of calling `.toJson()` on the unencodable object.
138 */
139 String encode(Object value, {toEncodable(var object)}) {
140 if (toEncodable == null) toEncodable = _toEncodable;
141 if (toEncodable == null) return encoder.convert(value);
142 return new JsonEncoder(toEncodable).convert(value);
143 }
144
145 JsonEncoder get encoder {
146 if (_toEncodable == null) return const JsonEncoder();
147 return new JsonEncoder(_toEncodable);
148 }
149
150 JsonDecoder get decoder {
151 if (_reviver == null) return const JsonDecoder();
152 return new JsonDecoder(_reviver);
153 }
154 }
155
156 /**
157 * This class converts JSON objects to strings.
158 */
159 class JsonEncoder extends Converter<Object, String> {
160 /**
161 * The string used for indention.
162 *
163 * When generating multi-line output, this string is inserted once at the
164 * beginning of each indented line for each level of indentation.
165 *
166 * If `null`, the output is encoded as a single line.
167 */
168 final String indent;
169
170 /**
171 * Function called on non-encodable objects to return a replacement
172 * encodable object that will be encoded in the orignal's place.
173 */
174 final Function _toEncodable;
175
176 /**
177 * Creates a JSON encoder.
178 *
179 * The JSON encoder handles numbers, strings, booleans, null, lists and
180 * maps directly.
181 *
182 * Any other object is attempted converted by [toEncodable] to an
183 * object that is of one of the convertible types.
184 *
185 * If [toEncodable] is omitted, it defaults to calling `.toJson()` on
186 * the object.
187 */
188 const JsonEncoder([Object toEncodable(Object nonSerializable)])
189 : this.indent = null,
190 this._toEncodable = toEncodable;
191
192 /**
193 * Creates a JSON encoder that creates multi-line JSON.
194 *
195 * The encoding of elements of lists and maps are indented and put on separate
196 * lines. The [indent] string is prepended to these elements, once for each
197 * level of indentation.
198 *
199 * If [indent] is `null`, the output is encoded as a single line.
200 *
201 * The JSON encoder handles numbers, strings, booleans, null, lists and
202 * maps directly.
203 *
204 * Any other object is attempted converted by [toEncodable] to an
205 * object that is of one of the convertible types.
206 *
207 * If [toEncodable] is omitted, it defaults to calling `.toJson()` on
208 * the object.
209 */
210 const JsonEncoder.withIndent(this.indent,
211 [Object toEncodable(Object nonSerializable)])
212 : this._toEncodable = toEncodable;
213
214 /**
215 * Converts [object] to a JSON [String].
216 *
217 * Directly serializable values are [num], [String], [bool], and [Null], as
218 * well as some [List] and [Map] values.
219 * For [List], the elements must all be serializable.
220 * For [Map], the keys must be [String] and the values must be serializable.
221 *
222 * If a value is any other type is attempted serialized, the conversion
223 * function provided in the constructor is invoked with the object as argument
224 * and the result, which must be a directly serializable value,
225 * is serialized instead of the original value.
226 *
227 * If the conversion throws, or returns a value that is not directly
228 * serializable, a [JsonUnsupportedObjectError] exception is thrown.
229 * If the call throws, the error is caught and stored in the
230 * [JsonUnsupportedObjectError]'s [:cause:] field.
231 *
232 * If a [List] or [Map] contains a reference to itself, directly or through
233 * other lists or maps, it cannot be serialized and a [JsonCyclicError] is
234 * thrown.
235 *
236 * [object] should not change during serialization.
237 *
238 * If an object is serialized more than once, [convert] may cache the text
239 * for it. In other words, if the content of an object changes after it is
240 * first serialized, the new values may not be reflected in the result.
241 */
242 String convert(Object object) =>
243 _JsonStringStringifier.stringify(object, _toEncodable, indent);
244
245 /**
246 * Starts a chunked conversion.
247 *
248 * The converter works more efficiently if the given [sink] is a
249 * [StringConversionSink].
250 *
251 * Returns a chunked-conversion sink that accepts at most one object. It is
252 * an error to invoke `add` more than once on the returned sink.
253 */
254 ChunkedConversionSink<Object> startChunkedConversion(Sink<String> sink) {
255 if (sink is! StringConversionSink) {
256 sink = new StringConversionSink.from(sink);
257 } else if (sink is _Utf8EncoderSink) {
258 return new _JsonUtf8EncoderSink(sink._sink, _toEncodable,
259 JsonUtf8Encoder._utf8Encode(indent),
260 JsonUtf8Encoder.DEFAULT_BUFFER_SIZE);
261 }
262 return new _JsonEncoderSink(sink, _toEncodable, indent);
263 }
264
265 // Override the base-classes bind, to provide a better type.
266 Stream<String> bind(Stream<Object> stream) => super.bind(stream);
267
268 Converter<Object, dynamic> fuse(Converter<String, dynamic> other) {
269 if (other is Utf8Encoder) {
270 return new JsonUtf8Encoder(indent, _toEncodable);
271 }
272 return super.fuse(other);
273 }
274 }
275
276 /**
277 * Encoder that encodes a single object as a UTF-8 encoded JSON string.
278 *
279 * This encoder works equivalently to first converting the object to
280 * a JSON string, and then UTF-8 encoding the string, but without
281 * creating an intermediate string.
282 */
283 class JsonUtf8Encoder extends Converter<Object, List<int>> {
284 /** Default buffer size used by the JSON-to-UTF-8 encoder. */
285 static const int DEFAULT_BUFFER_SIZE = 256;
286 /** Indentation used in pretty-print mode, `null` if not pretty. */
287 final List<int> _indent;
288 /** Function called with each un-encodable object encountered. */
289 final Function _toEncodable;
290 /** UTF-8 buffer size. */
291 final int _bufferSize;
292
293 /**
294 * Create converter.
295 *
296 * If [indent] is non-`null`, the converter attempts to "pretty-print" the
297 * JSON, and uses `indent` as the indentation. Otherwise the result has no
298 * whitespace outside of string literals.
299 * If `indent` contains characters that are not valid JSON whitespace
300 * characters, the result will not be valid JSON. JSON whitespace characters
301 * are space (U+0020), tab (U+0009), line feed (U+000a) and carriage return
302 * (U+000d) (ECMA 404).
303 *
304 * The [bufferSize] is the size of the internal buffers used to collect
305 * UTF-8 code units.
306 * If using [startChunkedConversion], it will be the size of the chunks.
307 *
308 * The JSON encoder handles numbers, strings, booleans, null, lists and
309 * maps directly.
310 *
311 * Any other object is attempted converted by [toEncodable] to an
312 * object that is of one of the convertible types.
313 *
314 * If [toEncodable] is omitted, it defaults to calling `.toJson()` on
315 * the object.
316 */
317 JsonUtf8Encoder([String indent,
318 toEncodable(Object object),
319 int bufferSize = DEFAULT_BUFFER_SIZE])
320 : _indent = _utf8Encode(indent),
321 _toEncodable = toEncodable,
322 _bufferSize = bufferSize;
323
324 static List<int> _utf8Encode(String string) {
325 if (string == null) return null;
326 if (string.isEmpty) return new Uint8List(0);
327 checkAscii: {
328 for (int i = 0; i < string.length; i++) {
329 if (string.codeUnitAt(i) >= 0x80) break checkAscii;
330 }
331 return string.codeUnits;
332 }
333 return UTF8.encode(string);
334 }
335
336 /** Convert [object] into UTF-8 encoded JSON. */
337 List<int> convert(Object object) {
338 List<List<int>> bytes = [];
339 // The `stringify` function always converts into chunks.
340 // Collect the chunks into the `bytes` list, then combine them afterwards.
341 void addChunk(Uint8List chunk, int start, int end) {
342 if (start > 0 || end < chunk.length) {
343 int length = end - start;
344 chunk = new Uint8List.view(chunk.buffer,
345 chunk.offsetInBytes + start,
346 length);
347 }
348 bytes.add(chunk);
349 }
350 _JsonUtf8Stringifier.stringify(object,
351 _indent,
352 _toEncodable,
353 _bufferSize,
354 addChunk);
355 if (bytes.length == 1) return bytes[0];
356 int length = 0;
357 for (int i = 0; i < bytes.length; i++) {
358 length += bytes[i].length;
359 }
360 Uint8List result = new Uint8List(length);
361 for (int i = 0, offset = 0; i < bytes.length; i++) {
362 var byteList = bytes[i];
363 int end = offset + byteList.length;
364 result.setRange(offset, end, byteList);
365 offset = end;
366 }
367 return result;
368 }
369
370 /**
371 * Start a chunked conversion.
372 *
373 * Only one object can be passed into the returned sink.
374 *
375 * The argument [sink] will receive byte lists in sizes depending on the
376 * `bufferSize` passed to the constructor when creating this encoder.
377 */
378 ChunkedConversionSink<Object> startChunkedConversion(Sink<List<int>> sink) {
379 ByteConversionSink byteSink;
380 if (sink is ByteConversionSink) {
381 byteSink = sink;
382 } else {
383 byteSink = new ByteConversionSink.from(sink);
384 }
385 return new _JsonUtf8EncoderSink(byteSink, _toEncodable,
386 _indent, _bufferSize);
387 }
388
389 // Override the base-classes bind, to provide a better type.
390 Stream<List<int>> bind(Stream<Object> stream) {
391 return super.bind(stream);
392 }
393
394 Converter<Object, dynamic> fuse(Converter<List<int>, dynamic> other) {
395 return super.fuse(other);
396 }
397 }
398
399 /**
400 * Implements the chunked conversion from object to its JSON representation.
401 *
402 * The sink only accepts one value, but will produce output in a chunked way.
403 */
404 class _JsonEncoderSink extends ChunkedConversionSink<Object> {
405 final String _indent;
406 final Function _toEncodable;
407 final StringConversionSink _sink;
408 bool _isDone = false;
409
410 _JsonEncoderSink(this._sink, this._toEncodable, this._indent);
411
412 /**
413 * Encodes the given object [o].
414 *
415 * It is an error to invoke this method more than once on any instance. While
416 * this makes the input effectly non-chunked the output will be generated in
417 * a chunked way.
418 */
419 void add(Object o) {
420 if (_isDone) {
421 throw new StateError("Only one call to add allowed");
422 }
423 _isDone = true;
424 ClosableStringSink stringSink = _sink.asStringSink();
425 _JsonStringStringifier.printOn(o, stringSink, _toEncodable, _indent);
426 stringSink.close();
427 }
428
429 void close() { /* do nothing */ }
430 }
431
432 /**
433 * Sink returned when starting a chunked conversion from object to bytes.
434 */
435 class _JsonUtf8EncoderSink extends ChunkedConversionSink<Object> {
436 /** The byte sink receiveing the encoded chunks. */
437 final ByteConversionSink _sink;
438 final List<int> _indent;
439 final Function _toEncodable;
440 final int _bufferSize;
441 bool _isDone = false;
442 _JsonUtf8EncoderSink(this._sink, this._toEncodable, this._indent,
443 this._bufferSize);
444
445 /** Callback called for each slice of result bytes. */
446 void _addChunk(Uint8List chunk, int start, int end) {
447 _sink.addSlice(chunk, start, end, false);
448 }
449
450 void add(Object object) {
451 if (_isDone) {
452 throw new StateError("Only one call to add allowed");
453 }
454 _isDone = true;
455 _JsonUtf8Stringifier.stringify(object, _indent, _toEncodable,
456 _bufferSize,
457 _addChunk);
458 _sink.close();
459 }
460
461 void close() {
462 if (!_isDone) {
463 _isDone = true;
464 _sink.close();
465 }
466 }
467 }
468
469 /**
470 * This class parses JSON strings and builds the corresponding objects.
471 */
472 class JsonDecoder extends Converter<String, Object> {
473 final _Reviver _reviver;
474 /**
475 * Constructs a new JsonDecoder.
476 *
477 * The [reviver] may be `null`.
478 */
479 const JsonDecoder([reviver(var key, var value)]) : this._reviver = reviver;
480
481 /**
482 * Converts the given JSON-string [input] to its corresponding object.
483 *
484 * Parsed JSON values are of the types [num], [String], [bool], [Null],
485 * [List]s of parsed JSON values or [Map]s from [String] to parsed
486 * JSON values.
487 *
488 * If `this` was initialized with a reviver, then the parsing operation
489 * invokes the reviver on every object or list property that has been parsed.
490 * The arguments are the property name ([String]) or list index ([int]), and
491 * the value is the parsed value. The return value of the reviver is used as
492 * the value of that property instead the parsed value.
493 *
494 * Throws [FormatException] if the input is not valid JSON text.
495 */
496 dynamic convert(String input) => _parseJson(input, _reviver);
497
498 /**
499 * Starts a conversion from a chunked JSON string to its corresponding
500 * object.
501 *
502 * The output [sink] receives exactly one decoded element through `add`.
503 */
504 StringConversionSink startChunkedConversion(Sink<Object> sink) {
505 return new _JsonDecoderSink(_reviver, sink);
506 }
507
508 // Override the base-classes bind, to provide a better type.
509 Stream<Object> bind(Stream<String> stream) => super.bind(stream);
510 }
511
512 // Internal optimized JSON parsing implementation.
513 _parseJson(String source, reviver(key, value)) {
514 if (source is! String) throw new ArgumentError(source);
515
516 var parsed;
517 try {
518 parsed = JS('=Object|JSExtendableArray|Null|bool|num|String',
519 'JSON.parse(#)',
520 source);
521 } catch (e) {
522 throw new FormatException(JS('String', 'String(#)', e));
523 }
524
525 if (reviver == null) {
526 return _convertJsonToDartLazy(parsed);
527 } else {
528 return _convertJsonToDart(parsed, reviver);
529 }
530 }
531
532
533 // Implementation of encoder/stringifier.
534
535 Object _defaultToEncodable(object) => object.toJson();
536
537 /**
538 * JSON encoder that traverses an object structure and writes JSON source.
539 *
540 * This is an abstract implementation that doesn't decide on the output
541 * format, but writes the JSON through abstract methods like [writeString].
542 */
543 abstract class _JsonStringifier {
544 // Character code constants.
545 static const int BACKSPACE = 0x08;
546 static const int TAB = 0x09;
547 static const int NEWLINE = 0x0a;
548 static const int CARRIAGE_RETURN = 0x0d;
549 static const int FORM_FEED = 0x0c;
550 static const int QUOTE = 0x22;
551 static const int CHAR_0 = 0x30;
552 static const int BACKSLASH = 0x5c;
553 static const int CHAR_b = 0x62;
554 static const int CHAR_f = 0x66;
555 static const int CHAR_n = 0x6e;
556 static const int CHAR_r = 0x72;
557 static const int CHAR_t = 0x74;
558 static const int CHAR_u = 0x75;
559
560 /** List of objects currently being traversed. Used to detect cycles. */
561 final List _seen = new List();
562 /** Function called for each un-encodable object encountered. */
563 final Function _toEncodable;
564
565 _JsonStringifier(Object _toEncodable(Object o))
566 : _toEncodable = (_toEncodable != null) ? _toEncodable
567 : _defaultToEncodable;
568
569 /** Append a string to the JSON output. */
570 void writeString(String characters);
571 /** Append part of a string to the JSON output. */
572 void writeStringSlice(String characters, int start, int end);
573 /** Append a single character, given by its code point, to the JSON output. */
574 void writeCharCode(int charCode);
575 /** Write a number to the JSON output. */
576 void writeNumber(num number);
577
578 // ('0' + x) or ('a' + x - 10)
579 static int hexDigit(int x) => x < 10 ? 48 + x : 87 + x;
580
581 /**
582 * Write, and suitably escape, a string's content as a JSON string literal.
583 */
584 void writeStringContent(String s) {
585 int offset = 0;
586 final int length = s.length;
587 for (int i = 0; i < length; i++) {
588 int charCode = s.codeUnitAt(i);
589 if (charCode > BACKSLASH) continue;
590 if (charCode < 32) {
591 if (i > offset) writeStringSlice(s, offset, i);
592 offset = i + 1;
593 writeCharCode(BACKSLASH);
594 switch (charCode) {
595 case BACKSPACE:
596 writeCharCode(CHAR_b);
597 break;
598 case TAB:
599 writeCharCode(CHAR_t);
600 break;
601 case NEWLINE:
602 writeCharCode(CHAR_n);
603 break;
604 case FORM_FEED:
605 writeCharCode(CHAR_f);
606 break;
607 case CARRIAGE_RETURN:
608 writeCharCode(CHAR_r);
609 break;
610 default:
611 writeCharCode(CHAR_u);
612 writeCharCode(CHAR_0);
613 writeCharCode(CHAR_0);
614 writeCharCode(hexDigit((charCode >> 4) & 0xf));
615 writeCharCode(hexDigit(charCode & 0xf));
616 break;
617 }
618 } else if (charCode == QUOTE || charCode == BACKSLASH) {
619 if (i > offset) writeStringSlice(s, offset, i);
620 offset = i + 1;
621 writeCharCode(BACKSLASH);
622 writeCharCode(charCode);
623 }
624 }
625 if (offset == 0) {
626 writeString(s);
627 } else if (offset < length) {
628 writeStringSlice(s, offset, length);
629 }
630 }
631
632 /**
633 * Check if an encountered object is already being traversed.
634 *
635 * Records the object if it isn't already seen.
636 * Should have a matching call to [_removeSeen] when the object
637 * is no longer being traversed.
638 */
639 void _checkCycle(object) {
640 for (int i = 0; i < _seen.length; i++) {
641 if (identical(object, _seen[i])) {
642 throw new JsonCyclicError(object);
643 }
644 }
645 _seen.add(object);
646 }
647
648 /**
649 * Removes object from the list of currently traversed objects.
650 *
651 * Should be called in the opposite order of the matching [_checkCycle]
652 * calls.
653 */
654 void _removeSeen(object) {
655 assert(!_seen.isEmpty);
656 assert(identical(_seen.last, object));
657 _seen.removeLast();
658 }
659
660 /**
661 * Writes an object.
662 *
663 * If the object isn't directly encodable, the [_toEncodable] function
664 * gets one chance to return a replacement which is encodable.
665 */
666 void writeObject(object) {
667 // Tries stringifying object directly. If it's not a simple value, List or
668 // Map, call toJson() to get a custom representation and try serializing
669 // that.
670 if (writeJsonValue(object)) return;
671 _checkCycle(object);
672 try {
673 var customJson = _toEncodable(object);
674 if (!writeJsonValue(customJson)) {
675 throw new JsonUnsupportedObjectError(object);
676 }
677 _removeSeen(object);
678 } catch (e) {
679 throw new JsonUnsupportedObjectError(object, cause: e);
680 }
681 }
682
683 /**
684 * Serializes a [num], [String], [bool], [Null], [List] or [Map] value.
685 *
686 * Returns true if the value is one of these types, and false if not.
687 * If a value is both a [List] and a [Map], it's serialized as a [List].
688 */
689 bool writeJsonValue(object) {
690 if (object is num) {
691 if (!object.isFinite) return false;
692 writeNumber(object);
693 return true;
694 } else if (identical(object, true)) {
695 writeString('true');
696 return true;
697 } else if (identical(object, false)) {
698 writeString('false');
699 return true;
700 } else if (object == null) {
701 writeString('null');
702 return true;
703 } else if (object is String) {
704 writeString('"');
705 writeStringContent(object);
706 writeString('"');
707 return true;
708 } else if (object is List) {
709 _checkCycle(object);
710 writeList(object);
711 _removeSeen(object);
712 return true;
713 } else if (object is Map) {
714 _checkCycle(object);
715 writeMap(object);
716 _removeSeen(object);
717 return true;
718 } else {
719 return false;
720 }
721 }
722
723 /** Serializes a [List]. */
724 void writeList(List list) {
725 writeString('[');
726 if (list.length > 0) {
727 writeObject(list[0]);
728 for (int i = 1; i < list.length; i++) {
729 writeString(',');
730 writeObject(list[i]);
731 }
732 }
733 writeString(']');
734 }
735
736 /** Serializes a [Map]. */
737 void writeMap(Map<String, Object> map) {
738 writeString('{');
739 String separator = '"';
740 map.forEach((String key, value) {
741 writeString(separator);
742 separator = ',"';
743 writeStringContent(key);
744 writeString('":');
745 writeObject(value);
746 });
747 writeString('}');
748 }
749 }
750
751 /**
752 * A modification of [_JsonStringifier] which indents the contents of [List] and
753 * [Map] objects using the specified indent value.
754 *
755 * Subclasses should implement [writeIndentation].
756 */
757 abstract class _JsonPrettyPrintMixin implements _JsonStringifier {
758 int _indentLevel = 0;
759
760 /**
761 * Add [indentLevel] indentations to the JSON output.
762 */
763 void writeIndentation(indentLevel);
764
765 void writeList(List list) {
766 if (list.isEmpty) {
767 writeString('[]');
768 } else {
769 writeString('[\n');
770 _indentLevel++;
771 writeIndentation(_indentLevel);
772 writeObject(list[0]);
773 for (int i = 1; i < list.length; i++) {
774 writeString(',\n');
775 writeIndentation(_indentLevel);
776 writeObject(list[i]);
777 }
778 writeString('\n');
779 _indentLevel--;
780 writeIndentation(_indentLevel);
781 writeString(']');
782 }
783 }
784
785 void writeMap(Map map) {
786 if (map.isEmpty) {
787 writeString('{}');
788 } else {
789 writeString('{\n');
790 _indentLevel++;
791 bool first = true;
792 map.forEach((String key, Object value) {
793 if (!first) {
794 writeString(",\n");
795 }
796 writeIndentation(_indentLevel);
797 writeString('"');
798 writeStringContent(key);
799 writeString('": ');
800 writeObject(value);
801 first = false;
802 });
803 writeString('\n');
804 _indentLevel--;
805 writeIndentation(_indentLevel);
806 writeString('}');
807 }
808 }
809 }
810
811 /**
812 * A specialziation of [_JsonStringifier] that writes its JSON to a string.
813 */
814 class _JsonStringStringifier extends _JsonStringifier {
815 final StringSink _sink;
816
817 _JsonStringStringifier(this._sink, _toEncodable) : super(_toEncodable);
818
819 /**
820 * Convert object to a string.
821 *
822 * The [toEncodable] function is used to convert non-encodable objects
823 * to encodable ones.
824 *
825 * If [indent] is not `null`, the resulting JSON will be "pretty-printed"
826 * with newlines and indentation. The `indent` string is added as indentation
827 * for each indentation level. It should only contain valid JSON whitespace
828 * characters (space, tab, carriage return or line feed).
829 */
830 static String stringify(object, toEncodable(object), String indent) {
831 StringBuffer output = new StringBuffer();
832 printOn(object, output, toEncodable, indent);
833 return output.toString();
834 }
835
836 /**
837 * Convert object to a string, and write the result to the [output] sink.
838 *
839 * The result is written piecemally to the sink.
840 */
841 static void printOn(object, StringSink output, toEncodable(object),
842 String indent) {
843 var stringifier;
844 if (indent == null) {
845 stringifier = new _JsonStringStringifier(output, toEncodable);
846 } else {
847 stringifier =
848 new _JsonStringStringifierPretty(output, toEncodable, indent);
849 }
850 stringifier.writeObject(object);
851 }
852
853 void writeNumber(num number) {
854 _sink.write(number.toString());
855 }
856 void writeString(String string) {
857 _sink.write(string);
858 }
859 void writeStringSlice(String string, int start, int end) {
860 _sink.write(string.substring(start, end));
861 }
862 void writeCharCode(int charCode) {
863 _sink.writeCharCode(charCode);
864 }
865 }
866
867 class _JsonStringStringifierPretty extends _JsonStringStringifier
868 with _JsonPrettyPrintMixin {
869 final String _indent;
870
871 _JsonStringStringifierPretty(StringSink sink, Function toEncodable,
872 this._indent)
873 : super(sink, toEncodable);
874
875 void writeIndentation(int count) {
876 for (int i = 0; i < count; i++) writeString(_indent);
877 }
878 }
879
880 /**
881 * Specialization of [_JsonStringifier] that writes the JSON as UTF-8.
882 *
883 * The JSON text is UTF-8 encoded and written to [Uint8List] buffers.
884 * The buffers are then passed back to a user provided callback method.
885 */
886 class _JsonUtf8Stringifier extends _JsonStringifier {
887 final int bufferSize;
888 final Function addChunk;
889 Uint8List buffer;
890 int index = 0;
891
892 _JsonUtf8Stringifier(toEncodable, int bufferSize, this.addChunk)
893 : this.bufferSize = bufferSize,
894 buffer = new Uint8List(bufferSize),
895 super(toEncodable)
896 ;
897
898 /**
899 * Convert [object] to UTF-8 encoded JSON.
900 *
901 * Calls [addChunk] with slices of UTF-8 code units.
902 * These will typically have size [bufferSize], but may be shorter.
903 * The buffers are not reused, so the [addChunk] call may keep and reuse
904 * the chunks.
905 *
906 * If [indent] is non-`null`, the result will be "pretty-printed" with
907 * extra newlines and indentation, using [indent] as the indentation.
908 */
909 static void stringify(Object object,
910 List<int> indent,
911 toEncodableFunction(Object o),
912 int bufferSize,
913 void addChunk(Uint8List chunk, int start, int end)) {
914 _JsonUtf8Stringifier stringifier;
915 if (indent != null) {
916 stringifier = new _JsonUtf8StringifierPretty(toEncodableFunction, indent,
917 bufferSize, addChunk);
918 } else {
919 stringifier = new _JsonUtf8Stringifier(toEncodableFunction,
920 bufferSize, addChunk);
921 }
922 stringifier.writeObject(object);
923 stringifier.flush();
924 }
925
926 /**
927 * Must be called at the end to push the last chunk to the [addChunk]
928 * callback.
929 */
930 void flush() {
931 if (index > 0) {
932 addChunk(buffer, 0, index);
933 }
934 buffer = null;
935 index = 0;
936 }
937
938 void writeNumber(num number) {
939 writeAsciiString(number.toString());
940 }
941
942 /** Write a string that is known to not have non-ASCII characters. */
943 void writeAsciiString(String string) {
944 // TODO(lrn): Optimize by copying directly into buffer instead of going
945 // through writeCharCode;
946 for (int i = 0; i < string.length; i++) {
947 int char = string.codeUnitAt(i);
948 assert(char <= 0x7f);
949 writeByte(char);
950 }
951 }
952
953 void writeString(String string) {
954 writeStringSlice(string, 0, string.length);
955 }
956
957 void writeStringSlice(String string, int start, int end) {
958 // TODO(lrn): Optimize by copying directly into buffer instead of going
959 // through writeCharCode/writeByte. Assumption is the most characters
960 // in starings are plain ASCII.
961 for (int i = start; i < end; i++) {
962 int char = string.codeUnitAt(i);
963 if (char <= 0x7f) {
964 writeByte(char);
965 } else {
966 if ((char & 0xFC00) == 0xD800 && i + 1 < end) {
967 // Lead surrogate.
968 int nextChar = string.codeUnitAt(i + 1);
969 if ((nextChar & 0xFC00) == 0xDC00) {
970 // Tail surrogate.
971 char = 0x10000 + ((char & 0x3ff) << 10) + (nextChar & 0x3ff);
972 writeFourByteCharCode(char);
973 i++;
974 continue;
975 }
976 }
977 writeMultiByteCharCode(char);
978 }
979 }
980 }
981
982 void writeCharCode(int charCode) {
983 if (charCode <= 0x7f) {
984 writeByte(charCode);
985 return;
986 }
987 writeMultiByteCharCode(charCode);
988 }
989
990 void writeMultiByteCharCode(int charCode) {
991 if (charCode <= 0x7ff) {
992 writeByte(0xC0 | (charCode >> 6));
993 writeByte(0x80 | (charCode & 0x3f));
994 return;
995 }
996 if (charCode <= 0xffff) {
997 writeByte(0xE0 | (charCode >> 12));
998 writeByte(0x80 | ((charCode >> 6) & 0x3f));
999 writeByte(0x80 | (charCode & 0x3f));
1000 return;
1001 }
1002 writeFourByteCharCode(charCode);
1003 }
1004
1005 void writeFourByteCharCode(int charCode) {
1006 assert(charCode <= 0x10ffff);
1007 writeByte(0xF0 | (charCode >> 18));
1008 writeByte(0x80 | ((charCode >> 12) & 0x3f));
1009 writeByte(0x80 | ((charCode >> 6) & 0x3f));
1010 writeByte(0x80 | (charCode & 0x3f));
1011 }
1012
1013 void writeByte(int byte) {
1014 assert(byte <= 0xff);
1015 if (index == buffer.length) {
1016 addChunk(buffer, 0, index);
1017 buffer = new Uint8List(bufferSize);
1018 index = 0;
1019 }
1020 buffer[index++] = byte;
1021 }
1022 }
1023
1024 /**
1025 * Pretty-printing version of [_JsonUtf8Stringifier].
1026 */
1027 class _JsonUtf8StringifierPretty extends _JsonUtf8Stringifier
1028 with _JsonPrettyPrintMixin {
1029 final List<int> indent;
1030 _JsonUtf8StringifierPretty(toEncodableFunction, this.indent,
1031 bufferSize, addChunk)
1032 : super(toEncodableFunction, bufferSize, addChunk);
1033
1034 void writeIndentation(int count) {
1035 List<int> indent = this.indent;
1036 int indentLength = indent.length;
1037 if (indentLength == 1) {
1038 int char = indent[0];
1039 while (count > 0) {
1040 writeByte(char);
1041 count -= 1;
1042 }
1043 return;
1044 }
1045 while (count > 0) {
1046 count--;
1047 int end = index + indentLength;
1048 if (end <= buffer.length) {
1049 buffer.setRange(index, end, indent);
1050 index = end;
1051 } else {
1052 for (int i = 0; i < indentLength; i++) {
1053 writeByte(indent[i]);
1054 }
1055 }
1056 }
1057 }
1058 }
OLDNEW
« no previous file with comments | « test/generated_sdk/lib/convert/html_escape.dart ('k') | test/generated_sdk/lib/convert/latin1.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698