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 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
188 E elementAt(int index) { | 188 E elementAt(int index) { |
189 if (index is! int || index < 0) throw new RangeError.value(index); | 189 if (index is! int || index < 0) throw new RangeError.value(index); |
190 int remaining = index; | 190 int remaining = index; |
191 for (E element in this) { | 191 for (E element in this) { |
192 if (remaining == 0) return element; | 192 if (remaining == 0) return element; |
193 remaining--; | 193 remaining--; |
194 } | 194 } |
195 throw new RangeError.value(index); | 195 throw new RangeError.value(index); |
196 } | 196 } |
197 | 197 |
198 String toString() => _iterableToString(this); | 198 String toString() => IterableBase.iterableToShortString(this, '(', ')'); |
199 } | 199 } |
200 | 200 |
201 /** | 201 /** |
202 * Base class for implementing [Iterable]. | 202 * Base class for implementing [Iterable]. |
203 * | 203 * |
204 * This class implements all methods of [Iterable] except [Iterable.iterator] | 204 * This class implements all methods of [Iterable] except [Iterable.iterator] |
205 * in terms of `iterator`. | 205 * in terms of `iterator`. |
206 */ | 206 */ |
207 abstract class IterableBase<E> implements Iterable<E> { | 207 abstract class IterableBase<E> implements Iterable<E> { |
208 // TODO(lrn): Base this on IterableMixin if there ever becomes a way | 208 // TODO(lrn): Base this on IterableMixin if there ever becomes a way |
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
395 * Elements are represented by their own `toString` results. | 395 * Elements are represented by their own `toString` results. |
396 * | 396 * |
397 * The representation always contains the first three elements. | 397 * The representation always contains the first three elements. |
398 * If there are less than a hundred elements in the iterable, it also | 398 * If there are less than a hundred elements in the iterable, it also |
399 * contains the last two elements. | 399 * contains the last two elements. |
400 * | 400 * |
401 * If the resulting string isn't above 80 characters, more elements are | 401 * If the resulting string isn't above 80 characters, more elements are |
402 * included from the start of the iterable. | 402 * included from the start of the iterable. |
403 * | 403 * |
404 * The conversion may omit calling `toString` on some elements if they | 404 * The conversion may omit calling `toString` on some elements if they |
405 * are known to now occur in the output, and it may stop iterating after | 405 * are known to not occur in the output, and it may stop iterating after |
406 * a hundred elements. | 406 * a hundred elements. |
407 */ | 407 */ |
408 String toString() => _iterableToString(this); | 408 String toString() => IterableBase.iterableToShortString(this, '(', ')'); |
floitsch
2014/05/23 16:09:58
-IterableBase.-
Lasse Reichstein Nielsen
2014/05/23 16:50:01
You mean I should remove it?
It is in the same cla
| |
409 } | |
410 | 409 |
411 String _setToString(Set set) => _collectionToString(set, "{" , "}"); | 410 /** |
412 | 411 * Convert an `Iterable` to a string like [IterableBase.toString]. |
413 String _iterableToString(Iterable iterable) => | 412 * |
414 _collectionToString(iterable, "(", ")"); | 413 * Allows using other delimiters than '(' and ')'. |
415 | 414 * |
416 String _collectionToString(Iterable iterable, String before, String after) { | 415 * Handles circular references where converting one of the elements |
417 if (_toStringVisiting.contains(iterable)) return "$before...$after"; | 416 * to a string ends up converting [iterable] to a string again. |
418 _toStringVisiting.add(iterable); | 417 */ |
419 List parts = []; | 418 static String iterableToShortString(Iterable iterable, |
floitsch
2014/05/23 16:09:58
Isn't this an exposed class?
Do you want to expose
Lasse Reichstein Nielsen
2014/05/23 16:50:01
Yes, I want to expose it.
I see no reason to have
| |
420 try { | 419 [String leftDelimiter = '(', |
421 _collectionPartsToStrings(iterable, parts); | 420 String rightDelimiter = ')']) { |
422 } finally { | 421 if (_toStringVisiting.contains(iterable)) { |
423 _toStringVisiting.remove(iterable); | 422 if (leftDelimiter == "(" && rightDelimiter == ")") { |
424 } | 423 // Avoid creating a new string in the "common" case. |
425 return (new StringBuffer(before) | 424 return "(...)"; |
426 ..writeAll(parts, ", ") | 425 } |
427 ..write(after)).toString(); | 426 return "$leftDelimiter...$rightDelimiter"; |
428 } | |
429 | |
430 /** Convert elments of [iterable] to strings and store them in [parts]. */ | |
431 void _collectionPartsToStrings(Iterable iterable, List parts) { | |
432 /// Try to stay below this many characters. | |
433 const int LENGTH_LIMIT = 80; | |
434 /// Always at least this many elements at the start. | |
435 const int HEAD_COUNT = 3; | |
436 /// Always at least this many elements at the end. | |
437 const int TAIL_COUNT = 2; | |
438 /// Stop iterating after this many elements. Iterables can be infinite. | |
439 const int MAX_COUNT = 100; | |
440 // Per entry length overhead. It's for ", " for all after the first entry, | |
441 // and for "(" and ")" for the initial entry. By pure luck, that's the same | |
442 // number. | |
443 const int OVERHEAD = 2; | |
444 const int ELLIPSIS_SIZE = 3; // "...".length. | |
445 int length = 0; | |
446 int count = 0; | |
447 Iterator it = iterable.iterator; | |
448 // Initial run of elements, at least HEAD_COUNT, and then continue until | |
449 // passing at most LENGTH_LIMIT characters. | |
450 while (length < LENGTH_LIMIT || count < HEAD_COUNT) { | |
451 if (!it.moveNext()) { | |
452 return; | |
453 } | 427 } |
454 String next = "${it.current}"; | 428 List parts = []; |
455 parts.add(next); | 429 _toStringVisiting.add(iterable); |
456 length += next.length + OVERHEAD; | 430 try { |
457 count++; | 431 _iterablePartsToStrings(iterable, parts); |
432 } finally { | |
433 _toStringVisiting.remove(iterable); | |
434 } | |
435 return (new StringBuffer(leftDelimiter) | |
436 ..writeAll(parts, ", ") | |
437 ..write(rightDelimiter)).toString(); | |
458 } | 438 } |
459 | 439 |
460 String penultimateString; | 440 /** |
461 String ultimateString; | 441 * Converts an `Iterable` to a string. |
442 * | |
443 * Converts each elements to a string, and separates the results by ", ". | |
444 * Then wraps the result in [leftDelimiter] and [rightDelimiter]. | |
445 * | |
446 * Unlike [iterableToShortString], this conversion doesn't omit any | |
447 * elements or puts any limit on the size of the result. | |
448 * | |
449 * Handles circular references where converting one of the elements | |
450 * to a string ends up converting [iterable] to a string again. | |
451 */ | |
452 static String iterableToFullString(Iterable iterable, | |
floitsch
2014/05/23 16:09:58
ditto.
Lasse Reichstein Nielsen
2014/05/23 16:50:01
And yes, intended public.
I don't want anybody to
| |
453 [String leftDelimiter = '(', | |
454 String rightDelimiter = ')']) { | |
455 if (_toStringVisiting.contains(iterable)) { | |
456 return "$leftDelimiter...$rightDelimiter"; | |
457 } | |
458 StringBuffer buffer = new StringBuffer(leftDelimiter); | |
459 _toStringVisiting.add(iterable); | |
460 try { | |
461 buffer.writeAll(iterable, ", "); | |
462 } finally { | |
463 _toStringVisiting.remove(iterable); | |
464 } | |
465 buffer.write(rightDelimiter); | |
466 return buffer.toString(); | |
467 } | |
462 | 468 |
463 // Find last two elements. One or more of them may already be in the | 469 /** A set used to identify cyclic lists during toString() calls. */ |
464 // parts array. Include their length in `length`. | 470 static Set _toStringVisiting = new HashSet.identity(); |
465 var penultimate = null; | 471 |
466 var ultimate = null; | 472 /** |
467 if (!it.moveNext()) { | 473 * Convert elments of [iterable] to strings and store them in [parts]. |
468 if (count <= HEAD_COUNT + TAIL_COUNT) return; | 474 * |
469 ultimateString = parts.removeLast(); | 475 * This is the complicated part of [iterableToShortString]. |
470 penultimateString = parts.removeLast(); | 476 * It is extracted as a separate function to avoid having too much code |
471 } else { | 477 * inside the try/finally. |
floitsch
2014/05/23 16:09:58
This should not be a dart-doc comment.
Feel free
Lasse Reichstein Nielsen
2014/05/23 16:50:01
Why not. It's a private method, so it won't be exp
| |
472 penultimate = it.current; | 478 */ |
473 count++; | 479 static void _iterablePartsToStrings(Iterable iterable, List parts) { |
480 /// Try to stay below this many characters. | |
481 const int LENGTH_LIMIT = 80; | |
482 /// Always at least this many elements at the start. | |
483 const int HEAD_COUNT = 3; | |
484 /// Always at least this many elements at the end. | |
485 const int TAIL_COUNT = 2; | |
486 /// Stop iterating after this many elements. Iterables can be infinite. | |
487 const int MAX_COUNT = 100; | |
488 // Per entry length overhead. It's for ", " for all after the first entry, | |
489 // and for "(" and ")" for the initial entry. By pure luck, that's the same | |
490 // number. | |
491 const int OVERHEAD = 2; | |
492 const int ELLIPSIS_SIZE = 3; // "...".length. | |
493 | |
494 int length = 0; | |
495 int count = 0; | |
496 Iterator it = iterable.iterator; | |
497 // Initial run of elements, at least HEAD_COUNT, and then continue until | |
498 // passing at most LENGTH_LIMIT characters. | |
499 while (length < LENGTH_LIMIT || count < HEAD_COUNT) { | |
500 if (!it.moveNext()) return; | |
501 String next = "${it.current}"; | |
502 parts.add(next); | |
503 length += next.length + OVERHEAD; | |
504 count++; | |
505 } | |
506 | |
507 String penultimateString; | |
508 String ultimateString; | |
509 | |
510 // Find last two elements. One or more of them may already be in the | |
511 // parts array. Include their length in `length`. | |
512 var penultimate = null; | |
513 var ultimate = null; | |
474 if (!it.moveNext()) { | 514 if (!it.moveNext()) { |
475 if (count <= HEAD_COUNT + 1) { | 515 if (count <= HEAD_COUNT + TAIL_COUNT) return; |
476 parts.add("$penultimate"); | 516 ultimateString = parts.removeLast(); |
477 return; | |
478 } | |
479 ultimateString = "$penultimate"; | |
480 penultimateString = parts.removeLast(); | 517 penultimateString = parts.removeLast(); |
481 length += ultimateString.length + OVERHEAD; | |
482 } else { | 518 } else { |
483 ultimate = it.current; | 519 penultimate = it.current; |
484 count++; | 520 count++; |
485 // Then keep looping, keeping the last two elements in variables. | 521 if (!it.moveNext()) { |
486 assert(count < MAX_COUNT); | 522 if (count <= HEAD_COUNT + 1) { |
487 while (it.moveNext()) { | 523 parts.add("$penultimate"); |
488 penultimate = ultimate; | 524 return; |
525 } | |
526 ultimateString = "$penultimate"; | |
527 penultimateString = parts.removeLast(); | |
528 length += ultimateString.length + OVERHEAD; | |
529 } else { | |
489 ultimate = it.current; | 530 ultimate = it.current; |
490 count++; | 531 count++; |
491 if (count > MAX_COUNT) { | 532 // Then keep looping, keeping the last two elements in variables. |
492 // If we haven't found the end before MAX_COUNT, give up. | 533 assert(count < MAX_COUNT); |
493 // This cannot happen in the code above because each entry | 534 while (it.moveNext()) { |
494 // increases length by at least two, so there is no way to | 535 penultimate = ultimate; |
495 // visit more than ~40 elements before this loop. | 536 ultimate = it.current; |
537 count++; | |
538 if (count > MAX_COUNT) { | |
539 // If we haven't found the end before MAX_COUNT, give up. | |
540 // This cannot happen in the code above because each entry | |
541 // increases length by at least two, so there is no way to | |
542 // visit more than ~40 elements before this loop. | |
496 | 543 |
497 // Remove any surplus elements until length, including ", ...)", | 544 // Remove any surplus elements until length, including ", ...)", |
498 // is at most LENGTH_LIMIT. | 545 // is at most LENGTH_LIMIT. |
499 while (length > LENGTH_LIMIT - ELLIPSIS_SIZE - OVERHEAD && | 546 while (length > LENGTH_LIMIT - ELLIPSIS_SIZE - OVERHEAD && |
500 count > HEAD_COUNT) { | 547 count > HEAD_COUNT) { |
501 length -= parts.removeLast().length + OVERHEAD; | 548 length -= parts.removeLast().length + OVERHEAD; |
502 count--; | 549 count--; |
550 } | |
551 parts.add("..."); | |
552 return; | |
503 } | 553 } |
504 parts.add("..."); | |
505 return; | |
506 } | 554 } |
555 penultimateString = "$penultimate"; | |
556 ultimateString = "$ultimate"; | |
557 length += | |
558 ultimateString.length + penultimateString.length + 2 * OVERHEAD; | |
507 } | 559 } |
508 penultimateString = "$penultimate"; | |
509 ultimateString = "$ultimate"; | |
510 length += | |
511 ultimateString.length + penultimateString.length + 2 * OVERHEAD; | |
512 } | 560 } |
513 } | |
514 | 561 |
515 // If there is a gap between the initial run and the last two, | 562 // If there is a gap between the initial run and the last two, |
516 // prepare to add an ellipsis. | 563 // prepare to add an ellipsis. |
517 String elision = null; | 564 String elision = null; |
518 if (count > parts.length + TAIL_COUNT) { | 565 if (count > parts.length + TAIL_COUNT) { |
519 elision = "..."; | |
520 length += ELLIPSIS_SIZE + OVERHEAD; | |
521 } | |
522 | |
523 // If the last two elements were very long, and we have more than | |
524 // HEAD_COUNT elements in the initial run, drop some to make room for | |
525 // the last two. | |
526 while (length > LENGTH_LIMIT && parts.length > HEAD_COUNT) { | |
527 String lastPart = parts.removeLast(); | |
528 length -= lastPart.length + OVERHEAD; | |
529 if (elision == null) { | |
530 elision = "..."; | 566 elision = "..."; |
531 length += ELLIPSIS_SIZE + OVERHEAD; | 567 length += ELLIPSIS_SIZE + OVERHEAD; |
532 } | 568 } |
569 | |
570 // If the last two elements were very long, and we have more than | |
571 // HEAD_COUNT elements in the initial run, drop some to make room for | |
572 // the last two. | |
573 while (length > LENGTH_LIMIT && parts.length > HEAD_COUNT) { | |
574 length -= parts.removeLast().length + OVERHEAD; | |
575 if (elision == null) { | |
576 elision = "..."; | |
577 length += ELLIPSIS_SIZE + OVERHEAD; | |
578 } | |
579 } | |
580 if (elision != null) { | |
581 parts.add(elision); | |
582 } | |
583 parts.add(penultimateString); | |
584 parts.add(ultimateString); | |
533 } | 585 } |
534 if (elision != null) { | |
535 parts.add(elision); | |
536 } | |
537 parts.add(penultimateString); | |
538 parts.add(ultimateString); | |
539 } | 586 } |
OLD | NEW |