| Index: sdk/lib/convert/line_splitter.dart
|
| diff --git a/sdk/lib/convert/line_splitter.dart b/sdk/lib/convert/line_splitter.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c29b2c730feff884c216c5345c060d19db730ae1
|
| --- /dev/null
|
| +++ b/sdk/lib/convert/line_splitter.dart
|
| @@ -0,0 +1,288 @@
|
| +// 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 LineSplitter extends Converter<String, List<String>> {
|
| + LineSplitter();
|
| +
|
| + ChunkedConversionSink<Object> startChunkedConversion(
|
| + ChunkedConversionSink sink) {
|
| + return new _LineSplitterSink(outputInterface.adapt(sink));
|
| + }
|
| +
|
| + ChunkedConversionInterface get inputInterface =>
|
| + StringConversionSink.INTERFACE;
|
| +
|
| + ChunkedConversionInterface get outputInterface =>
|
| + MultiStringConversionSink.INTERFACE;
|
| +}
|
| +
|
| +class LineFuser extends Converter<List<String>, String> {
|
| + LineFuser();
|
| +
|
| + String convert(List<String> input) => input.join("\n");
|
| +
|
| + fuse(Converter other) => other.fuseInput(this);
|
| + fuseInput(Converter other) => new FusedConverter(other, this);
|
| +
|
| + MultiStringConversionSink startChunkedConversion(ChunkedConversionSink sink) {
|
| + return new _LineFuserSink(outputInterface.adapt(sink));
|
| + }
|
| +
|
| + ChunkedConversionInterface get inputInterface =>
|
| + StringConversionSink.INTERFACE;
|
| +}
|
| +
|
| +class _LineSplitterSink extends StringConversionSinkBase {
|
| + static const int _LF = 10;
|
| + static const int _CR = 13;
|
| +
|
| + Utf8Handler _utf8Handler;
|
| + MultiStringConversionSink _sink;
|
| + StringBuffer _carry;
|
| + // True if the last chunk finished with a _CR.
|
| + bool _ignoreLineFeed = false;
|
| +
|
| + _LineSplitterSink(this._sink);
|
| +
|
| + bool get _hasPartialUtf8Input {
|
| + return _utf8Handler != null && _utf8Handler.hasPartialInput;
|
| + }
|
| +
|
| + bool get _hasPartialInput {
|
| + if (_carry != null && _carry.isNotEmpty) return true;
|
| + return _hasPartialUtf8Input;
|
| + }
|
| +
|
| + int _findFirstLineSplit(List<int> charCodes, int start, int end) {
|
| + for (int i = start; i < end; i++) {
|
| + int codeUnit = charCodes[i];
|
| + if (codeUnit == _LF || codeUnit == _CR) {
|
| + return i;
|
| + };
|
| + }
|
| + return end;
|
| + }
|
| +
|
| + void _addCarry(List<int> charCodes, int start, int end) {
|
| + if (_utf8Handler != null && _utf8Handler.hasPartialInput) {
|
| + throw "Incomplete UTF8 sequence";
|
| + }
|
| + if (_carry == null) _carry = new StringBuffer();
|
| + for (int i = start; i < end; i++) {
|
| + _carry.writeCharCode(charCodes[i]);
|
| + }
|
| + }
|
| +
|
| + void _addUtf8Carry(List<int> codeUnits, int start, int end) {
|
| + if (_utf8Handler == null) _utf8Handler = new Utf8Handler();
|
| + if (_carry == null) _carry = new StringBuffer();
|
| + _utf8Handler.convertToSink(codeUnits, start, end, _carry);
|
| + }
|
| +
|
| + void _split(
|
| + List<int> charCodes,
|
| + int startIndex,
|
| + int endIndex,
|
| + bool isLast,
|
| + void addLine(List<int> charCodes, int start, int end, bool isLast),
|
| + void addCarry(List<int> charCodes, int start, int end)) {
|
| + int lineStart = startIndex;
|
| + // If the last chunk ended with a carriage-return we have to ignore a
|
| + // leading line feed.
|
| + if (_ignoreLineFeed) {
|
| + if (startIndex != endIndex && charCodes[startIndex] == _LF) {
|
| + startIndex++;
|
| + _ignoreLineFeed = false;
|
| + }
|
| + }
|
| + // Set the `_ignoreLineFeed` boolean for the next chunk.
|
| + if (startIndex != endIndex) {
|
| + if (charCodes[endIndex - 1] == _CR) {
|
| + _ignoreLineFeed = true;
|
| + }
|
| + }
|
| + // If there is a carry we have to append the next part of the line to the
|
| + // buffer.
|
| + if (_hasPartialInput) {
|
| + int firstSplit = _findFirstLineSplit(charCodes, startIndex, endIndex);
|
| + addCarry(charCodes, startIndex, firstSplit);
|
| + if (firstSplit == endIndex) {
|
| + // If `isLast` then nothing comes anymore. Send the carry.
|
| + if (isLast) {
|
| + if (_hasPartialUtf8Input) {
|
| + throw "Incomplete Utf8 sequence";
|
| + }
|
| + _sink.addString(_carry.toString(), true);
|
| + }
|
| + // Otherwise just return and wait for the next chunk.
|
| + return;
|
| + } else {
|
| + // Send the string that is in the carry and reset it.
|
| + _sink.addString(_carry.toString(), false);
|
| + _carry.clear();
|
| + lineStart = firstSplit + 1;
|
| + if (charCodes[firstSplit] == _CR &&
|
| + firstSplit + 1 < endIndex &&
|
| + charCodes[firstSplit + 1] == _LF) {
|
| + // Ignore LF following a CR.
|
| + lineStart++;
|
| + }
|
| + }
|
| + }
|
| + assert(!_hasPartialInput);
|
| +
|
| + // At this point the carry is empty. We are now exclusively working on the
|
| + // new chunk.
|
| + for (int i = lineStart; i < endIndex; i++) {
|
| + int codeUnit = charCodes[i];
|
| + if (codeUnit == _LF) {
|
| + bool isLastLine = isLast && i == endIndex - 1;
|
| + addLine(charCodes, lineStart, i, isLastLine);
|
| + lineStart = i + 1;
|
| + } else if (codeUnit == _CR) {
|
| + int lineEnd = i;
|
| + if (i + 1 < charCodes.length && charCodes[i + 1] == _LF) {
|
| + i++;
|
| + }
|
| + bool isLastLine = isLast && i == endIndex - 1;
|
| + addLine(charCodes, lineStart, lineEnd, isLastLine);
|
| + lineStart = i + 1;
|
| + }
|
| + }
|
| + if (lineStart != endIndex) {
|
| + // The last part of the chunk was not consumed. Store it in the carry or
|
| + // send it directly if this is the last chunk.
|
| + if (isLast) {
|
| + addLine(charCodes, lineStart, endIndex, true);
|
| + } else {
|
| + addCarry(charCodes, lineStart, endIndex);
|
| + }
|
| + } else if (isLast) {
|
| + _sink.close();
|
| + }
|
| + }
|
| +
|
| + void add(String str) => addString(str, false);
|
| +
|
| + void addString(String str, bool isLast) {
|
| + _split(str.codeUnits, 0, str.length, isLast, _sink.addCodeUnits, _addCarry);
|
| + }
|
| +
|
| + void addCodeUnits(List<int> codeUnits, int start, int end, bool isLast) {
|
| + _split(codeUnits, start, end, isLast, _sink.addCodeUnits, _addCarry);
|
| + }
|
| +
|
| + void addRunes(List<int> runes, int start, int end, bool isLast) {
|
| + _split(runes, start, end, isLast, _sink.addRunes, _addCarry);
|
| + }
|
| +
|
| + void addUtf8(List<int> utf8Units, int start, int end, bool isLast) {
|
| + _split(utf8Units, start, end, isLast, _sink.addUtf8, _addUtf8Carry);
|
| + }
|
| +
|
| + void addIsoLatin1(List<int> codeUnits, int start, int end, bool isLast) {
|
| + _split(codeUnits, start, end, isLast, _sink.addIsoLatin1, _addCarry);
|
| + }
|
| +
|
| + void addAscii(List<int> codeUnits, int start, int end, bool isLast) {
|
| + _split(codeUnits, start, end, isLast, _sink.addAscii, _addCarry);
|
| + }
|
| +}
|
| +
|
| +class _LineFuserSink implements MultiStringConversionSink {
|
| + static const _NL = 10; // New line code.
|
| + bool needsSeparator = false;
|
| + StringConversionSink _sink;
|
| +
|
| + _LineFuserSink(this._sink);
|
| +
|
| + var _handler = null;
|
| +
|
| + void writeNewLineIfNecessary() {
|
| + if (needsSeparator) _sink.writeCharCode(_NL);
|
| + needsSeparator = true;
|
| + }
|
| +
|
| + void addNonChunked(List<String> list) {
|
| + _sink.writeAll(list, "\n");
|
| + _sink.close();
|
| + }
|
| +
|
| + void add(String str) {
|
| + writeNewLineIfNecessary();
|
| + _sink.add(str);
|
| + }
|
| +
|
| + void addString(String str, bool isLast) {
|
| + writeNewLineIfNecessary();
|
| + _sink.addString(str, isLast);
|
| + }
|
| +
|
| + void addCodeUnits(List<int> codeUnits, int start, int end, bool isLast) {
|
| + writeNewLineIfNecessary();
|
| + _sink.addCodeUnits(codeUnits, start, end, isLast);
|
| + }
|
| + void addRunes(List<int> runes, int start, int end, bool isLast) {
|
| + writeNewLineIfNecessary();
|
| + _sink.addRunes(runes, start, end, isLast);
|
| + }
|
| + void addUtf8(List<int> utf8Units, int start, int end, bool isLast) {
|
| + writeNewLineIfNecessary();
|
| + _sink.addUtf8(utf8Units, start, end, isLast);
|
| + }
|
| + void addIsoLatin1(List<int> codeUnits, int start, int end, bool isLast) {
|
| + writeNewLineIfNecessary();
|
| + _sink.addIsoLatin1(codeUnits, start, end, isLast);
|
| + }
|
| + void addAscii(List<int> codeUnits, int start, int end, bool isLast) {
|
| + writeNewLineIfNecessary();
|
| + _sink.addAscii(codeUnits, start, end, isLast);
|
| + }
|
| + void iterateCodeUnits(StringIterator iterator, bool isLast) {
|
| + writeNewLineIfNecessary();
|
| + _sink.iterateCodeUnits(iterator, isLast);
|
| + }
|
| + void iterateRunes(StringIterator iterator, bool isLast) {
|
| + writeNewLineIfNecessary();
|
| + _sink.iterateRunes(iterator, isLast);
|
| + }
|
| + void iterateUtf8(StringIterator iterator, bool isLast) {
|
| + writeNewLineIfNecessary();
|
| + _sink.iterateUtf8(iterator, isLast);
|
| + }
|
| + void iterateIsoLatin1(StringIterator iterator, bool isLast) {
|
| + writeNewLineIfNecessary();
|
| + _sink.iterateIsoLatin1(iterator, isLast);
|
| + }
|
| + void iterateAscii(StringIterator iterator, bool isLast) {
|
| + writeNewLineIfNecessary();
|
| + _sink.iterateAscii(iterator, isLast);
|
| + }
|
| +
|
| + void write(Object o) {
|
| + writeNewLineIfNecessary();
|
| + _sink.write(o);
|
| + }
|
| + void writeln([Object o]) {
|
| + writeNewLineIfNecessary();
|
| + _sink.writeln(o);
|
| + }
|
| + void writeCharCode(int charCode) {
|
| + writeNewLineIfNecessary();
|
| + _sink.writeCharCode(charCode);
|
| + }
|
| + void writeAll(Iterable objects, [String separator = ""]) {
|
| + writeNewLineIfNecessary();
|
| + _sink.writeAll(objects, separator);
|
| + }
|
| +
|
| + close() {
|
| + _sink.close();
|
| + }
|
| +
|
| + ChunkedConversionInterface get interface =>
|
| + MultiStringConversionSink.INTERFACE;
|
| +}
|
|
|