| Index: pkg/dev_compiler/tool/input_sdk/lib/convert/json.dart
|
| diff --git a/pkg/dev_compiler/tool/input_sdk/lib/convert/json.dart b/pkg/dev_compiler/tool/input_sdk/lib/convert/json.dart
|
| deleted file mode 100644
|
| index 24931df19496e9b18f0a7f6aa101a553b1ddebb0..0000000000000000000000000000000000000000
|
| --- a/pkg/dev_compiler/tool/input_sdk/lib/convert/json.dart
|
| +++ /dev/null
|
| @@ -1,1064 +0,0 @@
|
| -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -part of dart.convert;
|
| -
|
| -/**
|
| - * Error thrown by JSON serialization if an object cannot be serialized.
|
| - *
|
| - * The [unsupportedObject] field holds that object that failed to be serialized.
|
| - *
|
| - * If an object isn't directly serializable, the serializer calls the `toJson`
|
| - * method on the object. If that call fails, the error will be stored in the
|
| - * [cause] field. If the call returns an object that isn't directly
|
| - * serializable, the [cause] is null.
|
| - */
|
| -class JsonUnsupportedObjectError extends Error {
|
| - /** The object that could not be serialized. */
|
| - final unsupportedObject;
|
| - /** The exception thrown when trying to convert the object. */
|
| - final cause;
|
| -
|
| - JsonUnsupportedObjectError(this.unsupportedObject, { this.cause });
|
| -
|
| - String toString() {
|
| - if (cause != null) {
|
| - return "Converting object to an encodable object failed.";
|
| - } else {
|
| - return "Converting object did not return an encodable object.";
|
| - }
|
| - }
|
| -}
|
| -
|
| -
|
| -/**
|
| - * Reports that an object could not be stringified due to cyclic references.
|
| - *
|
| - * An object that references itself cannot be serialized by [stringify].
|
| - * When the cycle is detected, a [JsonCyclicError] is thrown.
|
| - */
|
| -class JsonCyclicError extends JsonUnsupportedObjectError {
|
| - /** The first object that was detected as part of a cycle. */
|
| - JsonCyclicError(Object object): super(object);
|
| - String toString() => "Cyclic error in JSON stringify";
|
| -}
|
| -
|
| -
|
| -/**
|
| - * An instance of the default implementation of the [JsonCodec].
|
| - *
|
| - * This instance provides a convenient access to the most common JSON
|
| - * use cases.
|
| - *
|
| - * Examples:
|
| - *
|
| - * var encoded = JSON.encode([1, 2, { "a": null }]);
|
| - * var decoded = JSON.decode('["foo", { "bar": 499 }]');
|
| - */
|
| -const JsonCodec JSON = const JsonCodec();
|
| -
|
| -typedef _Reviver(var key, var value);
|
| -typedef _ToEncodable(var o);
|
| -
|
| -
|
| -/**
|
| - * A [JsonCodec] encodes JSON objects to strings and decodes strings to
|
| - * JSON objects.
|
| - *
|
| - * Examples:
|
| - *
|
| - * var encoded = JSON.encode([1, 2, { "a": null }]);
|
| - * var decoded = JSON.decode('["foo", { "bar": 499 }]');
|
| - */
|
| -class JsonCodec extends Codec<Object, String> {
|
| - final _Reviver _reviver;
|
| - final _ToEncodable _toEncodable;
|
| -
|
| - /**
|
| - * Creates a `JsonCodec` with the given reviver and encoding function.
|
| - *
|
| - * The [reviver] function is called during decoding. It is invoked once for
|
| - * each object or list property that has been parsed.
|
| - * The `key` argument is either the integer list index for a list property,
|
| - * the string map key for object properties, or `null` for the final result.
|
| - *
|
| - * If [reviver] is omitted, it defaults to returning the value argument.
|
| - *
|
| - * The [toEncodable] function is used during encoding. It is invoked for
|
| - * values that are not directly encodable to a string (a value that is not a
|
| - * number, boolean, string, null, list or a map with string keys). The
|
| - * function must return an object that is directly encodable. The elements of
|
| - * a returned list and values of a returned map do not need to be directly
|
| - * encodable, and if they aren't, `toEncodable` will be used on them as well.
|
| - * Please notice that it is possible to cause an infinite recursive regress
|
| - * in this way, by effectively creating an infinite data structure through
|
| - * repeated call to `toEncodable`.
|
| - *
|
| - * If [toEncodable] is omitted, it defaults to a function that returns the
|
| - * result of calling `.toJson()` on the unencodable object.
|
| - */
|
| - const JsonCodec({reviver(var key, var value), toEncodable(var object)})
|
| - : _reviver = reviver,
|
| - _toEncodable = toEncodable;
|
| -
|
| - /**
|
| - * Creates a `JsonCodec` with the given reviver.
|
| - *
|
| - * The [reviver] function is called once for each object or list property
|
| - * that has been parsed during decoding. The `key` argument is either the
|
| - * integer list index for a list property, the string map key for object
|
| - * properties, or `null` for the final result.
|
| - */
|
| - JsonCodec.withReviver(reviver(var key, var value)) : this(reviver: reviver);
|
| -
|
| - /**
|
| - * Parses the string and returns the resulting Json object.
|
| - *
|
| - * The optional [reviver] function is called once for each object or list
|
| - * property that has been parsed during decoding. The `key` argument is either
|
| - * the integer list index for a list property, the string map key for object
|
| - * properties, or `null` for the final result.
|
| - *
|
| - * The default [reviver] (when not provided) is the identity function.
|
| - */
|
| - dynamic decode(String source, {reviver(var key, var value)}) {
|
| - if (reviver == null) reviver = _reviver;
|
| - if (reviver == null) return decoder.convert(source);
|
| - return new JsonDecoder(reviver).convert(source);
|
| - }
|
| -
|
| - /**
|
| - * Converts [value] to a JSON string.
|
| - *
|
| - * If value contains objects that are not directly encodable to a JSON
|
| - * string (a value that is not a number, boolean, string, null, list or a map
|
| - * with string keys), the [toEncodable] function is used to convert it to an
|
| - * object that must be directly encodable.
|
| - *
|
| - * If [toEncodable] is omitted, it defaults to a function that returns the
|
| - * result of calling `.toJson()` on the unencodable object.
|
| - */
|
| - String encode(Object value, {toEncodable(object)}) {
|
| - if (toEncodable == null) toEncodable = _toEncodable;
|
| - if (toEncodable == null) return encoder.convert(value);
|
| - return new JsonEncoder(toEncodable).convert(value);
|
| - }
|
| -
|
| - JsonEncoder get encoder {
|
| - if (_toEncodable == null) return const JsonEncoder();
|
| - return new JsonEncoder(_toEncodable);
|
| - }
|
| -
|
| - JsonDecoder get decoder {
|
| - if (_reviver == null) return const JsonDecoder();
|
| - return new JsonDecoder(_reviver);
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * This class converts JSON objects to strings.
|
| - */
|
| -class JsonEncoder extends Converter<Object, String> {
|
| - /**
|
| - * The string used for indention.
|
| - *
|
| - * When generating multi-line output, this string is inserted once at the
|
| - * beginning of each indented line for each level of indentation.
|
| - *
|
| - * If `null`, the output is encoded as a single line.
|
| - */
|
| - final String indent;
|
| -
|
| - /**
|
| - * Function called on non-encodable objects to return a replacement
|
| - * encodable object that will be encoded in the orignal's place.
|
| - */
|
| - final _ToEncodable _toEncodable;
|
| -
|
| - /**
|
| - * Creates a JSON encoder.
|
| - *
|
| - * The JSON encoder handles numbers, strings, booleans, null, lists and
|
| - * maps directly.
|
| - *
|
| - * Any other object is attempted converted by [toEncodable] to an
|
| - * object that is of one of the convertible types.
|
| - *
|
| - * If [toEncodable] is omitted, it defaults to calling `.toJson()` on
|
| - * the object.
|
| - */
|
| - const JsonEncoder([toEncodable(nonSerializable)])
|
| - : this.indent = null,
|
| - this._toEncodable = toEncodable;
|
| -
|
| - /**
|
| - * Creates a JSON encoder that creates multi-line JSON.
|
| - *
|
| - * The encoding of elements of lists and maps are indented and put on separate
|
| - * lines. The [indent] string is prepended to these elements, once for each
|
| - * level of indentation.
|
| - *
|
| - * If [indent] is `null`, the output is encoded as a single line.
|
| - *
|
| - * The JSON encoder handles numbers, strings, booleans, null, lists and
|
| - * maps directly.
|
| - *
|
| - * Any other object is attempted converted by [toEncodable] to an
|
| - * object that is of one of the convertible types.
|
| - *
|
| - * If [toEncodable] is omitted, it defaults to calling `.toJson()` on
|
| - * the object.
|
| - */
|
| - const JsonEncoder.withIndent(this.indent, [toEncodable(nonSerializable)])
|
| - : this._toEncodable = toEncodable;
|
| -
|
| - /**
|
| - * Converts [object] to a JSON [String].
|
| - *
|
| - * Directly serializable values are [num], [String], [bool], and [Null], as
|
| - * well as some [List] and [Map] values. For [List], the elements must all be
|
| - * serializable. For [Map], the keys must be [String] and the values must be
|
| - * serializable.
|
| - *
|
| - * If a value of any other type is attempted to be serialized, the
|
| - * `toEncodable` function provided in the constructor is called with the value
|
| - * as argument. The result, which must be a directly serializable value, is
|
| - * serialized instead of the original value.
|
| - *
|
| - * If the conversion throws, or returns a value that is not directly
|
| - * serializable, a [JsonUnsupportedObjectError] exception is thrown.
|
| - * If the call throws, the error is caught and stored in the
|
| - * [JsonUnsupportedObjectError]'s [:cause:] field.
|
| - *
|
| - * If a [List] or [Map] contains a reference to itself, directly or through
|
| - * other lists or maps, it cannot be serialized and a [JsonCyclicError] is
|
| - * thrown.
|
| - *
|
| - * [object] should not change during serialization.
|
| - *
|
| - * If an object is serialized more than once, [convert] may cache the text
|
| - * for it. In other words, if the content of an object changes after it is
|
| - * first serialized, the new values may not be reflected in the result.
|
| - */
|
| - String convert(Object object) =>
|
| - _JsonStringStringifier.stringify(object, _toEncodable, indent);
|
| -
|
| - /**
|
| - * Starts a chunked conversion.
|
| - *
|
| - * The converter works more efficiently if the given [sink] is a
|
| - * [StringConversionSink].
|
| - *
|
| - * Returns a chunked-conversion sink that accepts at most one object. It is
|
| - * an error to invoke `add` more than once on the returned sink.
|
| - */
|
| - ChunkedConversionSink<Object> startChunkedConversion(Sink<String> sink) {
|
| - if (sink is! StringConversionSink) {
|
| - sink = new StringConversionSink.from(sink);
|
| - } else if (sink is _Utf8EncoderSink) {
|
| - return new _JsonUtf8EncoderSink(sink._sink, _toEncodable,
|
| - JsonUtf8Encoder._utf8Encode(indent),
|
| - JsonUtf8Encoder.DEFAULT_BUFFER_SIZE);
|
| - }
|
| - return new _JsonEncoderSink(sink, _toEncodable, indent);
|
| - }
|
| -
|
| - // Override the base class's bind, to provide a better type.
|
| - Stream<String> bind(Stream<Object> stream) => super.bind(stream);
|
| -
|
| - Converter<Object, dynamic/*=T*/> fuse/*<T>*/(
|
| - Converter<String, dynamic/*=T*/> other) {
|
| - if (other is Utf8Encoder) {
|
| - return new JsonUtf8Encoder(indent, _toEncodable)
|
| - as dynamic/*=Converter<Object, T>*/;
|
| - }
|
| - return super.fuse/*<T>*/(other);
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Encoder that encodes a single object as a UTF-8 encoded JSON string.
|
| - *
|
| - * This encoder works equivalently to first converting the object to
|
| - * a JSON string, and then UTF-8 encoding the string, but without
|
| - * creating an intermediate string.
|
| - */
|
| -class JsonUtf8Encoder extends Converter<Object, List<int>> {
|
| - /** Default buffer size used by the JSON-to-UTF-8 encoder. */
|
| - static const int DEFAULT_BUFFER_SIZE = 256;
|
| - /** Indentation used in pretty-print mode, `null` if not pretty. */
|
| - final List<int> _indent;
|
| - /** Function called with each un-encodable object encountered. */
|
| - final _ToEncodable _toEncodable;
|
| - /** UTF-8 buffer size. */
|
| - final int _bufferSize;
|
| -
|
| - /**
|
| - * Create converter.
|
| - *
|
| - * If [indent] is non-`null`, the converter attempts to "pretty-print" the
|
| - * JSON, and uses `indent` as the indentation. Otherwise the result has no
|
| - * whitespace outside of string literals.
|
| - * If `indent` contains characters that are not valid JSON whitespace
|
| - * characters, the result will not be valid JSON. JSON whitespace characters
|
| - * are space (U+0020), tab (U+0009), line feed (U+000a) and carriage return
|
| - * (U+000d) ([ECMA
|
| - * 404](http://www.ecma-international.org/publications/standards/Ecma-404.htm)).
|
| - *
|
| - * The [bufferSize] is the size of the internal buffers used to collect
|
| - * UTF-8 code units.
|
| - * If using [startChunkedConversion], it will be the size of the chunks.
|
| - *
|
| - * The JSON encoder handles numbers, strings, booleans, null, lists and maps
|
| - * directly.
|
| - *
|
| - * Any other object is attempted converted by [toEncodable] to an object that
|
| - * is of one of the convertible types.
|
| - *
|
| - * If [toEncodable] is omitted, it defaults to calling `.toJson()` on the
|
| - * object.
|
| - */
|
| - JsonUtf8Encoder([String indent,
|
| - toEncodable(object),
|
| - int bufferSize = DEFAULT_BUFFER_SIZE])
|
| - : _indent = _utf8Encode(indent),
|
| - _toEncodable = toEncodable,
|
| - _bufferSize = bufferSize;
|
| -
|
| - static List<int> _utf8Encode(String string) {
|
| - if (string == null) return null;
|
| - if (string.isEmpty) return new Uint8List(0);
|
| - checkAscii: {
|
| - for (int i = 0; i < string.length; i++) {
|
| - if (string.codeUnitAt(i) >= 0x80) break checkAscii;
|
| - }
|
| - return string.codeUnits;
|
| - }
|
| - return UTF8.encode(string);
|
| - }
|
| -
|
| - /** Convert [object] into UTF-8 encoded JSON. */
|
| - List<int> convert(Object object) {
|
| - List<List<int>> bytes = [];
|
| - // The `stringify` function always converts into chunks.
|
| - // Collect the chunks into the `bytes` list, then combine them afterwards.
|
| - void addChunk(Uint8List chunk, int start, int end) {
|
| - if (start > 0 || end < chunk.length) {
|
| - int length = end - start;
|
| - chunk = new Uint8List.view(chunk.buffer,
|
| - chunk.offsetInBytes + start,
|
| - length);
|
| - }
|
| - bytes.add(chunk);
|
| - }
|
| - _JsonUtf8Stringifier.stringify(object,
|
| - _indent,
|
| - _toEncodable,
|
| - _bufferSize,
|
| - addChunk);
|
| - if (bytes.length == 1) return bytes[0];
|
| - int length = 0;
|
| - for (int i = 0; i < bytes.length; i++) {
|
| - length += bytes[i].length;
|
| - }
|
| - Uint8List result = new Uint8List(length);
|
| - for (int i = 0, offset = 0; i < bytes.length; i++) {
|
| - var byteList = bytes[i];
|
| - int end = offset + byteList.length;
|
| - result.setRange(offset, end, byteList);
|
| - offset = end;
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - /**
|
| - * Start a chunked conversion.
|
| - *
|
| - * Only one object can be passed into the returned sink.
|
| - *
|
| - * The argument [sink] will receive byte lists in sizes depending on the
|
| - * `bufferSize` passed to the constructor when creating this encoder.
|
| - */
|
| - ChunkedConversionSink<Object> startChunkedConversion(Sink<List<int>> sink) {
|
| - ByteConversionSink byteSink;
|
| - if (sink is ByteConversionSink) {
|
| - byteSink = sink;
|
| - } else {
|
| - byteSink = new ByteConversionSink.from(sink);
|
| - }
|
| - return new _JsonUtf8EncoderSink(byteSink, _toEncodable,
|
| - _indent, _bufferSize);
|
| - }
|
| -
|
| - // Override the base class's bind, to provide a better type.
|
| - Stream<List<int>> bind(Stream<Object> stream) {
|
| - return super.bind(stream);
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Implements the chunked conversion from object to its JSON representation.
|
| - *
|
| - * The sink only accepts one value, but will produce output in a chunked way.
|
| - */
|
| -class _JsonEncoderSink extends ChunkedConversionSink<Object> {
|
| - final String _indent;
|
| - final _ToEncodable _toEncodable;
|
| - final StringConversionSink _sink;
|
| - bool _isDone = false;
|
| -
|
| - _JsonEncoderSink(this._sink, this._toEncodable, this._indent);
|
| -
|
| - /**
|
| - * Encodes the given object [o].
|
| - *
|
| - * It is an error to invoke this method more than once on any instance. While
|
| - * this makes the input effectly non-chunked the output will be generated in
|
| - * a chunked way.
|
| - */
|
| - void add(Object o) {
|
| - if (_isDone) {
|
| - throw new StateError("Only one call to add allowed");
|
| - }
|
| - _isDone = true;
|
| - ClosableStringSink stringSink = _sink.asStringSink();
|
| - _JsonStringStringifier.printOn(o, stringSink, _toEncodable, _indent);
|
| - stringSink.close();
|
| - }
|
| -
|
| - void close() { /* do nothing */ }
|
| -}
|
| -
|
| -/**
|
| - * Sink returned when starting a chunked conversion from object to bytes.
|
| - */
|
| -class _JsonUtf8EncoderSink extends ChunkedConversionSink<Object> {
|
| - /** The byte sink receiveing the encoded chunks. */
|
| - final ByteConversionSink _sink;
|
| - final List<int> _indent;
|
| - final _ToEncodable _toEncodable;
|
| - final int _bufferSize;
|
| - bool _isDone = false;
|
| - _JsonUtf8EncoderSink(this._sink, this._toEncodable, this._indent,
|
| - this._bufferSize);
|
| -
|
| - /** Callback called for each slice of result bytes. */
|
| - void _addChunk(Uint8List chunk, int start, int end) {
|
| - _sink.addSlice(chunk, start, end, false);
|
| - }
|
| -
|
| - void add(Object object) {
|
| - if (_isDone) {
|
| - throw new StateError("Only one call to add allowed");
|
| - }
|
| - _isDone = true;
|
| - _JsonUtf8Stringifier.stringify(object, _indent, _toEncodable,
|
| - _bufferSize,
|
| - _addChunk);
|
| - _sink.close();
|
| - }
|
| -
|
| - void close() {
|
| - if (!_isDone) {
|
| - _isDone = true;
|
| - _sink.close();
|
| - }
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * This class parses JSON strings and builds the corresponding objects.
|
| - */
|
| -class JsonDecoder extends Converter<String, Object> {
|
| - final _Reviver _reviver;
|
| - /**
|
| - * Constructs a new JsonDecoder.
|
| - *
|
| - * The [reviver] may be `null`.
|
| - */
|
| - const JsonDecoder([reviver(var key, var value)]) : this._reviver = reviver;
|
| -
|
| - /**
|
| - * Converts the given JSON-string [input] to its corresponding object.
|
| - *
|
| - * Parsed JSON values are of the types [num], [String], [bool], [Null],
|
| - * [List]s of parsed JSON values or [Map]s from [String] to parsed JSON
|
| - * values.
|
| - *
|
| - * If `this` was initialized with a reviver, then the parsing operation
|
| - * invokes the reviver on every object or list property that has been parsed.
|
| - * The arguments are the property name ([String]) or list index ([int]), and
|
| - * the value is the parsed value. The return value of the reviver is used as
|
| - * the value of that property instead the parsed value.
|
| - *
|
| - * Throws [FormatException] if the input is not valid JSON text.
|
| - */
|
| - dynamic convert(String input) => _parseJson(input, _reviver);
|
| -
|
| - /**
|
| - * Starts a conversion from a chunked JSON string to its corresponding object.
|
| - *
|
| - * The output [sink] receives exactly one decoded element through `add`.
|
| - */
|
| - external StringConversionSink startChunkedConversion(Sink<Object> sink);
|
| -
|
| - // Override the base class's bind, to provide a better type.
|
| - Stream<Object> bind(Stream<String> stream) => super.bind(stream);
|
| -}
|
| -
|
| -// Internal optimized JSON parsing implementation.
|
| -external _parseJson(String source, reviver(key, value));
|
| -
|
| -
|
| -// Implementation of encoder/stringifier.
|
| -
|
| -dynamic _defaultToEncodable(dynamic object) => object.toJson();
|
| -
|
| -/**
|
| - * JSON encoder that traverses an object structure and writes JSON source.
|
| - *
|
| - * This is an abstract implementation that doesn't decide on the output
|
| - * format, but writes the JSON through abstract methods like [writeString].
|
| - */
|
| -abstract class _JsonStringifier {
|
| - // Character code constants.
|
| - static const int BACKSPACE = 0x08;
|
| - static const int TAB = 0x09;
|
| - static const int NEWLINE = 0x0a;
|
| - static const int CARRIAGE_RETURN = 0x0d;
|
| - static const int FORM_FEED = 0x0c;
|
| - static const int QUOTE = 0x22;
|
| - static const int CHAR_0 = 0x30;
|
| - static const int BACKSLASH = 0x5c;
|
| - static const int CHAR_b = 0x62;
|
| - static const int CHAR_f = 0x66;
|
| - static const int CHAR_n = 0x6e;
|
| - static const int CHAR_r = 0x72;
|
| - static const int CHAR_t = 0x74;
|
| - static const int CHAR_u = 0x75;
|
| -
|
| - /** List of objects currently being traversed. Used to detect cycles. */
|
| - final List _seen = new List();
|
| - /** Function called for each un-encodable object encountered. */
|
| - final _ToEncodable _toEncodable;
|
| -
|
| - _JsonStringifier(toEncodable(o))
|
| - : _toEncodable = toEncodable ?? _defaultToEncodable;
|
| -
|
| - /** Append a string to the JSON output. */
|
| - void writeString(String characters);
|
| - /** Append part of a string to the JSON output. */
|
| - void writeStringSlice(String characters, int start, int end);
|
| - /** Append a single character, given by its code point, to the JSON output. */
|
| - void writeCharCode(int charCode);
|
| - /** Write a number to the JSON output. */
|
| - void writeNumber(num number);
|
| -
|
| - // ('0' + x) or ('a' + x - 10)
|
| - static int hexDigit(int x) => x < 10 ? 48 + x : 87 + x;
|
| -
|
| - /**
|
| - * Write, and suitably escape, a string's content as a JSON string literal.
|
| - */
|
| - void writeStringContent(String s) {
|
| - int offset = 0;
|
| - final int length = s.length;
|
| - for (int i = 0; i < length; i++) {
|
| - int charCode = s.codeUnitAt(i);
|
| - if (charCode > BACKSLASH) continue;
|
| - if (charCode < 32) {
|
| - if (i > offset) writeStringSlice(s, offset, i);
|
| - offset = i + 1;
|
| - writeCharCode(BACKSLASH);
|
| - switch (charCode) {
|
| - case BACKSPACE:
|
| - writeCharCode(CHAR_b);
|
| - break;
|
| - case TAB:
|
| - writeCharCode(CHAR_t);
|
| - break;
|
| - case NEWLINE:
|
| - writeCharCode(CHAR_n);
|
| - break;
|
| - case FORM_FEED:
|
| - writeCharCode(CHAR_f);
|
| - break;
|
| - case CARRIAGE_RETURN:
|
| - writeCharCode(CHAR_r);
|
| - break;
|
| - default:
|
| - writeCharCode(CHAR_u);
|
| - writeCharCode(CHAR_0);
|
| - writeCharCode(CHAR_0);
|
| - writeCharCode(hexDigit((charCode >> 4) & 0xf));
|
| - writeCharCode(hexDigit(charCode & 0xf));
|
| - break;
|
| - }
|
| - } else if (charCode == QUOTE || charCode == BACKSLASH) {
|
| - if (i > offset) writeStringSlice(s, offset, i);
|
| - offset = i + 1;
|
| - writeCharCode(BACKSLASH);
|
| - writeCharCode(charCode);
|
| - }
|
| - }
|
| - if (offset == 0) {
|
| - writeString(s);
|
| - } else if (offset < length) {
|
| - writeStringSlice(s, offset, length);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Check if an encountered object is already being traversed.
|
| - *
|
| - * Records the object if it isn't already seen. Should have a matching call to
|
| - * [_removeSeen] when the object is no longer being traversed.
|
| - */
|
| - void _checkCycle(object) {
|
| - for (int i = 0; i < _seen.length; i++) {
|
| - if (identical(object, _seen[i])) {
|
| - throw new JsonCyclicError(object);
|
| - }
|
| - }
|
| - _seen.add(object);
|
| - }
|
| -
|
| - /**
|
| - * Remove [object] from the list of currently traversed objects.
|
| - *
|
| - * Should be called in the opposite order of the matching [_checkCycle]
|
| - * calls.
|
| - */
|
| - void _removeSeen(object) {
|
| - assert(!_seen.isEmpty);
|
| - assert(identical(_seen.last, object));
|
| - _seen.removeLast();
|
| - }
|
| -
|
| - /**
|
| - * Write an object.
|
| - *
|
| - * If [object] isn't directly encodable, the [_toEncodable] function gets one
|
| - * chance to return a replacement which is encodable.
|
| - */
|
| - void writeObject(object) {
|
| - // Tries stringifying object directly. If it's not a simple value, List or
|
| - // Map, call toJson() to get a custom representation and try serializing
|
| - // that.
|
| - if (writeJsonValue(object)) return;
|
| - _checkCycle(object);
|
| - try {
|
| - var customJson = _toEncodable(object);
|
| - if (!writeJsonValue(customJson)) {
|
| - throw new JsonUnsupportedObjectError(object);
|
| - }
|
| - _removeSeen(object);
|
| - } catch (e) {
|
| - throw new JsonUnsupportedObjectError(object, cause: e);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Serialize a [num], [String], [bool], [Null], [List] or [Map] value.
|
| - *
|
| - * Returns true if the value is one of these types, and false if not.
|
| - * If a value is both a [List] and a [Map], it's serialized as a [List].
|
| - */
|
| - bool writeJsonValue(object) {
|
| - if (object is num) {
|
| - if (!object.isFinite) return false;
|
| - writeNumber(object);
|
| - return true;
|
| - } else if (identical(object, true)) {
|
| - writeString('true');
|
| - return true;
|
| - } else if (identical(object, false)) {
|
| - writeString('false');
|
| - return true;
|
| - } else if (object == null) {
|
| - writeString('null');
|
| - return true;
|
| - } else if (object is String) {
|
| - writeString('"');
|
| - writeStringContent(object);
|
| - writeString('"');
|
| - return true;
|
| - } else if (object is List) {
|
| - _checkCycle(object);
|
| - writeList(object);
|
| - _removeSeen(object);
|
| - return true;
|
| - } else if (object is Map) {
|
| - _checkCycle(object);
|
| - // writeMap can fail if keys are not all strings.
|
| - var success = writeMap(object);
|
| - _removeSeen(object);
|
| - return success;
|
| - } else {
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - /** Serialize a [List]. */
|
| - void writeList(List list) {
|
| - writeString('[');
|
| - if (list.length > 0) {
|
| - writeObject(list[0]);
|
| - for (int i = 1; i < list.length; i++) {
|
| - writeString(',');
|
| - writeObject(list[i]);
|
| - }
|
| - }
|
| - writeString(']');
|
| - }
|
| -
|
| - /** Serialize a [Map]. */
|
| - bool writeMap(Map map) {
|
| - if (map.isEmpty) {
|
| - writeString("{}");
|
| - return true;
|
| - }
|
| - List keyValueList = new List(map.length * 2);
|
| - int i = 0;
|
| - bool allStringKeys = true;
|
| - map.forEach((key, value) {
|
| - if (key is! String) {
|
| - allStringKeys = false;
|
| - }
|
| - keyValueList[i++] = key;
|
| - keyValueList[i++] = value;
|
| - });
|
| - if (!allStringKeys) return false;
|
| - writeString('{');
|
| - String separator = '"';
|
| - for (int i = 0; i < keyValueList.length; i += 2) {
|
| - writeString(separator);
|
| - separator = ',"';
|
| - writeStringContent(keyValueList[i]);
|
| - writeString('":');
|
| - writeObject(keyValueList[i + 1]);
|
| - }
|
| - writeString('}');
|
| - return true;
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * A modification of [_JsonStringifier] which indents the contents of [List] and
|
| - * [Map] objects using the specified indent value.
|
| - *
|
| - * Subclasses should implement [writeIndentation].
|
| - */
|
| -abstract class _JsonPrettyPrintMixin implements _JsonStringifier {
|
| - int _indentLevel = 0;
|
| -
|
| - /**
|
| - * Add [indentLevel] indentations to the JSON output.
|
| - */
|
| - void writeIndentation(int indentLevel);
|
| -
|
| - void writeList(List list) {
|
| - if (list.isEmpty) {
|
| - writeString('[]');
|
| - } else {
|
| - writeString('[\n');
|
| - _indentLevel++;
|
| - writeIndentation(_indentLevel);
|
| - writeObject(list[0]);
|
| - for (int i = 1; i < list.length; i++) {
|
| - writeString(',\n');
|
| - writeIndentation(_indentLevel);
|
| - writeObject(list[i]);
|
| - }
|
| - writeString('\n');
|
| - _indentLevel--;
|
| - writeIndentation(_indentLevel);
|
| - writeString(']');
|
| - }
|
| - }
|
| -
|
| - bool writeMap(Map map) {
|
| - if (map.isEmpty) {
|
| - writeString("{}");
|
| - return true;
|
| - }
|
| - List keyValueList = new List(map.length * 2);
|
| - int i = 0;
|
| - bool allStringKeys = true;
|
| - map.forEach((key, value) {
|
| - if (key is! String) {
|
| - allStringKeys = false;
|
| - }
|
| - keyValueList[i++] = key;
|
| - keyValueList[i++] = value;
|
| - });
|
| - if (!allStringKeys) return false;
|
| - writeString('{\n');
|
| - _indentLevel++;
|
| - String separator = "";
|
| - for (int i = 0; i < keyValueList.length; i += 2) {
|
| - writeString(separator);
|
| - separator = ",\n";
|
| - writeIndentation(_indentLevel);
|
| - writeString('"');
|
| - writeStringContent(keyValueList[i]);
|
| - writeString('": ');
|
| - writeObject(keyValueList[i + 1]);
|
| - }
|
| - writeString('\n');
|
| - _indentLevel--;
|
| - writeIndentation(_indentLevel);
|
| - writeString('}');
|
| - return true;
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * A specialziation of [_JsonStringifier] that writes its JSON to a string.
|
| - */
|
| -class _JsonStringStringifier extends _JsonStringifier {
|
| - final StringSink _sink;
|
| -
|
| - _JsonStringStringifier(this._sink, _toEncodable) : super(_toEncodable);
|
| -
|
| - /**
|
| - * Convert object to a string.
|
| - *
|
| - * The [toEncodable] function is used to convert non-encodable objects
|
| - * to encodable ones.
|
| - *
|
| - * If [indent] is not `null`, the resulting JSON will be "pretty-printed"
|
| - * with newlines and indentation. The `indent` string is added as indentation
|
| - * for each indentation level. It should only contain valid JSON whitespace
|
| - * characters (space, tab, carriage return or line feed).
|
| - */
|
| - static String stringify(object, toEncodable(o), String indent) {
|
| - StringBuffer output = new StringBuffer();
|
| - printOn(object, output, toEncodable, indent);
|
| - return output.toString();
|
| - }
|
| -
|
| - /**
|
| - * Convert object to a string, and write the result to the [output] sink.
|
| - *
|
| - * The result is written piecemally to the sink.
|
| - */
|
| - static void printOn(
|
| - object, StringSink output, toEncodable(o), String indent) {
|
| - var stringifier;
|
| - if (indent == null) {
|
| - stringifier = new _JsonStringStringifier(output, toEncodable);
|
| - } else {
|
| - stringifier =
|
| - new _JsonStringStringifierPretty(output, toEncodable, indent);
|
| - }
|
| - stringifier.writeObject(object);
|
| - }
|
| -
|
| - void writeNumber(num number) {
|
| - _sink.write(number.toString());
|
| - }
|
| - void writeString(String string) {
|
| - _sink.write(string);
|
| - }
|
| - void writeStringSlice(String string, int start, int end) {
|
| - _sink.write(string.substring(start, end));
|
| - }
|
| - void writeCharCode(int charCode) {
|
| - _sink.writeCharCode(charCode);
|
| - }
|
| -}
|
| -
|
| -class _JsonStringStringifierPretty extends _JsonStringStringifier
|
| - with _JsonPrettyPrintMixin {
|
| - final String _indent;
|
| -
|
| - _JsonStringStringifierPretty(StringSink sink, toEncodable(o), this._indent)
|
| - : super(sink, toEncodable);
|
| -
|
| - void writeIndentation(int count) {
|
| - for (int i = 0; i < count; i++) writeString(_indent);
|
| - }
|
| -}
|
| -
|
| -typedef void _AddChunk(Uint8List list, int start, int end);
|
| -
|
| -/**
|
| - * Specialization of [_JsonStringifier] that writes the JSON as UTF-8.
|
| - *
|
| - * The JSON text is UTF-8 encoded and written to [Uint8List] buffers.
|
| - * The buffers are then passed back to a user provided callback method.
|
| - */
|
| -class _JsonUtf8Stringifier extends _JsonStringifier {
|
| - final int bufferSize;
|
| - final _AddChunk addChunk;
|
| - Uint8List buffer;
|
| - int index = 0;
|
| -
|
| - _JsonUtf8Stringifier(toEncodable(o), int bufferSize, this.addChunk)
|
| - : this.bufferSize = bufferSize,
|
| - buffer = new Uint8List(bufferSize),
|
| - super(toEncodable);
|
| -
|
| - /**
|
| - * Convert [object] to UTF-8 encoded JSON.
|
| - *
|
| - * Calls [addChunk] with slices of UTF-8 code units.
|
| - * These will typically have size [bufferSize], but may be shorter.
|
| - * The buffers are not reused, so the [addChunk] call may keep and reuse the
|
| - * chunks.
|
| - *
|
| - * If [indent] is non-`null`, the result will be "pretty-printed" with extra
|
| - * newlines and indentation, using [indent] as the indentation.
|
| - */
|
| - static void stringify(Object object,
|
| - List<int> indent,
|
| - toEncodable(o),
|
| - int bufferSize,
|
| - void addChunk(Uint8List chunk, int start, int end)) {
|
| - _JsonUtf8Stringifier stringifier;
|
| - if (indent != null) {
|
| - stringifier = new _JsonUtf8StringifierPretty(toEncodable, indent,
|
| - bufferSize, addChunk);
|
| - } else {
|
| - stringifier = new _JsonUtf8Stringifier(toEncodable, bufferSize, addChunk);
|
| - }
|
| - stringifier.writeObject(object);
|
| - stringifier.flush();
|
| - }
|
| -
|
| - /**
|
| - * Must be called at the end to push the last chunk to the [addChunk]
|
| - * callback.
|
| - */
|
| - void flush() {
|
| - if (index > 0) {
|
| - addChunk(buffer, 0, index);
|
| - }
|
| - buffer = null;
|
| - index = 0;
|
| - }
|
| -
|
| - void writeNumber(num number) {
|
| - writeAsciiString(number.toString());
|
| - }
|
| -
|
| - /** Write a string that is known to not have non-ASCII characters. */
|
| - void writeAsciiString(String string) {
|
| - // TODO(lrn): Optimize by copying directly into buffer instead of going
|
| - // through writeCharCode;
|
| - for (int i = 0; i < string.length; i++) {
|
| - int char = string.codeUnitAt(i);
|
| - assert(char <= 0x7f);
|
| - writeByte(char);
|
| - }
|
| - }
|
| -
|
| - void writeString(String string) {
|
| - writeStringSlice(string, 0, string.length);
|
| - }
|
| -
|
| - void writeStringSlice(String string, int start, int end) {
|
| - // TODO(lrn): Optimize by copying directly into buffer instead of going
|
| - // through writeCharCode/writeByte. Assumption is the most characters
|
| - // in starings are plain ASCII.
|
| - for (int i = start; i < end; i++) {
|
| - int char = string.codeUnitAt(i);
|
| - if (char <= 0x7f) {
|
| - writeByte(char);
|
| - } else {
|
| - if ((char & 0xFC00) == 0xD800 && i + 1 < end) {
|
| - // Lead surrogate.
|
| - int nextChar = string.codeUnitAt(i + 1);
|
| - if ((nextChar & 0xFC00) == 0xDC00) {
|
| - // Tail surrogate.
|
| - char = 0x10000 + ((char & 0x3ff) << 10) + (nextChar & 0x3ff);
|
| - writeFourByteCharCode(char);
|
| - i++;
|
| - continue;
|
| - }
|
| - }
|
| - writeMultiByteCharCode(char);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void writeCharCode(int charCode) {
|
| - if (charCode <= 0x7f) {
|
| - writeByte(charCode);
|
| - return;
|
| - }
|
| - writeMultiByteCharCode(charCode);
|
| - }
|
| -
|
| - void writeMultiByteCharCode(int charCode) {
|
| - if (charCode <= 0x7ff) {
|
| - writeByte(0xC0 | (charCode >> 6));
|
| - writeByte(0x80 | (charCode & 0x3f));
|
| - return;
|
| - }
|
| - if (charCode <= 0xffff) {
|
| - writeByte(0xE0 | (charCode >> 12));
|
| - writeByte(0x80 | ((charCode >> 6) & 0x3f));
|
| - writeByte(0x80 | (charCode & 0x3f));
|
| - return;
|
| - }
|
| - writeFourByteCharCode(charCode);
|
| - }
|
| -
|
| - void writeFourByteCharCode(int charCode) {
|
| - assert(charCode <= 0x10ffff);
|
| - writeByte(0xF0 | (charCode >> 18));
|
| - writeByte(0x80 | ((charCode >> 12) & 0x3f));
|
| - writeByte(0x80 | ((charCode >> 6) & 0x3f));
|
| - writeByte(0x80 | (charCode & 0x3f));
|
| - }
|
| -
|
| - void writeByte(int byte) {
|
| - assert(byte <= 0xff);
|
| - if (index == buffer.length) {
|
| - addChunk(buffer, 0, index);
|
| - buffer = new Uint8List(bufferSize);
|
| - index = 0;
|
| - }
|
| - buffer[index++] = byte;
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Pretty-printing version of [_JsonUtf8Stringifier].
|
| - */
|
| -class _JsonUtf8StringifierPretty extends _JsonUtf8Stringifier
|
| - with _JsonPrettyPrintMixin {
|
| - final List<int> indent;
|
| - _JsonUtf8StringifierPretty(
|
| - toEncodable(o), this.indent,
|
| - bufferSize, void addChunk(Uint8List buffer, int start, int end))
|
| - : super(toEncodable, bufferSize, addChunk);
|
| -
|
| - void writeIndentation(int count) {
|
| - List<int> indent = this.indent;
|
| - int indentLength = indent.length;
|
| - if (indentLength == 1) {
|
| - int char = indent[0];
|
| - while (count > 0) {
|
| - writeByte(char);
|
| - count -= 1;
|
| - }
|
| - return;
|
| - }
|
| - while (count > 0) {
|
| - count--;
|
| - int end = index + indentLength;
|
| - if (end <= buffer.length) {
|
| - buffer.setRange(index, end, indent);
|
| - index = end;
|
| - } else {
|
| - for (int i = 0; i < indentLength; i++) {
|
| - writeByte(indent[i]);
|
| - }
|
| - }
|
| - }
|
| - }
|
| -}
|
|
|