| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of dart.convert; | 5 part of dart.convert; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * This class provides an interface for converters to | 8 * This class provides an interface for converters to |
| 9 * efficiently transmit String data. | 9 * efficiently transmit String data. |
| 10 * | 10 * |
| 11 * Instead of limiting the interface to one non-chunked String it accepts | 11 * Instead of limiting the interface to one non-chunked String it accepts |
| 12 * partial strings or can be transformed into a byte sink that | 12 * partial strings or can be transformed into a byte sink that |
| 13 * accepts UTF-8 code units. | 13 * accepts UTF-8 code units. |
| 14 * | 14 * |
| 15 * This abstract class will likely get more methods over time. Implementers are | 15 * This abstract class will likely get more methods over time. Implementers are |
| 16 * urged to extend [StringConversionSinkBase] or to mix in | 16 * urged to extend [StringConversionSinkBase] or to mix in |
| 17 * [StringConversionSinkMixin], to ensure that their class covers the newly | 17 * [StringConversionSinkMixin], to ensure that their class covers the newly |
| 18 * added methods. | 18 * added methods. |
| 19 */ | 19 */ |
| 20 abstract class StringConversionSink extends ChunkedConversionSink<String> { | 20 abstract class StringConversionSink extends ChunkedConversionSink<String> { |
| 21 StringConversionSink(); | 21 StringConversionSink(); |
| 22 factory StringConversionSink.withCallback(void callback(String accumulated)) | 22 factory StringConversionSink.withCallback(void callback(String accumulated)) = |
| 23 = _StringCallbackSink; | 23 _StringCallbackSink; |
| 24 factory StringConversionSink.from(Sink<String> sink) | 24 factory StringConversionSink.from(Sink<String> sink) = _StringAdapterSink; |
| 25 = _StringAdapterSink; | |
| 26 | 25 |
| 27 /** | 26 /** |
| 28 * Creates a new instance wrapping the given [sink]. | 27 * Creates a new instance wrapping the given [sink]. |
| 29 * | 28 * |
| 30 * Every string that is added to the returned instance is forwarded to | 29 * Every string that is added to the returned instance is forwarded to |
| 31 * the [sink]. The instance is allowed to buffer and is not required to | 30 * the [sink]. The instance is allowed to buffer and is not required to |
| 32 * forward immediately. | 31 * forward immediately. |
| 33 */ | 32 */ |
| 34 factory StringConversionSink.fromStringSink(StringSink sink) = | 33 factory StringConversionSink.fromStringSink(StringSink sink) = |
| 35 _StringSinkConversionSink; | 34 _StringSinkConversionSink; |
| (...skipping 28 matching lines...) Expand all Loading... |
| 64 | 63 |
| 65 /** | 64 /** |
| 66 * A [ClosableStringSink] extends the [StringSink] interface by adding a | 65 * A [ClosableStringSink] extends the [StringSink] interface by adding a |
| 67 * `close` method. | 66 * `close` method. |
| 68 */ | 67 */ |
| 69 abstract class ClosableStringSink extends StringSink { | 68 abstract class ClosableStringSink extends StringSink { |
| 70 /** | 69 /** |
| 71 * Creates a new instance combining a [StringSink] [sink] and a callback | 70 * Creates a new instance combining a [StringSink] [sink] and a callback |
| 72 * [onClose] which is invoked when the returned instance is closed. | 71 * [onClose] which is invoked when the returned instance is closed. |
| 73 */ | 72 */ |
| 74 factory ClosableStringSink.fromStringSink(StringSink sink, void onClose()) | 73 factory ClosableStringSink.fromStringSink(StringSink sink, void onClose()) = |
| 75 = _ClosableStringSink; | 74 _ClosableStringSink; |
| 76 | 75 |
| 77 /** | 76 /** |
| 78 * Closes `this` and flushes any outstanding data. | 77 * Closes `this` and flushes any outstanding data. |
| 79 */ | 78 */ |
| 80 void close(); | 79 void close(); |
| 81 } | 80 } |
| 82 | 81 |
| 83 typedef void _StringSinkCloseCallback(); | 82 typedef void _StringSinkCloseCallback(); |
| 84 | 83 |
| 85 /** | 84 /** |
| 86 * This class wraps an existing [StringSink] and invokes a | 85 * This class wraps an existing [StringSink] and invokes a |
| 87 * closure when [close] is invoked. | 86 * closure when [close] is invoked. |
| 88 */ | 87 */ |
| 89 class _ClosableStringSink implements ClosableStringSink { | 88 class _ClosableStringSink implements ClosableStringSink { |
| 90 final _StringSinkCloseCallback _callback; | 89 final _StringSinkCloseCallback _callback; |
| 91 final StringSink _sink; | 90 final StringSink _sink; |
| 92 | 91 |
| 93 _ClosableStringSink(this._sink, this._callback); | 92 _ClosableStringSink(this._sink, this._callback); |
| 94 | 93 |
| 95 void close() { _callback(); } | 94 void close() { |
| 95 _callback(); |
| 96 } |
| 96 | 97 |
| 97 void writeCharCode(int charCode) { _sink.writeCharCode(charCode); } | 98 void writeCharCode(int charCode) { |
| 98 void write(Object o) { _sink.write(o); } | 99 _sink.writeCharCode(charCode); |
| 99 void writeln([Object o = ""]) { _sink.writeln(o); } | 100 } |
| 101 |
| 102 void write(Object o) { |
| 103 _sink.write(o); |
| 104 } |
| 105 |
| 106 void writeln([Object o = ""]) { |
| 107 _sink.writeln(o); |
| 108 } |
| 109 |
| 100 void writeAll(Iterable objects, [String separator = ""]) { | 110 void writeAll(Iterable objects, [String separator = ""]) { |
| 101 _sink.writeAll(objects, separator); | 111 _sink.writeAll(objects, separator); |
| 102 } | 112 } |
| 103 } | 113 } |
| 104 | 114 |
| 105 /** | 115 /** |
| 106 * This class wraps an existing [StringConversionSink] and exposes a | 116 * This class wraps an existing [StringConversionSink] and exposes a |
| 107 * [ClosableStringSink] interface. The wrapped sink only needs to implement | 117 * [ClosableStringSink] interface. The wrapped sink only needs to implement |
| 108 * `add` and `close`. | 118 * `add` and `close`. |
| 109 */ | 119 */ |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 158 String accumulated = _buffer.toString(); | 168 String accumulated = _buffer.toString(); |
| 159 _buffer.clear(); | 169 _buffer.clear(); |
| 160 _chunkedSink.add(accumulated); | 170 _chunkedSink.add(accumulated); |
| 161 } | 171 } |
| 162 } | 172 } |
| 163 | 173 |
| 164 /** | 174 /** |
| 165 * This class provides a base-class for converters that need to accept String | 175 * This class provides a base-class for converters that need to accept String |
| 166 * inputs. | 176 * inputs. |
| 167 */ | 177 */ |
| 168 abstract class StringConversionSinkBase extends StringConversionSinkMixin { | 178 abstract class StringConversionSinkBase extends StringConversionSinkMixin {} |
| 169 } | |
| 170 | 179 |
| 171 /** | 180 /** |
| 172 * This class provides a mixin for converters that need to accept String | 181 * This class provides a mixin for converters that need to accept String |
| 173 * inputs. | 182 * inputs. |
| 174 */ | 183 */ |
| 175 abstract class StringConversionSinkMixin implements StringConversionSink { | 184 abstract class StringConversionSinkMixin implements StringConversionSink { |
| 176 | |
| 177 void addSlice(String str, int start, int end, bool isLast); | 185 void addSlice(String str, int start, int end, bool isLast); |
| 178 void close(); | 186 void close(); |
| 179 | 187 |
| 180 void add(String str) { addSlice(str, 0, str.length, false); } | 188 void add(String str) { |
| 189 addSlice(str, 0, str.length, false); |
| 190 } |
| 181 | 191 |
| 182 ByteConversionSink asUtf8Sink(bool allowMalformed) { | 192 ByteConversionSink asUtf8Sink(bool allowMalformed) { |
| 183 return new _Utf8ConversionSink(this, allowMalformed); | 193 return new _Utf8ConversionSink(this, allowMalformed); |
| 184 } | 194 } |
| 185 | 195 |
| 186 ClosableStringSink asStringSink() { | 196 ClosableStringSink asStringSink() { |
| 187 return new _StringConversionSinkAsStringSinkAdapter(this); | 197 return new _StringConversionSinkAsStringSinkAdapter(this); |
| 188 } | 198 } |
| 189 } | 199 } |
| 190 | 200 |
| 191 /** | 201 /** |
| 192 * This class is a [StringConversionSink] that wraps a [StringSink]. | 202 * This class is a [StringConversionSink] that wraps a [StringSink]. |
| 193 */ | 203 */ |
| 194 class _StringSinkConversionSink extends StringConversionSinkBase { | 204 class _StringSinkConversionSink extends StringConversionSinkBase { |
| 195 StringSink _stringSink; | 205 StringSink _stringSink; |
| 196 _StringSinkConversionSink(this._stringSink); | 206 _StringSinkConversionSink(this._stringSink); |
| 197 | 207 |
| 198 void close() {} | 208 void close() {} |
| 199 void addSlice(String str, int start, int end, bool isLast) { | 209 void addSlice(String str, int start, int end, bool isLast) { |
| 200 if (start != 0 || end != str.length) { | 210 if (start != 0 || end != str.length) { |
| 201 for (int i = start; i < end; i++) { | 211 for (int i = start; i < end; i++) { |
| 202 _stringSink.writeCharCode(str.codeUnitAt(i)); | 212 _stringSink.writeCharCode(str.codeUnitAt(i)); |
| 203 } | 213 } |
| 204 } else { | 214 } else { |
| 205 _stringSink.write(str); | 215 _stringSink.write(str); |
| 206 } | 216 } |
| 207 if (isLast) close(); | 217 if (isLast) close(); |
| 208 } | 218 } |
| 209 | 219 |
| 210 void add(String str) { _stringSink.write(str); } | 220 void add(String str) { |
| 221 _stringSink.write(str); |
| 222 } |
| 211 | 223 |
| 212 ByteConversionSink asUtf8Sink(bool allowMalformed) { | 224 ByteConversionSink asUtf8Sink(bool allowMalformed) { |
| 213 return new _Utf8StringSinkAdapter(this, _stringSink, allowMalformed); | 225 return new _Utf8StringSinkAdapter(this, _stringSink, allowMalformed); |
| 214 } | 226 } |
| 215 | 227 |
| 216 ClosableStringSink asStringSink() { | 228 ClosableStringSink asStringSink() { |
| 217 return new ClosableStringSink.fromStringSink(_stringSink, this.close); | 229 return new ClosableStringSink.fromStringSink(_stringSink, this.close); |
| 218 } | 230 } |
| 219 } | 231 } |
| 220 | 232 |
| 221 /** | 233 /** |
| 222 * This class accumulates all chunks into one string | 234 * This class accumulates all chunks into one string |
| 223 * and invokes a callback when the sink is closed. | 235 * and invokes a callback when the sink is closed. |
| 224 * | 236 * |
| 225 * This class can be used to terminate a chunked conversion. | 237 * This class can be used to terminate a chunked conversion. |
| 226 */ | 238 */ |
| 227 class _StringCallbackSink extends _StringSinkConversionSink { | 239 class _StringCallbackSink extends _StringSinkConversionSink { |
| 228 final _ChunkedConversionCallback<String> _callback; | 240 final _ChunkedConversionCallback<String> _callback; |
| 229 _StringCallbackSink(this._callback) : super(new StringBuffer()); | 241 _StringCallbackSink(this._callback) : super(new StringBuffer()); |
| 230 | 242 |
| 231 void close() { | 243 void close() { |
| 232 StringBuffer buffer = _stringSink; | 244 StringBuffer buffer = _stringSink; |
| 233 String accumulated = buffer.toString(); | 245 String accumulated = buffer.toString(); |
| 234 buffer.clear(); | 246 buffer.clear(); |
| 235 _callback(accumulated); | 247 _callback(accumulated); |
| 236 } | 248 } |
| 237 | 249 |
| 238 ByteConversionSink asUtf8Sink(bool allowMalformed) { | 250 ByteConversionSink asUtf8Sink(bool allowMalformed) { |
| 239 return new _Utf8StringSinkAdapter( | 251 return new _Utf8StringSinkAdapter(this, _stringSink, allowMalformed); |
| 240 this, _stringSink, allowMalformed); | |
| 241 } | 252 } |
| 242 } | 253 } |
| 243 | 254 |
| 244 /** | 255 /** |
| 245 * This class adapts a simple [ChunkedConversionSink] to a | 256 * This class adapts a simple [ChunkedConversionSink] to a |
| 246 * [StringConversionSink]. | 257 * [StringConversionSink]. |
| 247 * | 258 * |
| 248 * All additional methods of the [StringConversionSink] (compared to the | 259 * All additional methods of the [StringConversionSink] (compared to the |
| 249 * ChunkedConversionSink) are redirected to the `add` method. | 260 * ChunkedConversionSink) are redirected to the `add` method. |
| 250 */ | 261 */ |
| 251 class _StringAdapterSink extends StringConversionSinkBase { | 262 class _StringAdapterSink extends StringConversionSinkBase { |
| 252 final Sink<String> _sink; | 263 final Sink<String> _sink; |
| 253 | 264 |
| 254 _StringAdapterSink(this._sink); | 265 _StringAdapterSink(this._sink); |
| 255 | 266 |
| 256 void add(String str) { _sink.add(str); } | 267 void add(String str) { |
| 268 _sink.add(str); |
| 269 } |
| 257 | 270 |
| 258 void addSlice(String str, int start, int end, bool isLast) { | 271 void addSlice(String str, int start, int end, bool isLast) { |
| 259 if (start == 0 && end == str.length) { | 272 if (start == 0 && end == str.length) { |
| 260 add(str); | 273 add(str); |
| 261 } else { | 274 } else { |
| 262 add(str.substring(start, end)); | 275 add(str.substring(start, end)); |
| 263 } | 276 } |
| 264 if (isLast) close(); | 277 if (isLast) close(); |
| 265 } | 278 } |
| 266 | 279 |
| 267 void close() { _sink.close(); } | 280 void close() { |
| 281 _sink.close(); |
| 282 } |
| 268 } | 283 } |
| 269 | 284 |
| 270 | |
| 271 /** | 285 /** |
| 272 * Decodes UTF-8 code units and stores them in a [StringSink]. | 286 * Decodes UTF-8 code units and stores them in a [StringSink]. |
| 273 */ | 287 */ |
| 274 class _Utf8StringSinkAdapter extends ByteConversionSink { | 288 class _Utf8StringSinkAdapter extends ByteConversionSink { |
| 275 final _Utf8Decoder _decoder; | 289 final _Utf8Decoder _decoder; |
| 276 final Sink _sink; | 290 final Sink _sink; |
| 277 | 291 |
| 278 _Utf8StringSinkAdapter(this._sink, | 292 _Utf8StringSinkAdapter(this._sink, StringSink stringSink, bool allowMalformed) |
| 279 StringSink stringSink, bool allowMalformed) | |
| 280 : _decoder = new _Utf8Decoder(stringSink, allowMalformed); | 293 : _decoder = new _Utf8Decoder(stringSink, allowMalformed); |
| 281 | 294 |
| 282 void close() { | 295 void close() { |
| 283 _decoder.close(); | 296 _decoder.close(); |
| 284 if(_sink != null) _sink.close(); | 297 if (_sink != null) _sink.close(); |
| 285 } | 298 } |
| 286 | 299 |
| 287 void add(List<int> chunk) { | 300 void add(List<int> chunk) { |
| 288 addSlice(chunk, 0, chunk.length, false); | 301 addSlice(chunk, 0, chunk.length, false); |
| 289 } | 302 } |
| 290 | 303 |
| 291 void addSlice(List<int> codeUnits, int startIndex, int endIndex, | 304 void addSlice( |
| 292 bool isLast) { | 305 List<int> codeUnits, int startIndex, int endIndex, bool isLast) { |
| 293 _decoder.convert(codeUnits, startIndex, endIndex); | 306 _decoder.convert(codeUnits, startIndex, endIndex); |
| 294 if (isLast) close(); | 307 if (isLast) close(); |
| 295 } | 308 } |
| 296 } | 309 } |
| 297 | 310 |
| 298 /** | 311 /** |
| 299 * Decodes UTF-8 code units. | 312 * Decodes UTF-8 code units. |
| 300 * | 313 * |
| 301 * Forwards the decoded strings to the given [StringConversionSink]. | 314 * Forwards the decoded strings to the given [StringConversionSink]. |
| 302 */ | 315 */ |
| 303 // TODO(floitsch): make this class public? | 316 // TODO(floitsch): make this class public? |
| 304 class _Utf8ConversionSink extends ByteConversionSink { | 317 class _Utf8ConversionSink extends ByteConversionSink { |
| 305 | |
| 306 final _Utf8Decoder _decoder; | 318 final _Utf8Decoder _decoder; |
| 307 final StringConversionSink _chunkedSink; | 319 final StringConversionSink _chunkedSink; |
| 308 final StringBuffer _buffer; | 320 final StringBuffer _buffer; |
| 309 _Utf8ConversionSink(StringConversionSink sink, bool allowMalformed) | 321 _Utf8ConversionSink(StringConversionSink sink, bool allowMalformed) |
| 310 : this._(sink, new StringBuffer(), allowMalformed); | 322 : this._(sink, new StringBuffer(), allowMalformed); |
| 311 | 323 |
| 312 _Utf8ConversionSink._(this._chunkedSink, StringBuffer stringBuffer, | 324 _Utf8ConversionSink._( |
| 313 bool allowMalformed) | 325 this._chunkedSink, StringBuffer stringBuffer, bool allowMalformed) |
| 314 : _decoder = new _Utf8Decoder(stringBuffer, allowMalformed), | 326 : _decoder = new _Utf8Decoder(stringBuffer, allowMalformed), |
| 315 _buffer = stringBuffer; | 327 _buffer = stringBuffer; |
| 316 | 328 |
| 317 void close() { | 329 void close() { |
| 318 _decoder.close(); | 330 _decoder.close(); |
| 319 if (_buffer.isNotEmpty) { | 331 if (_buffer.isNotEmpty) { |
| 320 String accumulated = _buffer.toString(); | 332 String accumulated = _buffer.toString(); |
| 321 _buffer.clear(); | 333 _buffer.clear(); |
| 322 _chunkedSink.addSlice(accumulated, 0, accumulated.length, true); | 334 _chunkedSink.addSlice(accumulated, 0, accumulated.length, true); |
| 323 } else { | 335 } else { |
| 324 _chunkedSink.close(); | 336 _chunkedSink.close(); |
| 325 } | 337 } |
| 326 } | 338 } |
| 327 | 339 |
| 328 void add(List<int> chunk) { | 340 void add(List<int> chunk) { |
| 329 addSlice(chunk, 0, chunk.length, false); | 341 addSlice(chunk, 0, chunk.length, false); |
| 330 } | 342 } |
| 331 | 343 |
| 332 void addSlice(List<int> chunk, int startIndex, int endIndex, bool isLast) { | 344 void addSlice(List<int> chunk, int startIndex, int endIndex, bool isLast) { |
| 333 _decoder.convert(chunk, startIndex, endIndex); | 345 _decoder.convert(chunk, startIndex, endIndex); |
| 334 if (_buffer.isNotEmpty) { | 346 if (_buffer.isNotEmpty) { |
| 335 String accumulated = _buffer.toString(); | 347 String accumulated = _buffer.toString(); |
| 336 _chunkedSink.addSlice(accumulated, 0, accumulated.length, isLast); | 348 _chunkedSink.addSlice(accumulated, 0, accumulated.length, isLast); |
| 337 _buffer.clear(); | 349 _buffer.clear(); |
| 338 return; | 350 return; |
| 339 } | 351 } |
| 340 if (isLast) close(); | 352 if (isLast) close(); |
| 341 } | 353 } |
| 342 } | 354 } |
| OLD | NEW |