| Index: sdk/lib/convert/string_converter.dart
|
| diff --git a/sdk/lib/convert/string_converter.dart b/sdk/lib/convert/string_converter.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..9847f9dc70edd9fa84fb61d8197897915a4691cb
|
| --- /dev/null
|
| +++ b/sdk/lib/convert/string_converter.dart
|
| @@ -0,0 +1,370 @@
|
| +// 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;
|
| +
|
| +class _StringInterface extends ChunkedConversionInterface {
|
| + const _StringInterface();
|
| +
|
| + StringConversionSink adapt(ChunkedConversionSink sink) {
|
| + if (sink.interface == StringConversionSink.INTERFACE) return sink;
|
| + // Common interface is ChunkedConversionSink.
|
| + return new _StringAdapterSink(sink);
|
| + }
|
| +
|
| + StringConversionSink adaptEventSink(EventSink sink) {
|
| + return new _EventSinkStringAdapterSink(sink);
|
| + }
|
| +}
|
| +
|
| +abstract class StringConversionSink
|
| + extends ChunkedConversionSink<String>
|
| + implements StringSink {
|
| +
|
| + StringConversionSink();
|
| + factory StringConversionSink.withStringSink(StringSink sink) =
|
| + _StringSinkConversionSink;
|
| +
|
| + void addString(String str, bool isLast);
|
| + void addCodeUnits(List<int> codeUnits, int start, int end, bool isLast);
|
| + void addRunes(List<int> runes, int start, int end, bool isLast);
|
| + void addUtf8(List<int> utf8Units, int start, int end, bool isLast);
|
| + void addIsoLatin1(List<int> codeUnits, int start, int end, bool isLast);
|
| + void addAscii(List<int> codeUnits, int start, int end, bool isLast);
|
| + void iterateCodeUnits(StringIterator iterator, bool isLast);
|
| + void iterateRunes(StringIterator iterator, bool isLast);
|
| + void iterateUtf8(StringIterator iterator, bool isLast);
|
| + void iterateIsoLatin1(StringIterator iterator, bool isLast);
|
| + void iterateAscii(StringIterator iterator, bool isLast);
|
| + void close(); // Equivalent to addString("", true);
|
| +
|
| + static const ChunkedConversionInterface INTERFACE = const _StringInterface();
|
| +}
|
| +
|
| +class _StringSinkConversionSink extends StringConversionSinkBase {
|
| + StringSink _sink;
|
| + _StringSinkConversionSink(StringSink this._sink);
|
| +
|
| + void add(String str) => write(str);
|
| + void close() { /* do nothing */ }
|
| +
|
| + void write(Object o) => _sink.write(o);
|
| + void writeln([Object o]) => _sink.writeln(o);
|
| + void writeCharCode(int charCode) => _sink.writeCharCode(charCode);
|
| + void writeAll(Iterable objects, [String separator = ""])
|
| + => _sink.writeAll(objects, separator);
|
| +}
|
| +
|
| +class _StringAdapterSink extends StringConversionSinkBase {
|
| + final ChunkedConversionSink _otherSink;
|
| + final StringBuffer _buffer = new StringBuffer();
|
| +
|
| + _StringAdapterSink(this._otherSink);
|
| +
|
| + void addNonChunked(String str) => _otherSink.addNonChunked(str);
|
| +
|
| + void add(String str) => _buffer.write(str);
|
| + void close() => _otherSink.addNonChunked(_buffer.toString());
|
| +
|
| + void write(Object o) => _buffer.write(o);
|
| + void writeln([Object o]) => _buffer.writeln(o);
|
| + void writeCharCode(int charCode) => _buffer.writeCharCode(charCode);
|
| + void writeAll(Iterable objects, [String separator = ""])
|
| + => _buffer.writeAll(objects, separator);
|
| +}
|
| +
|
| +class _EventSinkStringAdapterSink extends StringConversionSinkBase {
|
| + final EventSink _sink;
|
| +
|
| + _EventSinkStringAdapterSink(this._sink);
|
| +
|
| + void add(String str) => _sink.add(str);
|
| + void close() => _sink.close();
|
| +}
|
| +
|
| +abstract class _StringMultiStringConversionSinkBase
|
| + extends ChunkedConversionSink {
|
| + var _handler = null;
|
| +
|
| + void add(String str);
|
| +
|
| + void addString(String str, bool isLast) {
|
| + add(str);
|
| + if (isLast) close();
|
| + }
|
| +
|
| + List<int> _sublist(List<int> list, int start, int end) {
|
| + if (start != 0 || end != list.length) return list.sublist(start, end);
|
| + return list;
|
| + }
|
| + void addCodeUnits(List<int> codeUnits, int start, int end, bool isLast) {
|
| + addString(new String.fromCharCodes(_sublist(codeUnits, start, end)),
|
| + isLast);
|
| + }
|
| + void addRunes(List<int> runes, int start, int end, bool isLast) {
|
| + addString(new String.fromCharCodes(_sublist(runes, start, end)), isLast);
|
| + }
|
| + void addUtf8(List<int> utf8Units, int start, int end, bool isLast) {
|
| + if (_handler == null) {
|
| + _handler = new Utf8Handler();
|
| + } else if (_handler is! Utf8Handler) {
|
| + throw new UnimplementedError("Switching handlers");
|
| + }
|
| + Utf8Handler handler = _handler;
|
| + String string = handler.convert(utf8Units, start, end);
|
| + bool isDone = isLast && !handler.hasPartialInput;
|
| + addString(string, isDone);
|
| + if (isLast && handler.hasPartialInput) {
|
| + throw new StateError("Bad input");
|
| + }
|
| + }
|
| + void addIsoLatin1(List<int> codeUnits, int start, int end, bool isLast) {
|
| + addRunes(codeUnits, start, end, isLast);
|
| + }
|
| + void addAscii(List<int> codeUnits, int start, int end, bool isLast) {
|
| + addIsoLatin1(codeUnits, start, end, isLast);
|
| + }
|
| + void iterateCodeUnits(StringIterator iterator, bool isLast) {
|
| + addString(iterator.convertToString(), isLast);
|
| + }
|
| + void iterateRunes(StringIterator iterator, bool isLast) {
|
| + addString(iterator.convertToString(), isLast);
|
| + }
|
| + void iterateUtf8(StringIterator iterator, bool isLast) {
|
| + addString(iterator.convertToString(), isLast);
|
| + }
|
| + void iterateIsoLatin1(StringIterator iterator, bool isLast) {
|
| + addString(iterator.convertToString(), isLast);
|
| + }
|
| + void iterateAscii(StringIterator iterator, bool isLast) {
|
| + addString(iterator.convertToString(), isLast);
|
| + }
|
| +
|
| + void write(Object o) { addString(o.toString(), false); }
|
| + void writeln([Object o]) {
|
| + if (o != null) {
|
| + addString(o.toString(), false);
|
| + }
|
| + addString("\n", false);
|
| + }
|
| + void writeCharCode(int charCode) {
|
| + addString(new String.fromCharCode(charCode), false);
|
| + }
|
| + void writeAll(Iterable objects, [String separator = ""]) {
|
| + addString(objects.join(separator), false);
|
| + }
|
| +
|
| + ChunkedConversionInterface get interface => StringConversionSink.INTERFACE;
|
| +}
|
| +
|
| +abstract class StringConversionSinkBase
|
| + extends _StringMultiStringConversionSinkBase
|
| + implements StringConversionSink {
|
| +
|
| + void addNonChunked(String str) => addString(str, true);
|
| +
|
| + close() => addString("", true);
|
| +}
|
| +
|
| +class Utf8Handler {
|
| + int _value = 0;
|
| + int _expectedUnits = 0;
|
| + int _extraUnits = 0;
|
| +
|
| + bool get hasPartialInput => _expectedUnits > 0;
|
| +
|
| + // Limits of one through four byte encodings.
|
| + static const List<int> _LIMITS = const <int>[
|
| + _ONE_BYTE_LIMIT,
|
| + _TWO_BYTE_LIMIT,
|
| + _THREE_BYTE_LIMIT,
|
| + _FOUR_BYTE_LIMIT ];
|
| +
|
| + /**
|
| + * This method will call _partialState for unfinished unicode sequences.
|
| + */
|
| + String convert(List<int> codeUnits, int startIndex, int endIndex) {
|
| + StringBuffer buffer = new StringBuffer();
|
| + convertToSink(codeUnits, startIndex, endIndex, buffer);
|
| + return buffer.toString();
|
| + }
|
| +
|
| + /**
|
| + * This method will call _partialState for unfinished unicode sequences.
|
| + */
|
| + void convertToSink(List<int> codeUnits, int startIndex, int endIndex,
|
| + StringSink sink) {
|
| + int value = _value;
|
| + int expectedUnits = _expectedUnits;
|
| + int extraUnits = _extraUnits;
|
| + _value = 0;
|
| + _expectedUnits = 0;
|
| + _extraUnits = 0;
|
| +
|
| + int i = startIndex;
|
| + loop: while (true) {
|
| + multibyte: if (expectedUnits > 0) {
|
| + do {
|
| + if (i == endIndex) {
|
| + break loop;
|
| + }
|
| + int unit = codeUnits[i];
|
| + if ((unit & 0xC0) != 0x80) {
|
| + int partialValue = value;
|
| + value = expectedUnits = 0;
|
| + if (!_onError(partialValue)) break loop;
|
| + break multibyte;
|
| + } else {
|
| + value = (value << 6) | (unit & 0x3f);
|
| + expectedUnits--;
|
| + i++;
|
| + }
|
| + } while (expectedUnits > 0);
|
| + if (value <= _LIMITS[extraUnits - 1]) {
|
| + // Overly long encoding. The value could be encoded with a shorter
|
| + // encoding.
|
| + if (!_onError(value)) break loop;
|
| + }
|
| + sink.writeCharCode(value);
|
| + }
|
| +
|
| + while (i < endIndex) {
|
| + int unit = codeUnits[i++];
|
| + if (unit <= _ONE_BYTE_LIMIT) {
|
| + sink.writeCharCode(unit);
|
| + } else {
|
| + if ((unit & 0xE0) == 0xC0) {
|
| + value = unit & 0x1F;
|
| + expectedUnits = extraUnits = 1;
|
| + continue loop;
|
| + }
|
| + if ((unit & 0xF0) == 0xE0) {
|
| + value = unit & 0x0F;
|
| + expectedUnits = extraUnits = 2;
|
| + continue loop;
|
| + }
|
| + if ((unit & 0xF8) == 0xF0) {
|
| + value = unit & 0x07;
|
| + expectedUnits = extraUnits = 3;
|
| + continue loop;
|
| + }
|
| + if (!_onError(unit)) {
|
| + break loop;
|
| + }
|
| + }
|
| + }
|
| + break loop;
|
| + }
|
| + if (expectedUnits > 0) {
|
| + _value = value;
|
| + _expectedUnits = expectedUnits;
|
| + _extraUnits = extraUnits;
|
| + }
|
| + }
|
| +
|
| + bool _onError(int unexpectedByte) {
|
| + throw new StateError
|
| + ("Unexpected byte 0x${unexpectedByte.toRadixString(16)}.");
|
| + }
|
| +}
|
| +
|
| +abstract class StringIterator extends Iterator {
|
| + /**
|
| + * Fills [list] at position [startIndex] up to [endIndex] (exclusive) with
|
| + * the next elements. Returns the next unwritten index.
|
| + */
|
| + int fill(List<int> list, int startIndex, int endIndex);
|
| +
|
| + /**
|
| + * Returns the string representation of the full content of this iterator.
|
| + */
|
| + String convertToString();
|
| +
|
| + StringIterator asRunes();
|
| + StringIterator asCodeUnits();
|
| + StringIterator asUtf8();
|
| +}
|
| +
|
| +
|
| +class _MultiStringInterface extends ChunkedConversionInterface {
|
| + const _MultiStringInterface();
|
| +
|
| + MultiStringConversionSink adapt(ChunkedConversionSink sink) {
|
| + if (sink.interface == MultiStringConversionSink.INTERFACE) return sink;
|
| + // Common interface is ChunkedConversionSink.
|
| + return new _MultiStringAdapterSink(sink);
|
| + }
|
| +
|
| + MultiStringConversionSink adaptEventSink(EventSink sink) {
|
| + return new _EventSinkMultiStringAdapterSink(sink);
|
| + }
|
| +}
|
| +
|
| +abstract class MultiStringConversionSink
|
| + extends ChunkedConversionSink<List<String>>
|
| + implements StringSink {
|
| +
|
| + MultiStringConversionSink();
|
| +
|
| + void addString(String str, bool isLast);
|
| + void addCodeUnits(List<int> codeUnits, int start, int end, bool isLast);
|
| + void addRunes(List<int> runes, int start, int end, bool isLast);
|
| + void addUtf8(List<int> utf8Units, int start, int end, bool isLast);
|
| + void addIsoLatin1(List<int> codeUnits, int start, int end, bool isLast);
|
| + void addAscii(List<int> codeUnits, int start, int end, bool isLast);
|
| + void iterateCodeUnits(StringIterator iterator, bool isLast);
|
| + void iterateRunes(StringIterator iterator, bool isLast);
|
| + void iterateUtf8(StringIterator iterator, bool isLast);
|
| + void iterateIsoLatin1(StringIterator iterator, bool isLast);
|
| + void iterateAscii(StringIterator iterator, bool isLast);
|
| + void close();
|
| +
|
| + static const ChunkedConversionInterface INTERFACE =
|
| + const _MultiStringInterface();
|
| +}
|
| +
|
| +abstract class MultiStringConversionSinkBase
|
| + extends _StringMultiStringConversionSinkBase
|
| + implements MultiStringConversionSink {
|
| +
|
| + void addNonChunked(List<String> list) {
|
| + for (int i = 0; i < list.length - 1; i++) {
|
| + addString(list[i], false);
|
| + }
|
| + if (!list.isEmpty) {
|
| + addString(list.last, true);
|
| + } else {
|
| + close();
|
| + }
|
| + }
|
| +
|
| + ChunkedConversionInterface get interface =>
|
| + MultiStringConversionSink.INTERFACE;
|
| +}
|
| +
|
| +
|
| +class _MultiStringAdapterSink extends MultiStringConversionSinkBase
|
| + implements MultiStringConversionSink {
|
| + final ChunkedConversionSink _otherSink;
|
| + final List<String> _accumulator = <String>[];
|
| +
|
| + _MultiStringAdapterSink(this._otherSink);
|
| +
|
| + void addNonChunked(List<String> list) {
|
| + _otherSink.addNonChunked(list.join("\n"));
|
| + }
|
| +
|
| + void add(String str) => _accumulator.add(str);
|
| + void close() { _otherSink.addNonChunked(_accumulator); }
|
| +}
|
| +
|
| +class _EventSinkMultiStringAdapterSink extends MultiStringConversionSinkBase
|
| + implements MultiStringConversionSink {
|
| + final EventSink _sink;
|
| +
|
| + _EventSinkMultiStringAdapterSink(this._sink);
|
| +
|
| + void add(String str) => _sink.add(str);
|
| + void close() { _sink.close(); }
|
| +}
|
|
|