| Index: sdk/lib/convert/converter.dart
|
| diff --git a/sdk/lib/convert/converter.dart b/sdk/lib/convert/converter.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..1e3f42c4cc8971f305d64e54665bee27cbdbf56c
|
| --- /dev/null
|
| +++ b/sdk/lib/convert/converter.dart
|
| @@ -0,0 +1,194 @@
|
| +// 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;
|
| +
|
| +abstract class Converter<S, T> {
|
| + // Synchronous conversion.
|
| + T convert(S input) {
|
| + T result;
|
| + ChunkedConversionSink outSink =
|
| + new ChunkedConversionSink.withCallback((x) { result = x; });
|
| + startChunkedConversion(outSink).addNonChunked(input);
|
| + return result;
|
| + }
|
| +
|
| +
|
| + // Can be implemented as other.fuseInput(this).
|
| + // The receiver can try to recognize [other] and optimize for it. If it
|
| + // doesn't recognize [other] it should always invoke `other.fuseInput(this)`.
|
| + Converter<S, dynamic> fuse(Converter<T, dynamic> other) {
|
| + return other.fuseInput(this);
|
| + }
|
| + // Double-dispatch method for [fuse]. Allows the successor to optimize
|
| + // in case it recognizes the source.
|
| + Converter<dynamic, T> fuseInput(Converter<dynamic, S> other) {
|
| + return new FusedConverter(other, this);
|
| + }
|
| +
|
| + ChunkedConversionSink startChunkedConversion(ChunkedConversionSink sink) {
|
| + return new _NonChunkedSink<S, T>(this, sink);
|
| + }
|
| +
|
| + ChunkedConversionInterface get inputInterface =>
|
| + ChunkedConversionSink.INTERFACE;
|
| + ChunkedConversionInterface get outputInterface =>
|
| + ChunkedConversionSink.INTERFACE;
|
| +}
|
| +
|
| +abstract class ChunkedConversionInterface {
|
| + const ChunkedConversionInterface();
|
| +
|
| + ChunkedConversionSink adapt(ChunkedConversionSink sink);
|
| + ChunkedConversionSink adaptEventSink(EventSink sink);
|
| +}
|
| +
|
| +/** A chunked producer needs an interface to feed *one* value in. */
|
| +abstract class ChunkedConversionSink<S> {
|
| + static const ChunkedConversionInterface INTERFACE =
|
| + const _NonChunkedInterface();
|
| +
|
| + ChunkedConversionSink();
|
| + factory ChunkedConversionSink.withCallback(void f(S input)) = _CallbackSink;
|
| + void addNonChunked(S input);
|
| +
|
| + ChunkedConversionInterface get interface => INTERFACE;
|
| +
|
| + // The type is dynamic on purpose since sinks can chose to have a different
|
| + // chunk-input then the non-chunked input.
|
| + void add(o) { throw new UnsupportedError("add"); }
|
| + void close() { throw new UnsupportedError("close"); }
|
| +}
|
| +
|
| +class _CallbackSink<S> extends ChunkedConversionSink<S> {
|
| + final Function _callback;
|
| + _CallbackSink(this._callback);
|
| + void addNonChunked(S input) { _callback(input); }
|
| +}
|
| +
|
| +class _NonChunkedInterface extends ChunkedConversionInterface {
|
| + const _NonChunkedInterface();
|
| +
|
| + ChunkedConversionSink adapt(ChunkedConversionSink sink) {
|
| + // Every sink is suitable as a non-chunked sink.
|
| + return sink;
|
| + }
|
| +
|
| + ChunkedConversionSink adaptEventSink(EventSink eventSink) {
|
| + throw new UnsupportedError("close");
|
| + }
|
| +}
|
| +
|
| +class _NonChunkedSink<S, T> extends ChunkedConversionSink<S> {
|
| + final Converter<S, T> _converter;
|
| + final ChunkedConversionSink<T> _output;
|
| +
|
| + _NonChunkedSink(this._converter, this._output);
|
| +
|
| + void addNonChunked(S input) {
|
| + _output.addNonChunked(_converter.convert(input));
|
| + }
|
| +}
|
| +
|
| +class FusedConverter<S, T> extends Converter<S, T> {
|
| + final Converter first;
|
| + final Converter second;
|
| + FusedConverter(this.first, this.second);
|
| +
|
| + T convert(S input) => second.convert(first.convert(input));
|
| +
|
| + fuse(Converter other) => other.fuseInput(this);
|
| + fuseInput(Converter other) => new FusedConverter(other, this);
|
| +
|
| + ChunkedConversionSink startChunkedConversion(ChunkedConversionSink sink) {
|
| + return first.startChunkedConversion(second.startChunkedConversion(sink));
|
| + }
|
| +
|
| + ChunkedConversionInterface get inputInterface => first.inputInterface;
|
| + ChunkedConversionInterface get outputInterface => second.outputInterface;
|
| +}
|
| +
|
| +class ConverterTransformer<S, T> implements StreamTransformer<S, T> {
|
| + // We can't put in the generic type of the converter since the synchronous
|
| + // conversion type is not necessarily the same as the chunked/streamed one.
|
| + final Converter _converter;
|
| + ConverterTransformer(Converter converter) : this._converter = converter;
|
| +
|
| + Stream<T> bind(Stream<S> source) {
|
| + return new _ConverterTransformStream<S, T>(source, _converter);
|
| + }
|
| +}
|
| +
|
| +class _ConverterTransformStream<S, T> extends EventTransformStream<S, T> {
|
| + final _ConverterStreamEventTransformer<S, T> _eventTransformer;
|
| +
|
| + _ConverterTransformStream(Stream<S> source, Converter converter)
|
| + : this._withEventTransformer(
|
| + source,
|
| + new _ConverterStreamEventTransformer<S, T>(converter));
|
| +
|
| + _ConverterTransformStream._withEventTransformer(
|
| + Stream<S> source,
|
| + _ConverterStreamEventTransformer<S, T> eventTransformer)
|
| + : _eventTransformer = eventTransformer,
|
| + super(source, eventTransformer);
|
| +
|
| + StreamSubscription<T> listen(void onData(T data),
|
| + { void onError(error),
|
| + void onDone(),
|
| + bool cancelOnError }) {
|
| + _eventTransformer._start();
|
| + return super.listen(onData, onError: onError, onDone: onDone,
|
| + cancelOnError: cancelOnError);
|
| + }
|
| +}
|
| +
|
| +class _ConverterStreamEventTransformer<S, T>
|
| + implements EventSink<T>, StreamEventTransformer<S, T> {
|
| + final Converter _converter;
|
| + EventSink _eventSink;
|
| + ChunkedConversionSink _chunkedSink;
|
| +
|
| + _ConverterStreamEventTransformer(this._converter);
|
| +
|
| + void _start() {
|
| + ChunkedConversionInterface outputInterface = _converter.outputInterface;
|
| + ChunkedConversionSink chunkedOutputSink =
|
| + outputInterface.adaptEventSink(this);
|
| + _chunkedSink = _converter.startChunkedConversion(chunkedOutputSink);
|
| + }
|
| +
|
| + Stream bind(Stream otherStream) {
|
| + throw new UnsupportedError("Converter streams must not call bind");
|
| + }
|
| + void add(T o) => _eventSink.add(o);
|
| + void close() => _eventSink.close();
|
| + void addError(var error) {
|
| + throw new UnsupportedError("converters should not use `addError`.");
|
| + }
|
| +
|
| + void handleData(S event, EventSink<T> eventSink) {
|
| + _eventSink = eventSink;
|
| + try {
|
| + _chunkedSink.add(event);
|
| + } catch(e) {
|
| + eventSink.addError(e);
|
| + }
|
| + _eventSink = null;
|
| + }
|
| +
|
| + void handleDone(EventSink<T> eventSink) {
|
| + _eventSink = eventSink;
|
| + try {
|
| + _chunkedSink.close();
|
| + } catch(e) {
|
| + eventSink.addError(e);
|
| + }
|
| + _eventSink = null;
|
| + }
|
| +
|
| + void handleError(var errorEvent, EventSink<T> eventSink) {
|
| + eventSink.addError(errorEvent);
|
| + }
|
| +}
|
|
|