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