| 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 const int _maxAscii = 0x7f; | 5 const int _maxAscii = 0x7f; |
| 6 const int _maxLatin1 = 0xff; | 6 const int _maxLatin1 = 0xff; |
| 7 const int _maxUtf16 = 0xffff; | 7 const int _maxUtf16 = 0xffff; |
| 8 const int _maxUnicode = 0x10ffff; | 8 const int _maxUnicode = 0x10ffff; |
| 9 | 9 |
| 10 patch class String { | 10 patch class String { |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 80 | 80 |
| 81 factory _StringBase._uninstantiable() { | 81 factory _StringBase._uninstantiable() { |
| 82 throw new UnsupportedError( | 82 throw new UnsupportedError( |
| 83 "_StringBase can't be instaniated"); | 83 "_StringBase can't be instaniated"); |
| 84 } | 84 } |
| 85 | 85 |
| 86 Type get runtimeType => String; | 86 Type get runtimeType => String; |
| 87 | 87 |
| 88 int get hashCode native "String_getHashCode"; | 88 int get hashCode native "String_getHashCode"; |
| 89 | 89 |
| 90 bool get _isOneByte { | |
| 91 // Alternatively return false and override it on one-byte string classes. | |
| 92 int id = ClassID.getID(this); | |
| 93 return id == ClassID.cidOneByteString || | |
| 94 id == ClassID.cidExternalOneByteString; | |
| 95 } | |
| 96 | |
| 97 /** | 90 /** |
| 98 * Create the most efficient string representation for specified | 91 * Create the most efficient string representation for specified |
| 99 * [charCodes]. | 92 * [charCodes]. |
| 100 * | 93 * |
| 101 * Only uses the character codes betwen index [start] and index [end] of | 94 * Only uses the character codes betwen index [start] and index [end] of |
| 102 * `charCodes`. They must satisfy `0 <= start <= end <= charCodes.length`. | 95 * `charCodes`. They must satisfy `0 <= start <= end <= charCodes.length`. |
| 103 * | 96 * |
| 104 * The [limit] is an upper limit on the character codes in the iterable. | 97 * The [limit] is an upper limit on the character codes in the iterable. |
| 105 * It's `null` if unknown. | 98 * It's `null` if unknown. |
| 106 */ | 99 */ |
| (...skipping 452 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 559 [int startIndex = 0]) { | 552 [int startIndex = 0]) { |
| 560 if (pattern is! Pattern) { | 553 if (pattern is! Pattern) { |
| 561 throw new ArgumentError("${pattern} is not a Pattern"); | 554 throw new ArgumentError("${pattern} is not a Pattern"); |
| 562 } | 555 } |
| 563 if (replacement is! String) { | 556 if (replacement is! String) { |
| 564 throw new ArgumentError("${replacement} is not a String"); | 557 throw new ArgumentError("${replacement} is not a String"); |
| 565 } | 558 } |
| 566 if (startIndex is! int) { | 559 if (startIndex is! int) { |
| 567 throw new ArgumentError("${startIndex} is not an int"); | 560 throw new ArgumentError("${startIndex} is not an int"); |
| 568 } | 561 } |
| 569 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex"); | 562 if ((startIndex < 0) || (startIndex > this.length)) { |
| 563 throw new RangeError.range(startIndex, 0, this.length); |
| 564 } |
| 570 Iterator iterator = | 565 Iterator iterator = |
| 571 startIndex == 0 ? pattern.allMatches(this).iterator | 566 startIndex == 0 ? pattern.allMatches(this).iterator |
| 572 : pattern.allMatches(this, startIndex).iterator; | 567 : pattern.allMatches(this, startIndex).iterator; |
| 573 if (!iterator.moveNext()) return this; | 568 if (!iterator.moveNext()) return this; |
| 574 Match match = iterator.current; | 569 Match match = iterator.current; |
| 575 return "${this.substring(0, match.start)}" | 570 return "${this.substring(0, match.start)}" |
| 576 "$replacement" | 571 "$replacement" |
| 577 "${this.substring(match.end)}"; | 572 "${this.substring(match.end)}"; |
| 578 } | 573 } |
| 579 | 574 |
| (...skipping 26 matching lines...) Expand all Loading... |
| 606 } | 601 } |
| 607 } else { | 602 } else { |
| 608 for (Match match in pattern.allMatches(this)) { | 603 for (Match match in pattern.allMatches(this)) { |
| 609 length += _addReplaceSlice(matches, startIndex, match.start); | 604 length += _addReplaceSlice(matches, startIndex, match.start); |
| 610 matches.add(replacement); | 605 matches.add(replacement); |
| 611 length += replacementLength; | 606 length += replacementLength; |
| 612 startIndex = match.end; | 607 startIndex = match.end; |
| 613 } | 608 } |
| 614 } | 609 } |
| 615 length += _addReplaceSlice(matches, startIndex, this.length); | 610 length += _addReplaceSlice(matches, startIndex, this.length); |
| 616 bool replacementIsOneByte = replacement._isOneByte; | 611 bool replacementIsOneByte = (replacement is _OneByteString) || |
| 617 if (replacementIsOneByte && | 612 (replacement is _ExternalOneByteString); |
| 618 length < _maxJoinReplaceOneByteStringLength && | 613 if (replacementIsOneByte && length < _maxJoinReplaceOneByteStringLength) { |
| 619 this._isOneByte) { | |
| 620 // TODO(lrn): Is there a cut-off point, or is runtime always faster? | 614 // TODO(lrn): Is there a cut-off point, or is runtime always faster? |
| 621 return _joinReplaceAllOneByteResult(this, matches, length); | 615 bool thisIsOneByte = (this is _OneByteString) || |
| 616 (this is _ExternalOneByteString); |
| 617 if (replacementIsOneByte && thisIsOneByte) { |
| 618 return _joinReplaceAllOneByteResult(this, matches, length); |
| 619 } |
| 622 } | 620 } |
| 623 return _joinReplaceAllResult(this, matches, length, | 621 return _joinReplaceAllResult(this, matches, length, |
| 624 replacementIsOneByte); | 622 replacementIsOneByte); |
| 625 } | 623 } |
| 626 | 624 |
| 627 /** | 625 /** |
| 628 * As [_joinReplaceAllResult], but knowing that the result | 626 * As [_joinReplaceAllResult], but knowing that the result |
| 629 * is always a [_OneByteString]. | 627 * is always a [_OneByteString]. |
| 630 */ | 628 */ |
| 631 static String _joinReplaceAllOneByteResult(String base, | 629 static String _joinReplaceAllOneByteResult(String base, |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 683 | 681 |
| 684 String replaceAllMapped(Pattern pattern, String replace(Match match)) { | 682 String replaceAllMapped(Pattern pattern, String replace(Match match)) { |
| 685 if (pattern == null) throw new ArgumentError.notNull("pattern"); | 683 if (pattern == null) throw new ArgumentError.notNull("pattern"); |
| 686 if (replace == null) throw new ArgumentError.notNull("replace"); | 684 if (replace == null) throw new ArgumentError.notNull("replace"); |
| 687 List matches = []; | 685 List matches = []; |
| 688 int length = 0; | 686 int length = 0; |
| 689 int startIndex = 0; | 687 int startIndex = 0; |
| 690 bool replacementStringsAreOneByte = true; | 688 bool replacementStringsAreOneByte = true; |
| 691 for (Match match in pattern.allMatches(this)) { | 689 for (Match match in pattern.allMatches(this)) { |
| 692 length += _addReplaceSlice(matches, startIndex, match.start); | 690 length += _addReplaceSlice(matches, startIndex, match.start); |
| 693 var replacement = "${replace(match)}"; | 691 String replacement = replace(match).toString(); |
| 694 matches.add(replacement); | 692 matches.add(replacement); |
| 695 length += replacement.length; | 693 length += replacement.length; |
| 696 replacementStringsAreOneByte = | 694 replacementStringsAreOneByte = replacementStringsAreOneByte && |
| 697 replacementStringsAreOneByte && replacement._isOneByte; | 695 (replacement is _OneByteString || |
| 696 replacement is _ExternalOneByteString); |
| 698 startIndex = match.end; | 697 startIndex = match.end; |
| 699 } | 698 } |
| 700 if (matches.isEmpty) return this; | |
| 701 length += _addReplaceSlice(matches, startIndex, this.length); | 699 length += _addReplaceSlice(matches, startIndex, this.length); |
| 702 if (replacementStringsAreOneByte && | 700 if (replacementStringsAreOneByte && |
| 703 length < _maxJoinReplaceOneByteStringLength && | 701 length < _maxJoinReplaceOneByteStringLength) { |
| 704 this._isOneByte) { | 702 bool thisIsOneByte = (this is _OneByteString) || |
| 705 return _joinReplaceAllOneByteResult(this, matches, length); | 703 (this is _ExternalOneByteString); |
| 704 if (thisIsOneByte) { |
| 705 return _joinReplaceAllOneByteResult(this, matches, length); |
| 706 } |
| 706 } | 707 } |
| 707 return _joinReplaceAllResult(this, matches, length, | 708 return _joinReplaceAllResult(this, matches, length, |
| 708 replacementStringsAreOneByte); | 709 replacementStringsAreOneByte); |
| 709 } | 710 } |
| 710 | 711 |
| 711 String replaceFirstMapped(Pattern pattern, String replace(Match match), | |
| 712 [int startIndex = 0]) { | |
| 713 if (pattern == null) throw new ArgumentError.notNull("pattern"); | |
| 714 if (replace == null) throw new ArgumentError.notNull("replace"); | |
| 715 if (startIndex == null) throw new ArgumentError.notNull("startIndex"); | |
| 716 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex"); | |
| 717 | |
| 718 var matches = pattern.allMatches(this, startIndex).iterator; | |
| 719 if (!matches.moveNext()) return this; | |
| 720 var match = matches.current; | |
| 721 var replacement = "${replace(match)}"; | |
| 722 var slices = []; | |
| 723 int length = 0; | |
| 724 if (match.start > 0) { | |
| 725 length += _addReplaceSlice(slices, 0, match.start); | |
| 726 } | |
| 727 slices.add(replacement); | |
| 728 length += replacement.length; | |
| 729 if (match.end < this.length) { | |
| 730 length += _addReplaceSlice(slices, match.end, this.length); | |
| 731 } | |
| 732 bool replacementIsOneByte = _replacement._isOneByte; | |
| 733 if (replacementIsOneByte && | |
| 734 length < _maxJoinReplaceOneByteStringLength && | |
| 735 this._isOneByte) { | |
| 736 return _joinReplaceAllOneByteResult(this, matches, length); | |
| 737 } | |
| 738 return _joinReplaceAllResult(this, slices, length, replacementIsOneByte); | |
| 739 } | |
| 740 | |
| 741 static String _matchString(Match match) => match[0]; | 712 static String _matchString(Match match) => match[0]; |
| 742 static String _stringIdentity(String string) => string; | 713 static String _stringIdentity(String string) => string; |
| 743 | 714 |
| 744 String _splitMapJoinEmptyString(String onMatch(Match match), | 715 String _splitMapJoinEmptyString(String onMatch(Match match), |
| 745 String onNonMatch(String nonMatch)) { | 716 String onNonMatch(String nonMatch)) { |
| 746 // Pattern is the empty string. | 717 // Pattern is the empty string. |
| 747 StringBuffer buffer = new StringBuffer(); | 718 StringBuffer buffer = new StringBuffer(); |
| 748 int length = this.length; | 719 int length = this.length; |
| 749 int i = 0; | 720 int i = 0; |
| 750 buffer.write(onNonMatch("")); | 721 buffer.write(onNonMatch("")); |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 792 startIndex = match.end; | 763 startIndex = match.end; |
| 793 } | 764 } |
| 794 buffer.write(onNonMatch(this.substring(startIndex))); | 765 buffer.write(onNonMatch(this.substring(startIndex))); |
| 795 return buffer.toString(); | 766 return buffer.toString(); |
| 796 } | 767 } |
| 797 | 768 |
| 798 // Convert single object to string. | 769 // Convert single object to string. |
| 799 static String _interpolateSingle(Object o) { | 770 static String _interpolateSingle(Object o) { |
| 800 final s = o.toString(); | 771 final s = o.toString(); |
| 801 if (s is! String) { | 772 if (s is! String) { |
| 802 throw new ArgumentError(Error.safeToString(o)); | 773 throw new ArgumentError(o); |
| 803 } | 774 } |
| 804 return s; | 775 return s; |
| 805 } | 776 } |
| 806 | 777 |
| 807 /** | 778 /** |
| 808 * Convert all objects in [values] to strings and concat them | 779 * Convert all objects in [values] to strings and concat them |
| 809 * into a result string. | 780 * into a result string. |
| 810 * Modifies the input list if it contains non-`String` values. | 781 * Modifies the input list if it contains non-`String` values. |
| 811 */ | 782 */ |
| 812 static String _interpolate(final List values) { | 783 static String _interpolate(final List values) { |
| 813 final numValues = values.length; | 784 final numValues = values.length; |
| 814 int totalLength = 0; | 785 int totalLength = 0; |
| 815 int i = 0; | 786 int i = 0; |
| 816 while (i < numValues) { | 787 while (i < numValues) { |
| 817 final e = values[i]; | 788 final e = values[i]; |
| 818 final s = e.toString(); | 789 final s = e.toString(); |
| 819 values[i] = s; | 790 values[i] = s; |
| 820 if (ClassID.getID(s) == ClassID.cidOneByteString) { | 791 if (ClassID.getID(s) == ClassID.cidOneByteString) { |
| 821 totalLength += s.length; | 792 totalLength += s.length; |
| 822 i++; | 793 i++; |
| 823 } else if (s is! String) { | 794 } else if (s is! String) { |
| 824 throw new ArgumentError(Error.safeToString(e)); | 795 throw new ArgumentError(s); |
| 825 } else { | 796 } else { |
| 826 // Handle remaining elements without checking for one-byte-ness. | 797 // Handle remaining elements without checking for one-byte-ness. |
| 827 while (++i < numValues) { | 798 while (++i < numValues) { |
| 828 final e = values[i]; | 799 final e = values[i]; |
| 829 final s = e.toString(); | 800 final s = e.toString(); |
| 830 values[i] = s; | 801 values[i] = s; |
| 831 if (s is! String) { | 802 if (s is! String) { |
| 832 throw new ArgumentError(Error.safeToString(e)); | 803 throw new ArgumentError(s); |
| 833 } | 804 } |
| 834 } | 805 } |
| 835 return _concatRangeNative(values, 0, numValues); | 806 return _concatRangeNative(values, 0, numValues); |
| 836 } | 807 } |
| 837 } | 808 } |
| 838 // All strings were one-byte strings. | 809 // All strings were one-byte strings. |
| 839 return _OneByteString._concatAll(values, totalLength); | 810 return _OneByteString._concatAll(values, totalLength); |
| 840 } | 811 } |
| 841 | 812 |
| 842 Iterable<Match> allMatches(String string, [int start = 0]) { | 813 Iterable<Match> allMatches(String string, [int start = 0]) { |
| (...skipping 450 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1293 for (int g in groups) { | 1264 for (int g in groups) { |
| 1294 result.add(group(g)); | 1265 result.add(group(g)); |
| 1295 } | 1266 } |
| 1296 return result; | 1267 return result; |
| 1297 } | 1268 } |
| 1298 | 1269 |
| 1299 final int start; | 1270 final int start; |
| 1300 final String input; | 1271 final String input; |
| 1301 final String pattern; | 1272 final String pattern; |
| 1302 } | 1273 } |
| OLD | NEW |