Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(226)

Side by Side Diff: sdk/lib/io/string_transformer.dart

Issue 12328037: Add missing files from previous commit (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « sdk/lib/io/io_stream_consumer.dart ('k') | sdk/lib/utf/utf_stream.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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.io;
6
7 /**
8 * Stream transformer that can decode a stream of bytes into a stream of
9 * strings using [encoding].
10 *
11 * Invalid or forbidden byte-sequences will not produce errors, but will instead
12 * insert [replacementChar] in the decoded strings.
13 */
14 class StringDecoder implements StreamTransformer<List<int>, String> {
15 var _decoder;
16
17 /**
18 * Create a new [StringDecoder] with an optional [encoding] and
19 * [replacementChar].
20 */
21 StringDecoder([Encoding encoding = Encoding.UTF_8, int replacementChar]) {
22 switch (encoding) {
23 case Encoding.UTF_8:
24 if (replacementChar == null) {
25 replacementChar = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT;
26 }
27 _decoder = new Utf8DecoderTransformer(replacementChar);
28 break;
29 case Encoding.ASCII:
30 if (replacementChar == null) {
31 replacementChar = '?'.charCodeAt(0);
32 } else if (replacementChar > 127) {
33 throw new ArgumentError("Invalid replacement character for ASCII");
34 }
35 _decoder = new _AsciiDecoder(replacementChar);
36 break;
37 case Encoding.ISO_8859_1:
38 if (replacementChar == null) {
39 replacementChar = '?'.charCodeAt(0);
40 } else if (replacementChar > 255) {
41 throw new ArgumentError(
42 "Invalid replacement character for ISO_8859_1");
43 }
44 _decoder = new _Latin1Decoder(replacementChar);
45 break;
46 case Encoding.SYSTEM:
47 if (Platform.operatingSystem == "windows") {
48 _decoder = new _WindowsCodePageDecoder();
49 } else {
50 if (replacementChar != null) {
51 // TODO(ajohnsen): Handle replacement character.
52 throw new UnsupportedError(
53 "Replacement character is not supported for SYSTEM encoding");
54 }
55 _decoder = new Utf8DecoderTransformer();
56 }
57 break;
58 default:
59 throw new ArgumentError("Unsupported encoding '$encoding'");
60 }
61 }
62
63 Stream<String> bind(Stream<List<int>> stream) => _decoder.bind(stream);
64 }
65
66
67 /**
68 * Stream transformer that can encode a stream of strings info a stream of
69 * bytes using [encoding].
70 *
71 * Strings that cannot be represented in the given encoding will result in an
72 * error and a close event on the stream.
73 */
74 class StringEncoder implements StreamTransformer<String, List<int>> {
75 var _encoder;
76
77 /**
78 * Create a new [StringDecoder] with an optional [encoding] and
79 * [replacementChar].
80 */
81 StringEncoder([Encoding encoding = Encoding.UTF_8]) {
82 switch (encoding) {
83 case Encoding.UTF_8:
84 _encoder = new Utf8EncoderTransformer();
85 break;
86 case Encoding.ASCII:
87 _encoder = new _AsciiEncoder();
88 break;
89 case Encoding.ISO_8859_1:
90 _encoder = new _Latin1Encoder();
91 break;
92 case Encoding.SYSTEM:
93 if (Platform.operatingSystem == "windows") {
94 _encoder = new _WindowsCodePageEncoder();
95 } else {
96 _encoder = new Utf8EncoderTransformer();
97 }
98 break;
99 default:
100 throw new ArgumentError("Unsupported encoding '$encoding'");
101 }
102 }
103
104 Stream<List<int>> bind(Stream<String> stream) => _encoder.bind(stream);
105 }
106
107
108 // Utility function to synchronously decode a list of bytes.
109 String _decodeString(List<int> bytes, [Encoding encoding = Encoding.UTF_8]) {
110 if (bytes.length == 0) return "";
111 var string;
112 var controller = new StreamController();
113 controller.stream
114 .transform(new StringDecoder(encoding))
115 .listen((data) => string = data);
116 controller.add(bytes);
117 controller.close();
118 assert(string != null);
119 return string;
120 }
121
122
123 // Utility function to synchronously encode a String.
124 // Will throw an exception if the encoding is invalid.
125 List<int> _encodeString(String string, [Encoding encoding = Encoding.UTF_8]) {
126 if (string.length == 0) return [];
127 var bytes;
128 var controller = new StreamController();
129 controller.stream
130 .transform(new StringEncoder(encoding))
131 .listen((data) => bytes = data);
132 controller.add(string);
133 controller.close();
134 assert(bytes != null);
135 return bytes;
136 }
137
138
139 class LineTransformer implements StreamTransformer<String, String> {
140 const int _LF = 10;
141 const int _CR = 13;
142
143 final StringBuffer _buffer = new StringBuffer();
144
145 StreamSubscription<String> _subscription;
146 StreamController<String> _controller;
147 String _carry;
148
149 Stream<String> bind(Stream<String> stream) {
150 _controller = new StreamController<String>(
151 onPauseStateChange: _pauseChanged,
152 onSubscriptionStateChange: _subscriptionChanged);
153
154 void handle(String data, bool isClosing) {
155 if (_carry != null) {
156 data = _carry.concat(data);
157 _carry = null;
158 }
159 int startPos = 0;
160 int pos = 0;
161 while (pos < data.length) {
162 int skip = 0;
163 int char = data.charCodeAt(pos);
164 if (char == _LF) {
165 skip = 1;
166 } else if (char == _CR) {
167 skip = 1;
168 if (pos + 1 < data.length) {
169 if (data.charCodeAt(pos + 1) == _LF) {
170 skip = 2;
171 }
172 } else if (!isClosing) {
173 _carry = data.substring(startPos);
174 return;
175 }
176 }
177 if (skip > 0) {
178 _buffer.add(data.substring(startPos, pos));
179 _controller.add(_buffer.toString());
180 _buffer.clear();
181 startPos = pos = pos + skip;
182 } else {
183 pos++;
184 }
185 }
186 if (pos != startPos) {
187 // Add remaining
188 _buffer.add(data.substring(startPos, pos));
189 }
190 if (isClosing && !_buffer.isEmpty) {
191 _controller.add(_buffer.toString());
192 _buffer.clear();
193 }
194 }
195
196 _subscription = stream.listen(
197 (data) => handle(data, false),
198 onDone: () {
199 // Handle remaining data (mainly _carry).
200 handle("", true);
201 _controller.close();
202 },
203 onError: _controller.signalError);
204 return _controller.stream;
205 }
206
207 void _pauseChanged() {
208 if (_controller.isPaused) {
209 _subscription.pause();
210 } else {
211 _subscription.resume();
212 }
213 }
214
215 void _subscriptionChanged() {
216 if (!_controller.hasSubscribers) {
217 _subscription.cancel();
218 }
219 }
220 }
221
222
223 class _SingleByteDecoder implements StreamTransformer<List<int>, String> {
224 StreamSubscription<List<int>> _subscription;
225 StreamController<String> _controller;
226 final int _replacementChar;
227
228 _SingleByteDecoder(this._replacementChar);
229
230 Stream<String> bind(Stream<List<int>> stream) {
231 _controller = new StreamController<String>(
232 onPauseStateChange: _pauseChanged,
233 onSubscriptionStateChange: _subscriptionChanged);
234 _subscription = stream.listen(
235 (data) {
236 var buffer = new List<int>.fixedLength(data.length);
237 for (int i = 0; i < data.length; i++) {
238 int char = _decodeByte(data[i]);
239 if (char < 0) char = _replacementChar;
240 buffer[i] = char;
241 }
242 _controller.add(new String.fromCharCodes(buffer));
243 },
244 onDone: _controller.close,
245 onError: _controller.signalError);
246 return _controller.stream;
247 }
248
249 int _decodeByte(int byte);
250
251 void _pauseChanged() {
252 if (_controller.isPaused) {
253 _subscription.pause();
254 } else {
255 _subscription.resume();
256 }
257 }
258
259 void _subscriptionChanged() {
260 if (!_controller.hasSubscribers) {
261 _subscription.cancel();
262 }
263 }
264 }
265
266
267 // Utility class for decoding ascii data delivered as a stream of
268 // bytes.
269 class _AsciiDecoder extends _SingleByteDecoder {
270 _AsciiDecoder(int replacementChar) : super(replacementChar);
271
272 int _decodeByte(int byte) => ((byte & 0x7f) == byte) ? byte : -1;
273 }
274
275
276 // Utility class for decoding Latin-1 data delivered as a stream of
277 // bytes.
278 class _Latin1Decoder extends _SingleByteDecoder {
279 _Latin1Decoder(int replacementChar) : super(replacementChar);
280
281 int _decodeByte(int byte) => ((byte & 0xFF) == byte) ? byte : -1;
282 }
283
284
285 class _SingleByteEncoder implements StreamTransformer<String, List<int>> {
286 StreamSubscription<String> _subscription;
287 StreamController<List<int>> _controller;
288
289 Stream<List<int>> bind(Stream<String> stream) {
290 _controller = new StreamController<List<int>>(
291 onPauseStateChange: _pauseChanged,
292 onSubscriptionStateChange: _subscriptionChanged);
293 _subscription = stream.listen(
294 (string) {
295 var bytes = _encode(string);
296 if (bytes == null) {
297 _controller.signalError(new FormatException(
298 "Invalid character for encoding"));
299 _controller.close();
300 _subscription.cancel();
301 } else {
302 _controller.add(bytes);
303 }
304 },
305 onDone: _controller.close,
306 onError: _controller.signalError);
307 return _controller.stream;
308 }
309
310 List<int> _encode(String string);
311
312 void _pauseChanged() {
313 if (_controller.isPaused) {
314 _subscription.pause();
315 } else {
316 _subscription.resume();
317 }
318 }
319
320 void _subscriptionChanged() {
321 if (!_controller.hasSubscribers) {
322 _subscription.cancel();
323 }
324 }
325 }
326
327
328 // Utility class for encoding a string into an ASCII byte stream.
329 class _AsciiEncoder extends _SingleByteEncoder {
330 List<int> _encode(String string) {
331 var bytes = string.charCodes;
332 for (var byte in bytes) {
333 if (byte > 127) return null;
334 }
335 return bytes;
336 }
337 }
338
339
340 // Utility class for encoding a string into a Latin1 byte stream.
341 class _Latin1Encoder extends _SingleByteEncoder {
342 List<int> _encode(String string) {
343 var bytes = string.charCodes;
344 for (var byte in bytes) {
345 if (byte > 255) return null;
346 }
347 return bytes;
348 }
349 }
350
351
352 // Utility class for encoding a string into a current windows
353 // code page byte list.
354 // Implemented on top of a _SingleByteEncoder, even though it's not really a
355 // single byte encoder, to avoid copying boilerplate.
356 class _WindowsCodePageEncoder extends _SingleByteEncoder {
357 List<int> _encode(String string) => _encodeString(string);
358
359 external static List<int> _encodeString(String string);
360 }
361
362
363 // Utility class for decoding Windows current code page data delivered
364 // as a stream of bytes.
365 class _WindowsCodePageDecoder implements StreamTransformer<List<int>, String> {
366 StreamSubscription<List<int>> _subscription;
367 StreamController<String> _controller;
368
369 Stream<String> bind(Stream<List<int>> stream) {
370 _controller = new StreamController<String>(
371 onPauseStateChange: _pauseChanged,
372 onSubscriptionStateChange: _subscriptionChanged);
373 _subscription = stream.listen(
374 (data) {
375 _controller.add(_decodeBytes(data));
376 },
377 onDone: _controller.close,
378 onError: _controller.signalError);
379 return _controller.stream;
380 }
381
382 external static String _decodeBytes(List<int> bytes);
383
384 void _pauseChanged() {
385 if (_controller.isPaused) {
386 _subscription.pause();
387 } else {
388 _subscription.resume();
389 }
390 }
391
392 void _subscriptionChanged() {
393 if (!_controller.hasSubscribers) {
394 _subscription.cancel();
395 }
396 }
397 }
OLDNEW
« no previous file with comments | « sdk/lib/io/io_stream_consumer.dart ('k') | sdk/lib/utf/utf_stream.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698