OLD | NEW |
---|---|
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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.collection; | 5 part of dart.collection; |
6 | 6 |
7 /** | 7 /** |
8 * This [Iterable] mixin implements all [Iterable] members except `iterator`. | 8 * This [Iterable] mixin implements all [Iterable] members except `iterator`. |
9 * | 9 * |
10 * All other methods are implemented in terms of `iterator`. | 10 * All other methods are implemented in terms of `iterator`. |
(...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
181 | 181 |
182 E elementAt(int index) { | 182 E elementAt(int index) { |
183 if (index is! int || index < 0) throw new RangeError.value(index); | 183 if (index is! int || index < 0) throw new RangeError.value(index); |
184 int remaining = index; | 184 int remaining = index; |
185 for (E element in this) { | 185 for (E element in this) { |
186 if (remaining == 0) return element; | 186 if (remaining == 0) return element; |
187 remaining--; | 187 remaining--; |
188 } | 188 } |
189 throw new RangeError.value(index); | 189 throw new RangeError.value(index); |
190 } | 190 } |
191 | |
192 String toString() => _iterableToString(this); | |
191 } | 193 } |
192 | 194 |
193 /** | 195 /** |
194 * Base class for implementing [Iterable]. | 196 * Base class for implementing [Iterable]. |
195 * | 197 * |
196 * This class implements all methods of [Iterable] except [Iterable.iterator] | 198 * This class implements all methods of [Iterable] except [Iterable.iterator] |
197 * in terms of `iterator`. | 199 * in terms of `iterator`. |
198 */ | 200 */ |
199 abstract class IterableBase<E> implements Iterable<E> { | 201 abstract class IterableBase<E> implements Iterable<E> { |
200 // TODO(lrn): Base this on IterableMixin if there ever becomes a way | 202 // TODO(lrn): Base this on IterableMixin if there ever becomes a way |
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
372 | 374 |
373 E elementAt(int index) { | 375 E elementAt(int index) { |
374 if (index is! int || index < 0) throw new RangeError.value(index); | 376 if (index is! int || index < 0) throw new RangeError.value(index); |
375 int remaining = index; | 377 int remaining = index; |
376 for (E element in this) { | 378 for (E element in this) { |
377 if (remaining == 0) return element; | 379 if (remaining == 0) return element; |
378 remaining--; | 380 remaining--; |
379 } | 381 } |
380 throw new RangeError.value(index); | 382 throw new RangeError.value(index); |
381 } | 383 } |
384 | |
385 /** | |
386 * Returns a string representation of (some of) the elements of this | |
387 * Iterabele. | |
floitsch
2013/10/11 09:29:43
of `this`.
I don't like to repeat the name of the
Lasse Reichstein Nielsen
2013/10/11 20:25:37
Done.
| |
388 * | |
389 * Elements are represented by their own `toString` results. | |
390 * | |
391 * The representation always contains the first three elements. | |
392 * If there are less than a hundred elements in the iterable, it also | |
393 * contains the last two elements. | |
394 * | |
395 * If the resulting string isn't above 80 characters, more elements are | |
396 * included from the start of the iterable. | |
397 * | |
398 * The conversion may omit calling `toString` on some elements if they | |
floitsch
2013/10/11 09:29:43
Not sure we want to keep this sentence. If it's no
Lasse Reichstein Nielsen
2013/10/11 20:25:37
I think it's worth emphasizing, to avoid someone t
| |
399 * are known to now occur in the output, and it may stop iterating after | |
400 * a hundred elements. | |
401 */ | |
402 String toString() => _iterableToString(this); | |
382 } | 403 } |
404 | |
405 String _iterableToString(Iterable iterable) { | |
406 const int LENGTH_LIMIT = 80; | |
407 const int MIN_COUNT = 3; // Always at least this many elements at the start. | |
408 const int MAX_COUNT = 100; | |
409 // Per entry length overhead for ", " (or for "(" and ")" for initial entry) | |
410 const int OVERHEAD = 2; | |
411 const int ELLIPSIS_SIZE = 3; // "...".length. | |
412 if (_toStringVisiting.contains(iterable)) return "(...)"; | |
413 _toStringVisiting.add(iterable); | |
414 List result = []; | |
415 try { | |
416 building: { // Break this block to complete the toString. | |
417 int length = 0; | |
418 int count = 0; | |
419 Iterator it = iterable.iterator; | |
420 // Initial run of elements, at least MIN_COUNT, and then continue until | |
421 // passing at most LENGTH_LIMIT characters. | |
422 while (length < LENGTH_LIMIT || count < MIN_COUNT) { | |
floitsch
2013/10/11 09:29:43
nit. reorder:
while (count < MIN_COUNT || length <
Lasse Reichstein Nielsen
2013/10/11 20:25:37
Done.
Although for efficiency, I'd expect the leng
| |
423 if (!it.moveNext()) break building; | |
424 String next = "${it.current}"; | |
floitsch
2013/10/11 09:29:43
it.current.toString() is more explicit.
But ok to
Lasse Reichstein Nielsen
2013/10/11 20:25:37
It may be slightly faster to call toString because
| |
425 result.add(next); | |
426 length += next.length + OVERHEAD; // Includes ")" for the first entry. | |
427 count++; | |
428 } | |
429 String penultimateString; | |
430 String ultimateString; | |
431 | |
432 // Find last two elements. One or more of them may already be in the | |
433 // result array. Include their length in `length`. | |
434 var penultimate = null; | |
435 var ultimate = null; | |
436 if (!it.moveNext()) { | |
437 if (count <= MIN_COUNT + 2) break building; | |
floitsch
2013/10/11 09:29:43
Add comment?:
// No need for ellipsis.
Lasse Reichstein Nielsen
2013/10/11 20:25:37
Done.
| |
438 ultimateString = result.removeLast(); | |
439 penultimateString = result.removeLast(); | |
440 } else { | |
441 penultimate = it.current; | |
442 count++; | |
443 if (!it.moveNext()) { | |
444 if (count <= MIN_COUNT + 1) { | |
445 result.add("$penultimate"); | |
446 break building; | |
447 } | |
448 ultimateString = "$penultimate"; | |
449 penultimateString = result.removeLast(); | |
450 length += ultimateString.length + OVERHEAD; | |
451 } else { | |
452 ultimate = it.current; | |
453 count++; | |
454 // Then keep looping, keeping the last two elements in variables. | |
floitsch
2013/10/11 09:29:43
"Then" doesn't fit.
Loop, keeping ...
Lasse Reichstein Nielsen
2013/10/11 20:25:37
Done.
| |
455 assert(count < MAX_COUNT); | |
456 while (it.moveNext()) { | |
457 penultimate = ultimate; | |
458 ultimate = it.current; | |
459 count++; | |
460 if (count > MAX_COUNT) { | |
461 // If we haven't found the end before MAX_COUNT, give up. | |
462 // This cannot happen before because each count increases | |
463 // length by at least two, so there is no way to see more | |
464 // than ~40 elements before this loop. | |
465 | |
466 // Remove any surplus elements until length including ", ...)" | |
467 // is at most LENGTH_LIMIT. | |
468 while (length > LENGTH_LIMIT - ELLIPSIS_SIZE - OVERHEAD && | |
469 count > MIN_COUNT) { | |
470 length -= result.removeLast().length + OVERHEAD; | |
471 count--; | |
472 } | |
473 result.add("..."); | |
474 break building; | |
475 } | |
476 } | |
477 penultimateString = "$penultimate"; | |
478 ultimateString = "$ultimate"; | |
479 length += | |
480 ultimateString.length + penultimateString.length + 2 * OVERHEAD; | |
481 } | |
482 } | |
483 | |
484 // If there is a gap between the initial run and the last two, | |
485 // prepare to add an ellipsis. | |
486 String elision = null; | |
487 if (count > result.length + 2) { | |
488 elision = "..."; | |
489 length += ELLIPSIS_SIZE + OVERHEAD; | |
490 } | |
491 | |
492 // If the last two elements were very long, and we have more than | |
493 // MIN_COUNT elements in the initial run, drop some to make room for | |
494 // the last two. | |
495 while (length > LENGTH_LIMIT && result.length > MIN_COUNT) { | |
496 length -= result.removeLast().length + OVERHEAD; | |
497 if (elision == null) { | |
498 elision = "..."; | |
499 length += ELLIPSIS_SIZE + OVERHEAD; | |
500 } | |
501 } | |
502 if (elision != null) { | |
503 result.add(elision); | |
504 } | |
505 result.add(penultimateString); | |
506 result.add(ultimateString); | |
507 } | |
508 } finally { | |
509 _toStringVisiting.remove(iterable); | |
510 } | |
511 return (new StringBuffer("(")..writeAll(result, ", ")..write(")")).toString(); | |
512 } | |
OLD | NEW |