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 |