Index: runtime/lib/string_base.dart |
diff --git a/runtime/lib/string_base.dart b/runtime/lib/string_base.dart |
index 3d2484e17b64c2c1304bb9c72ea4c9b7c53bb284..1f6d9fe7bfd28d5fc6cb0b00bd53f13943978722 100644 |
--- a/runtime/lib/string_base.dart |
+++ b/runtime/lib/string_base.dart |
@@ -155,6 +155,41 @@ class _StringBase { |
return _substringUnchecked(startIndex, endIndex); |
} |
+ String slice([int startIndex, int endIndex]) { |
+ int start, end; |
+ if (startIndex == null) { |
+ start = 0; |
+ } else if (startIndex is! int) { |
+ throw new ArgumentError("startIndex is not int"); |
+ } else if (startIndex >= 0) { |
+ start = startIndex; |
+ } else { |
+ start = this.length + startIndex; |
+ } |
+ if (start < 0 || start > this.length) { |
+ throw new RangeError( |
+ "startIndex out of range: $startIndex (length: $length)"); |
+ } |
+ if (endIndex == null) { |
+ end = this.length; |
+ } else if (endIndex is! int) { |
+ throw new ArgumentError("endIndex is not int"); |
+ } else if (endIndex >= 0) { |
+ end = endIndex; |
+ } else { |
+ end = this.length + endIndex; |
+ } |
+ if (end < 0 || end > this.length) { |
+ throw new RangeError( |
+ "endIndex out of range: $endIndex (length: $length)"); |
+ } |
+ if (end < start) { |
+ throw new ArgumentError( |
+ "End before start: $endIndex < $startIndex (length: $length)"); |
+ } |
+ return _substringUnchecked(start, end); |
+ } |
+ |
String _substringUnchecked(int startIndex, int endIndex) { |
assert(endIndex != null); |
assert((startIndex >= 0) && (startIndex <= this.length)); |
@@ -204,7 +239,7 @@ class _StringBase { |
if (pattern is String) { |
return indexOf(pattern, startIndex) >= 0; |
} |
- return pattern.allMatches(this.substring(startIndex)).iterator().hasNext; |
+ return pattern.allMatches(this.substring(startIndex)).iterator.moveNext(); |
} |
String replaceFirst(Pattern pattern, String replacement) { |
@@ -216,9 +251,9 @@ class _StringBase { |
} |
StringBuffer buffer = new StringBuffer(); |
int startIndex = 0; |
- Iterator iterator = pattern.allMatches(this).iterator(); |
- if (iterator.hasNext) { |
- Match match = iterator.next(); |
+ Iterator iterator = pattern.allMatches(this).iterator; |
+ if (iterator.moveNext()) { |
+ Match match = iterator.current; |
buffer..add(this.substring(startIndex, match.start)) |
..add(replacement); |
startIndex = match.end; |
@@ -231,7 +266,8 @@ class _StringBase { |
throw new ArgumentError("${pattern} is not a Pattern"); |
} |
if (replacement is! String) { |
- throw new ArgumentError("${replacement} is not a String"); |
+ throw new ArgumentError( |
+ "${replacement} is not a String or Match->String function"); |
} |
StringBuffer buffer = new StringBuffer(); |
int startIndex = 0; |
@@ -243,13 +279,75 @@ class _StringBase { |
return (buffer..add(this.substring(startIndex))).toString(); |
} |
+ String replaceAllMapped(Pattern pattern, String replace(Match match)) { |
+ return splitMapJoin(pattern, onMatch: replace); |
+ } |
+ |
+ static String _matchString(Match match) => match[0]; |
+ static String _stringIdentity(String string) => string; |
+ |
+ String _splitMapJoinEmptyString(String onMatch(Match match), |
+ String onNonMatch(String nonMatch)) { |
+ // Pattern is the empty string. |
+ StringBuffer buffer = new StringBuffer(); |
+ int length = this.length; |
+ int i = 0; |
+ buffer.add(onNonMatch("")); |
+ while (i < length) { |
+ buffer.add(onMatch(new _StringMatch(i, this, ""))); |
+ // Special case to avoid splitting a surrogate pair. |
+ int code = this.charCodeAt(i); |
+ if ((code & ~0x3FF) == 0xD800 && length > i + 1) { |
+ // Leading surrogate; |
+ code = this.charCodeAt(i + 1); |
+ if ((code & ~0x3FF) == 0xDC00) { |
+ // Matching trailing surrogate. |
+ buffer.add(onNonMatch(this.substring(i, i + 2))); |
+ i += 2; |
+ continue; |
+ } |
+ } |
+ buffer.add(onNonMatch(this[i])); |
+ i++; |
+ } |
+ buffer.add(onMatch(new _StringMatch(i, this, ""))); |
+ buffer.add(onNonMatch("")); |
+ return buffer.toString(); |
+ } |
+ |
+ String splitMapJoin(Pattern pattern, |
+ {String onMatch(Match match), |
+ String onNonMatch(String nonMatch)}) { |
+ if (pattern is! Pattern) { |
+ throw new ArgumentError("${pattern} is not a Pattern"); |
+ } |
+ if (onMatch == null) onMatch = _matchString; |
+ if (onNonMatch == null) onNonMatch = _stringIdentity; |
+ if (pattern is String) { |
+ String stringPattern = pattern; |
+ if (stringPattern.isEmpty) { |
+ return _splitMapJoinEmptyString(onMatch, onNonMatch); |
+ } |
+ } |
+ StringBuffer buffer = new StringBuffer(); |
+ int startIndex = 0; |
+ for (Match match in pattern.allMatches(this)) { |
+ buffer.add(onNonMatch(this.substring(startIndex, match.start))); |
+ buffer.add(onMatch(match).toString()); |
+ startIndex = match.end; |
+ } |
+ buffer.add(onNonMatch(this.substring(startIndex))); |
+ return buffer.toString(); |
+ } |
+ |
+ |
/** |
* Convert all objects in [values] to strings and concat them |
* into a result string. |
*/ |
static String _interpolate(List values) { |
int numValues = values.length; |
- var stringList = new List(numValues); |
+ var stringList = new List.fixedLength(numValues); |
for (int i = 0; i < numValues; i++) { |
stringList[i] = values[i].toString(); |
} |
@@ -284,8 +382,8 @@ class _StringBase { |
return splitChars(); |
} |
int length = this.length; |
- Iterator iterator = pattern.allMatches(this).iterator(); |
- if (length == 0 && iterator.hasNext) { |
+ Iterator iterator = pattern.allMatches(this).iterator; |
+ if (length == 0 && iterator.moveNext()) { |
// A matched empty string input returns the empty list. |
return <String>[]; |
} |
@@ -293,11 +391,11 @@ class _StringBase { |
int startIndex = 0; |
int previousIndex = 0; |
while (true) { |
- if (startIndex == length || !iterator.hasNext) { |
+ if (startIndex == length || !iterator.moveNext()) { |
result.add(this._substringUnchecked(previousIndex, length)); |
break; |
} |
- Match match = iterator.next(); |
+ Match match = iterator.current; |
if (match.start == length) { |
result.add(this._substringUnchecked(previousIndex, length)); |
break; |
@@ -315,7 +413,7 @@ class _StringBase { |
List<String> splitChars() { |
int len = this.length; |
- final result = new List<String>(len); |
+ final result = new List<String>.fixedLength(len); |
for (int i = 0; i < len; i++) { |
result[i] = this[i]; |
} |
@@ -324,7 +422,7 @@ class _StringBase { |
List<int> get charCodes { |
int len = this.length; |
- final result = new List<int>(len); |
+ final result = new List<int>.fixedLength(len); |
for (int i = 0; i < len; i++) { |
result[i] = this.charCodeAt(i); |
} |
@@ -336,34 +434,38 @@ class _StringBase { |
String toLowerCase() native "String_toLowerCase"; |
// Implementations of Strings methods follow below. |
- static String join(List<String> strings, String separator) { |
- final int length = strings.length; |
- if (length == 0) { |
- return ""; |
- } |
+ static String join(Iterable<String> strings, String separator) { |
+ bool first = true; |
+ List<String> stringsList = <String>[]; |
+ for (String string in strings) { |
+ if (first) { |
+ first = false; |
+ } else { |
+ stringsList.add(separator); |
+ } |
- List stringsList = strings; |
- if (separator.length != 0) { |
- stringsList = new List(2 * length - 1); |
- stringsList[0] = strings[0]; |
- int j = 1; |
- for (int i = 1; i < length; i++) { |
- stringsList[j++] = separator; |
- stringsList[j++] = strings[i]; |
+ if (string is! String) { |
+ throw new ArgumentError(Error.safeToString(string)); |
} |
+ stringsList.add(string); |
} |
return concatAll(stringsList); |
} |
- static String concatAll(List<String> strings) { |
+ static String concatAll(Iterable<String> strings) { |
_ObjectArray stringsArray; |
if (strings is _ObjectArray) { |
stringsArray = strings; |
+ for (int i = 0; i < strings.length; i++) { |
+ if (strings[i] is! String) throw new ArgumentError(strings[i]); |
+ } |
} else { |
int len = strings.length; |
stringsArray = new _ObjectArray(len); |
- for (int i = 0; i < len; i++) { |
- stringsArray[i] = strings[i]; |
+ int i = 0; |
+ for (String string in strings) { |
+ if (string is! String) throw new ArgumentError(string); |
+ stringsArray[i++] = string; |
} |
} |
return _concatAll(stringsArray); |