Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 * This class represents the [StringConversionSink] interface. It is | |
| 9 * accessed through [StringConversionSink.INTERFACE] or as instance field | |
| 10 * `interface` from [StringConversionSink]s. | |
| 11 */ | |
| 12 class _StringInterface extends ChunkedConversionInterface { | |
| 13 const _StringInterface(); | |
| 14 | |
| 15 StringConversionSink adapt(ChunkedConversionSink sink) { | |
| 16 if (sink.interface == StringConversionSink.INTERFACE) return sink; | |
| 17 return new _StringAdapterSink(sink); | |
| 18 } | |
| 19 | |
| 20 /** | |
| 21 * Creates a [StringConversionSink] with a callback. | |
| 22 * | |
| 23 * The [callback] is invoked with the accumulated data when the sink is | |
| 24 * closed. | |
| 25 */ | |
| 26 StringConversionSink createSink(void callback(String accumulated)) { | |
| 27 return new _StringCallbackSink(callback); | |
| 28 } | |
| 29 } | |
| 30 | |
| 31 /** | |
| 32 * This class provides an interface for converters to | |
| 33 * efficiently transmit String data. | |
| 34 * | |
| 35 * Instead of limiting the interface to one non-chunked String it accepts | |
| 36 * partial strings and UTF-8 code units. | |
|
Søren Gjesse
2013/07/25 08:07:00
The comment here is a bit misleading. It says "acc
floitsch
2013/07/25 12:57:24
Rewritten.
| |
| 37 */ | |
| 38 abstract class StringConversionSink | |
| 39 extends ChunkedConversionSink<String> { | |
| 40 | |
| 41 static const ChunkedConversionInterface INTERFACE = const _StringInterface(); | |
| 42 | |
| 43 StringConversionSink(); | |
| 44 | |
| 45 /** | |
| 46 * Creates a new instance wrapping the given [sink]. | |
| 47 * | |
| 48 * Every string that is added to the returned instance is forwarded to | |
| 49 * the [sink]. The instance is allowed to buffer and is not required to | |
| 50 * forward immediately. | |
| 51 */ | |
| 52 factory StringConversionSink.fromStringSink(StringSink sink) = | |
| 53 _StringSinkConversionSink; | |
| 54 | |
| 55 /** | |
| 56 * Adds the next [chunk] to `this`. | |
| 57 * | |
| 58 * Adds the substring defined by [start] and [end]-exclusive to `this`. | |
| 59 * | |
| 60 * If [isLast] is `true` closes `this`. | |
| 61 */ | |
| 62 void addSlice(String chunk, int start, int end, bool isLast); | |
| 63 | |
| 64 /** | |
| 65 * Returns `this` as a sink that accepts UTF-8 input. | |
| 66 * | |
| 67 * This method must be the first and only call to `this`. It invalidates | |
|
Søren Gjesse
2013/07/25 08:07:00
Prefix "This meth..." with "If used".
floitsch
2013/07/25 12:57:24
Done.
| |
| 68 * `this`. All further operations must be performed on the result. | |
| 69 */ | |
| 70 ByteConversionSink asUtf8Sink(bool allowMalformed); | |
| 71 // - asRuneSink | |
| 72 // - asCodeUnitsSink | |
| 73 | |
| 74 /** | |
| 75 * Returns `this` as a [ClosableStringSink]. | |
| 76 * | |
| 77 * This method must be the first and only call to `this`. It invalidates | |
|
Søren Gjesse
2013/07/25 08:07:00
Ditto.
floitsch
2013/07/25 12:57:24
Done.
| |
| 78 * `this`. All further operations must be performed on the result. | |
| 79 */ | |
| 80 ClosableStringSink asStringSink(); | |
| 81 | |
| 82 ChunkedConversionInterface get interface => INTERFACE; | |
| 83 } | |
| 84 | |
| 85 /** | |
| 86 * A [ClosableStringSink] extends the [StringSink] interface by adding a | |
| 87 * `close` method. | |
| 88 */ | |
| 89 abstract class ClosableStringSink extends StringSink { | |
| 90 /** | |
| 91 * Creates a new instance combining a [StringSink] [sink] and a callback | |
| 92 * [onClose] which is invoked when the returned instance is closed. | |
| 93 */ | |
| 94 factory ClosableStringSink.fromStringSink(StringSink sink, void onClose()) | |
| 95 = _ClosableStringSink; | |
| 96 | |
| 97 /** | |
| 98 * Closes `this` and flushes any outstanding data. | |
| 99 */ | |
| 100 void close(); | |
| 101 } | |
| 102 | |
| 103 typedef void _StringSinkCloseCallback(); | |
| 104 | |
| 105 /** | |
| 106 * This class wraps an existing [StringSink] and invokes a | |
| 107 * closure when [close] is invoked. | |
| 108 */ | |
| 109 class _ClosableStringSink implements ClosableStringSink { | |
| 110 final _StringSinkCloseCallback _callback; | |
| 111 final StringSink _sink; | |
| 112 | |
| 113 _ClosableStringSink(this._sink, this._callback); | |
| 114 | |
| 115 void close() => _callback(); | |
| 116 | |
| 117 void writeCharCode(int charCode) => _sink.writeCharCode(charCode); | |
| 118 void write(Object o) => _sink.write(o); | |
| 119 void writeln([Object o]) => _sink.writeln(o); | |
| 120 void writeAll(Iterable objects, [String separator]) | |
| 121 => _sink.writeAll(objects, separator); | |
| 122 } | |
| 123 | |
| 124 /** | |
| 125 * This class wraps an existing [StringConversionSink] and exposes a | |
| 126 * [ClosableStringSink] interface. The wrapped sink only needs to implement | |
| 127 * `add` and `close`. | |
| 128 */ | |
| 129 // TODO(floitsch): make this class public? | |
| 130 class _StringConversionSinkAsStringSinkAdapter implements ClosableStringSink { | |
| 131 static const _MIN_STRING_SIZE = 16; | |
| 132 | |
| 133 StringBuffer _buffer; | |
| 134 StringConversionSink _chunkedSink; | |
| 135 | |
| 136 _StringConversionSinkAsStringSinkAdapter(this._chunkedSink) | |
| 137 : _buffer = new StringBuffer(); | |
| 138 | |
| 139 void close() { | |
| 140 if (_buffer.isNotEmpty) _flush(); | |
| 141 _chunkedSink.close(); | |
| 142 } | |
| 143 | |
| 144 void writeCharCode(int charCode) { | |
| 145 _buffer.writeCharCode(charCode); | |
| 146 if (_buffer.length > _MIN_STRING_SIZE) _flush(); | |
| 147 } | |
| 148 | |
| 149 void write(Object o) { | |
| 150 if (_buffer.isNotEmpty) _flush(); | |
| 151 String str = o.toString(); | |
| 152 _chunkedSink.add(o.toString()); | |
| 153 } | |
| 154 | |
| 155 void writeln([Object o]) { | |
| 156 _buffer.writeln(o); | |
| 157 if (_buffer.length > _MIN_STRING_SIZE) _flush(); | |
| 158 } | |
| 159 | |
| 160 void writeAll(Iterable objects, [String separator]) { | |
| 161 if (_buffer.isNotEmpty) _flush(); | |
| 162 Iterator iterator = objects.iterator; | |
| 163 if (!iterator.moveNext()) return; | |
| 164 if (separator.isEmpty) { | |
| 165 do { | |
| 166 _chunkedSink.add(iterator.current.toString()); | |
| 167 } while (iterator.moveNext()); | |
| 168 } else { | |
| 169 _chunkedSink.add(iterator.current.toString()); | |
| 170 while (iterator.moveNext()) { | |
| 171 write(separator); | |
| 172 _chunkedSink.add(iterator.current.toString()); | |
| 173 } | |
| 174 } | |
| 175 } | |
| 176 | |
| 177 void _flush() { | |
| 178 String accumulated = _buffer.toString(); | |
| 179 _buffer.clear(); | |
| 180 _chunkedSink.add(accumulated); | |
| 181 } | |
| 182 } | |
| 183 | |
| 184 /** | |
| 185 * This class provides a base-class for converters that need to accept String | |
| 186 * inputs. | |
| 187 */ | |
| 188 abstract class StringConversionSinkBase extends StringConversionSinkMixin { | |
| 189 } | |
| 190 | |
| 191 /** | |
| 192 * This class provides a mixin for converters that need to accept String | |
| 193 * inputs. | |
| 194 */ | |
| 195 abstract class StringConversionSinkMixin implements StringConversionSink { | |
| 196 | |
| 197 void addSlice(String str, int start, int end, bool isLast); | |
| 198 void close(); | |
| 199 | |
| 200 void add(String str) => addSlice(str, 0, str.length, false); | |
| 201 | |
| 202 ByteConversionSink asUtf8Sink(bool allowMalformed) { | |
| 203 return new _Utf8ConversionSink(this, allowMalformed); | |
| 204 } | |
| 205 | |
| 206 ChunkedConversionInterface get interface => StringConversionSink.INTERFACE; | |
| 207 | |
| 208 ClosableStringSink asStringSink() { | |
| 209 return new _StringConversionSinkAsStringSinkAdapter(this); | |
| 210 } | |
| 211 | |
| 212 ChunkedConversionSink adaptTo(ChunkedConversionInterface interface) { | |
| 213 if (this.interface == interface) return this; | |
| 214 return interface.adapt(this); | |
| 215 } | |
| 216 } | |
| 217 | |
| 218 /** | |
| 219 * This class is a [StringConversionSink] that wraps a [StringSink]. | |
| 220 */ | |
| 221 class _StringSinkConversionSink extends StringConversionSinkBase { | |
| 222 StringSink _stringSink; | |
| 223 _StringSinkConversionSink(StringSink this._stringSink); | |
| 224 | |
| 225 void close() {} | |
| 226 void addSlice(String str, int start, int end, bool isLast) { | |
| 227 if (start != 0 || end != str.length) { | |
| 228 for (int i = start; i < end; i++) { | |
| 229 _stringSink.writeCharCode(str.codeUnitAt(i)); | |
| 230 } | |
| 231 } else { | |
| 232 _stringSink.write(str); | |
| 233 } | |
| 234 if (isLast) close(); | |
| 235 } | |
| 236 | |
| 237 void add(String str) => _stringSink.write(str); | |
| 238 | |
| 239 ByteConversionSink asUtf8Sink(bool allowMalformed) { | |
| 240 return new _Utf8StringSinkAdapter(this, _stringSink, allowMalformed); | |
| 241 } | |
| 242 | |
| 243 ClosableStringSink asStringSink() { | |
| 244 return new ClosableStringSink.fromStringSink(_stringSink, this.close); | |
| 245 } | |
| 246 } | |
| 247 | |
| 248 /** | |
| 249 * This class accumulates all chunks into one string | |
| 250 * and invokes a callback when the sink is closed. | |
| 251 * | |
| 252 * This class can be used to terminate a chunked conversion. | |
| 253 */ | |
| 254 class _StringCallbackSink extends _StringSinkConversionSink { | |
| 255 final _ChunkedConversionCallback<String> _callback; | |
| 256 _StringCallbackSink(this._callback) : super(new StringBuffer()); | |
| 257 | |
| 258 void close() { | |
| 259 StringBuffer buffer = _stringSink; | |
| 260 String accumulated = buffer.toString(); | |
| 261 buffer.clear(); | |
| 262 _callback(accumulated); | |
| 263 } | |
| 264 | |
| 265 ByteConversionSink asUtf8Sink(bool allowMalformed) { | |
| 266 return new _Utf8StringSinkAdapter( | |
| 267 this, _stringSink, allowMalformed); | |
| 268 } | |
| 269 } | |
| 270 | |
| 271 /** | |
| 272 * This class adapts a simple [ChunkedConversionSink] to a | |
| 273 * [StringConversionSink]. | |
| 274 * | |
| 275 * All additional methods of the [StringConversionSink] (compared to the | |
| 276 * ChunkedConversionSink) are redirected to the `add` method. | |
| 277 */ | |
| 278 class _StringAdapterSink extends StringConversionSinkBase { | |
| 279 final ChunkedConversionSink<String> _sink; | |
| 280 | |
| 281 _StringAdapterSink(this._sink); | |
| 282 | |
| 283 void add(String str) => _sink.add(str); | |
| 284 | |
| 285 void addSlice(String str, int start, int end, bool isLast) { | |
| 286 if (start == 0 && end == str.length) { | |
| 287 add(str); | |
| 288 } else { | |
| 289 add(str.substring(start, end)); | |
| 290 } | |
| 291 if (isLast) close(); | |
| 292 } | |
| 293 | |
| 294 void close() => _sink.close(); | |
| 295 } | |
| 296 | |
| 297 | |
| 298 /** | |
| 299 * Decodes UTF-8 code units and stores them in a [StringSink]. | |
| 300 */ | |
| 301 class _Utf8StringSinkAdapter extends ByteConversionSink { | |
| 302 final _Utf8Decoder _decoder; | |
| 303 final ChunkedConversionSink _chunkedSink; | |
| 304 | |
| 305 _Utf8StringSinkAdapter(ChunkedConversionSink chunkedSink, | |
| 306 StringSink sink, bool allowMalformed) | |
| 307 : _chunkedSink = chunkedSink, | |
| 308 _decoder = new _Utf8Decoder(sink, allowMalformed); | |
| 309 | |
| 310 void close() { | |
| 311 _decoder.close(); | |
| 312 if(_chunkedSink != null) _chunkedSink.close(); | |
| 313 } | |
| 314 | |
| 315 void add(List<int> chunk) { | |
| 316 addSlice(chunk, 0, chunk.length, false); | |
| 317 } | |
| 318 | |
| 319 void addSlice(List<int> codeUnits, int startIndex, int endIndex, | |
| 320 bool isLast) { | |
| 321 _decoder.convert(codeUnits, startIndex, endIndex); | |
| 322 if (isLast) close(); | |
| 323 } | |
| 324 } | |
| 325 | |
| 326 /** | |
| 327 * Decodes UTF-8 code units. | |
| 328 * | |
| 329 * Forwards the decoded strings to the given [StringConversionSink]. | |
| 330 */ | |
| 331 // TODO(floitsch): make this class public? | |
| 332 class _Utf8ConversionSink extends ByteConversionSink { | |
| 333 static const _MIN_STRING_SIZE = 16; | |
| 334 | |
| 335 final _Utf8Decoder _decoder; | |
| 336 final StringConversionSink _chunkedSink; | |
| 337 final StringBuffer _buffer; | |
| 338 _Utf8ConversionSink(StringConversionSink sink, bool allowMalformed) | |
| 339 : this._(sink, new StringBuffer(), allowMalformed); | |
| 340 | |
| 341 _Utf8ConversionSink._(this._chunkedSink, StringBuffer stringBuffer, | |
| 342 bool allowMalformed) | |
| 343 : _decoder = new _Utf8Decoder(stringBuffer, allowMalformed), | |
| 344 _buffer = stringBuffer; | |
| 345 | |
| 346 void close() { | |
| 347 _decoder.close(); | |
| 348 if (_buffer.isNotEmpty) { | |
| 349 String accumulated = _buffer.toString(); | |
| 350 _buffer.clear(); | |
| 351 _chunkedSink.addSlice(accumulated, 0, accumulated.length, true); | |
| 352 } else { | |
| 353 _chunkedSink.close(); | |
| 354 } | |
| 355 } | |
| 356 | |
| 357 void add(List<int> chunk) { | |
| 358 addSlice(chunk, 0, chunk.length, false); | |
| 359 } | |
| 360 | |
| 361 void addSlice(List<int> chunk, int startIndex, int endIndex, | |
| 362 bool isLast) { | |
| 363 _decoder.convert(chunk, startIndex, endIndex); | |
| 364 if (_buffer.length > _MIN_STRING_SIZE) { | |
| 365 String accumulated = _buffer.toString(); | |
| 366 _chunkedSink.addSlice(accumulated, 0, accumulated.length, isLast); | |
| 367 _buffer.clear(); | |
| 368 return; | |
| 369 } | |
| 370 if (isLast) close(); | |
| 371 } | |
| 372 } | |
| OLD | NEW |