Chromium Code Reviews| 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 |