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

Side by Side Diff: pkg/dev_compiler/tool/input_sdk/lib/convert/json.dart

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

Powered by Google App Engine
This is Rietveld 408576698