| 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._js_helper; | 5 part of dart._js_helper; |
| 6 | 6 |
| 7 @notNull |
| 7 int stringIndexOfStringUnchecked(receiver, other, startIndex) { | 8 int stringIndexOfStringUnchecked(receiver, other, startIndex) { |
| 8 return JS('int', '#.indexOf(#, #)', receiver, other, startIndex); | 9 return JS('int', '#.indexOf(#, #)', receiver, other, startIndex); |
| 9 } | 10 } |
| 10 | 11 |
| 12 @notNull |
| 11 String substring1Unchecked(receiver, startIndex) { | 13 String substring1Unchecked(receiver, startIndex) { |
| 12 return JS('String', '#.substring(#)', receiver, startIndex); | 14 return JS('String', '#.substring(#)', receiver, startIndex); |
| 13 } | 15 } |
| 14 | 16 |
| 17 @notNull |
| 15 String substring2Unchecked(receiver, startIndex, endIndex) { | 18 String substring2Unchecked(receiver, startIndex, endIndex) { |
| 16 return JS('String', '#.substring(#, #)', receiver, startIndex, endIndex); | 19 return JS('String', '#.substring(#, #)', receiver, startIndex, endIndex); |
| 17 } | 20 } |
| 18 | 21 |
| 22 @notNull |
| 19 bool stringContainsStringUnchecked(receiver, other, startIndex) { | 23 bool stringContainsStringUnchecked(receiver, other, startIndex) { |
| 20 return stringIndexOfStringUnchecked(receiver, other, startIndex) >= 0; | 24 return stringIndexOfStringUnchecked(receiver, other, startIndex) >= 0; |
| 21 } | 25 } |
| 22 | 26 |
| 23 class StringMatch implements Match { | 27 class StringMatch implements Match { |
| 24 const StringMatch(int this.start, String this.input, String this.pattern); | 28 const StringMatch(int this.start, String this.input, String this.pattern); |
| 25 | 29 |
| 26 int get end => start + pattern.length; | 30 int get end => start + pattern.length; |
| 27 String operator [](int g) => group(g); | 31 String operator [](int g) => group(g); |
| 28 int get groupCount => 0; | 32 int get groupCount => 0; |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 94 _current = new StringMatch(index, _input, _pattern); | 98 _current = new StringMatch(index, _input, _pattern); |
| 95 // Empty match, don't start at same location again. | 99 // Empty match, don't start at same location again. |
| 96 if (end == _index) end++; | 100 if (end == _index) end++; |
| 97 _index = end; | 101 _index = end; |
| 98 return true; | 102 return true; |
| 99 } | 103 } |
| 100 | 104 |
| 101 Match get current => _current; | 105 Match get current => _current; |
| 102 } | 106 } |
| 103 | 107 |
| 104 bool stringContainsUnchecked(String receiver, other, int startIndex) { | 108 @notNull |
| 109 bool stringContainsUnchecked( |
| 110 @notNull String receiver, @notNull other, int startIndex) { |
| 105 if (other is String) { | 111 if (other is String) { |
| 106 return stringContainsStringUnchecked(receiver, other, startIndex); | 112 return stringContainsStringUnchecked(receiver, other, startIndex); |
| 107 } else if (other is JSSyntaxRegExp) { | 113 } else if (other is JSSyntaxRegExp) { |
| 108 return other.hasMatch(receiver.substring(startIndex)); | 114 return other.hasMatch(receiver.substring(startIndex)); |
| 109 } else { | 115 } else { |
| 110 var substr = receiver.substring(startIndex); | 116 var substr = receiver.substring(startIndex); |
| 111 return other.allMatches(substr).isNotEmpty; | 117 return other.allMatches(substr).isNotEmpty; |
| 112 } | 118 } |
| 113 } | 119 } |
| 114 | 120 |
| 121 @notNull |
| 115 String stringReplaceJS(receiver, replacer, replacement) { | 122 String stringReplaceJS(receiver, replacer, replacement) { |
| 116 // The JavaScript String.replace method recognizes replacement | 123 // The JavaScript String.replace method recognizes replacement |
| 117 // patterns in the replacement string. Dart does not have that | 124 // patterns in the replacement string. Dart does not have that |
| 118 // behavior. | 125 // behavior. |
| 119 replacement = JS('String', r'#.replace(/\$/g, "$$$$")', replacement); | 126 replacement = JS('String', r'#.replace(/\$/g, "$$$$")', replacement); |
| 120 return JS('String', r'#.replace(#, #)', receiver, replacer, replacement); | 127 return JS('String', r'#.replace(#, #)', receiver, replacer, replacement); |
| 121 } | 128 } |
| 122 | 129 |
| 123 String stringReplaceFirstRE(String receiver, JSSyntaxRegExp regexp, | 130 @notNull |
| 131 String stringReplaceFirstRE(@notNull String receiver, JSSyntaxRegExp regexp, |
| 124 String replacement, int startIndex) { | 132 String replacement, int startIndex) { |
| 125 var match = regexp._execGlobal(receiver, startIndex); | 133 var match = regexp._execGlobal(receiver, startIndex); |
| 126 if (match == null) return receiver; | 134 if (match == null) return receiver; |
| 127 var start = match.start; | 135 var start = match.start; |
| 128 var end = match.end; | 136 var end = match.end; |
| 129 return stringReplaceRangeUnchecked(receiver, start, end, replacement); | 137 return stringReplaceRangeUnchecked(receiver, start, end, replacement); |
| 130 } | 138 } |
| 131 | 139 |
| 132 /// Returns a string for a RegExp pattern that matches [string]. This is done by | 140 /// Returns a string for a RegExp pattern that matches [string]. This is done by |
| 133 /// escaping all RegExp metacharacters. | 141 /// escaping all RegExp metacharacters. |
| 142 @notNull |
| 134 String quoteStringForRegExp(string) { | 143 String quoteStringForRegExp(string) { |
| 135 return JS('String', r'#.replace(/[[\]{}()*+?.\\^$|]/g, "\\$&")', string); | 144 return JS('String', r'#.replace(/[[\]{}()*+?.\\^$|]/g, "\\$&")', string); |
| 136 } | 145 } |
| 137 | 146 |
| 138 String stringReplaceAllUnchecked( | 147 @notNull |
| 139 String receiver, Pattern pattern, String replacement) { | 148 String stringReplaceAllUnchecked(@notNull String receiver, |
| 140 checkString(replacement); | 149 @nullCheck Pattern pattern, @nullCheck String replacement) { |
| 141 if (pattern is String) { | 150 if (pattern is String) { |
| 142 if (pattern == "") { | 151 if (pattern == "") { |
| 143 if (receiver == "") { | 152 if (receiver == "") { |
| 144 return replacement; | 153 return replacement; |
| 145 } else { | 154 } else { |
| 146 StringBuffer result = new StringBuffer(); | 155 StringBuffer result = new StringBuffer(); |
| 147 int length = receiver.length; | 156 int length = receiver.length; |
| 148 result.write(replacement); | 157 result.write(replacement); |
| 149 for (int i = 0; i < length; i++) { | 158 for (int i = 0; i < length; i++) { |
| 150 result.write(receiver[i]); | 159 result.write(receiver[i]); |
| 151 result.write(replacement); | 160 result.write(replacement); |
| 152 } | 161 } |
| 153 return result.toString(); | 162 return result.toString(); |
| 154 } | 163 } |
| 155 } else { | 164 } else { |
| 156 var quoted = quoteStringForRegExp(pattern); | 165 var quoted = quoteStringForRegExp(pattern); |
| 157 var replacer = JS('', "new RegExp(#, 'g')", quoted); | 166 var replacer = JS('', "new RegExp(#, 'g')", quoted); |
| 158 return stringReplaceJS(receiver, replacer, replacement); | 167 return stringReplaceJS(receiver, replacer, replacement); |
| 159 } | 168 } |
| 160 } else if (pattern is JSSyntaxRegExp) { | 169 } else if (pattern is JSSyntaxRegExp) { |
| 161 var re = regExpGetGlobalNative(pattern); | 170 var re = regExpGetGlobalNative(pattern); |
| 162 return stringReplaceJS(receiver, re, replacement); | 171 return stringReplaceJS(receiver, re, replacement); |
| 163 } else { | 172 } else { |
| 164 checkNull(pattern); | |
| 165 // TODO(floitsch): implement generic String.replace (with patterns). | 173 // TODO(floitsch): implement generic String.replace (with patterns). |
| 166 throw "String.replaceAll(Pattern) UNIMPLEMENTED"; | 174 throw "String.replaceAll(Pattern) UNIMPLEMENTED"; |
| 167 } | 175 } |
| 168 } | 176 } |
| 169 | 177 |
| 170 String _matchString(Match match) => match[0]; | 178 String _matchString(Match match) => match[0]; |
| 171 String _stringIdentity(String string) => string; | 179 String _stringIdentity(String string) => string; |
| 172 | 180 |
| 173 String stringReplaceAllFuncUnchecked(String receiver, Pattern pattern, | 181 @notNull |
| 174 String onMatch(Match match), String onNonMatch(String nonMatch)) { | 182 String stringReplaceAllFuncUnchecked( |
| 183 String receiver, |
| 184 @nullCheck Pattern pattern, |
| 185 String onMatch(Match match), |
| 186 String onNonMatch(String nonMatch)) { |
| 175 if (onMatch == null) onMatch = _matchString; | 187 if (onMatch == null) onMatch = _matchString; |
| 176 if (onNonMatch == null) onNonMatch = _stringIdentity; | 188 if (onNonMatch == null) onNonMatch = _stringIdentity; |
| 177 if (pattern is String) { | 189 if (pattern is String) { |
| 178 return stringReplaceAllStringFuncUnchecked( | 190 return stringReplaceAllStringFuncUnchecked( |
| 179 receiver, pattern, onMatch, onNonMatch); | 191 receiver, pattern, onMatch, onNonMatch); |
| 180 } | 192 } |
| 181 // Placing the Pattern test here is indistinguishable from placing it at the | |
| 182 // top of the method but it saves an extra check on the `pattern is String` | |
| 183 // path. | |
| 184 if (pattern is! Pattern) { | |
| 185 throw new ArgumentError.value(pattern, 'pattern', 'is not a Pattern'); | |
| 186 } | |
| 187 StringBuffer buffer = new StringBuffer(); | 193 StringBuffer buffer = new StringBuffer(); |
| 188 int startIndex = 0; | 194 int startIndex = 0; |
| 189 for (Match match in pattern.allMatches(receiver)) { | 195 for (Match match in pattern.allMatches(receiver)) { |
| 190 buffer.write(onNonMatch(receiver.substring(startIndex, match.start))); | 196 buffer.write(onNonMatch(receiver.substring(startIndex, match.start))); |
| 191 buffer.write(onMatch(match)); | 197 buffer.write(onMatch(match)); |
| 192 startIndex = match.end; | 198 startIndex = match.end; |
| 193 } | 199 } |
| 194 buffer.write(onNonMatch(receiver.substring(startIndex))); | 200 buffer.write(onNonMatch(receiver.substring(startIndex))); |
| 195 return buffer.toString(); | 201 return buffer.toString(); |
| 196 } | 202 } |
| 197 | 203 |
| 204 @notNull |
| 198 String stringReplaceAllEmptyFuncUnchecked(String receiver, | 205 String stringReplaceAllEmptyFuncUnchecked(String receiver, |
| 199 String onMatch(Match match), String onNonMatch(String nonMatch)) { | 206 String onMatch(Match match), String onNonMatch(String nonMatch)) { |
| 200 // Pattern is the empty string. | 207 // Pattern is the empty string. |
| 201 StringBuffer buffer = new StringBuffer(); | 208 StringBuffer buffer = new StringBuffer(); |
| 202 int length = receiver.length; | 209 int length = receiver.length; |
| 203 int i = 0; | 210 int i = 0; |
| 204 buffer.write(onNonMatch("")); | 211 buffer.write(onNonMatch("")); |
| 205 while (i < length) { | 212 while (i < length) { |
| 206 buffer.write(onMatch(new StringMatch(i, receiver, ""))); | 213 buffer.write(onMatch(new StringMatch(i, receiver, ""))); |
| 207 // Special case to avoid splitting a surrogate pair. | 214 // Special case to avoid splitting a surrogate pair. |
| 208 int code = receiver.codeUnitAt(i); | 215 int code = receiver.codeUnitAt(i); |
| 209 if ((code & ~0x3FF) == 0xD800 && length > i + 1) { | 216 if ((code & ~0x3FF) == 0xD800 && length > i + 1) { |
| 210 // Leading surrogate; | 217 // Leading surrogate; |
| 211 code = receiver.codeUnitAt(i + 1); | 218 code = receiver.codeUnitAt(i + 1); |
| 212 if ((code & ~0x3FF) == 0xDC00) { | 219 if ((code & ~0x3FF) == 0xDC00) { |
| 213 // Matching trailing surrogate. | 220 // Matching trailing surrogate. |
| 214 buffer.write(onNonMatch(receiver.substring(i, i + 2))); | 221 buffer.write(onNonMatch(receiver.substring(i, i + 2))); |
| 215 i += 2; | 222 i += 2; |
| 216 continue; | 223 continue; |
| 217 } | 224 } |
| 218 } | 225 } |
| 219 buffer.write(onNonMatch(receiver[i])); | 226 buffer.write(onNonMatch(receiver[i])); |
| 220 i++; | 227 i++; |
| 221 } | 228 } |
| 222 buffer.write(onMatch(new StringMatch(i, receiver, ""))); | 229 buffer.write(onMatch(new StringMatch(i, receiver, ""))); |
| 223 buffer.write(onNonMatch("")); | 230 buffer.write(onNonMatch("")); |
| 224 return buffer.toString(); | 231 return buffer.toString(); |
| 225 } | 232 } |
| 226 | 233 |
| 234 @notNull |
| 227 String stringReplaceAllStringFuncUnchecked(String receiver, String pattern, | 235 String stringReplaceAllStringFuncUnchecked(String receiver, String pattern, |
| 228 String onMatch(Match match), String onNonMatch(String nonMatch)) { | 236 String onMatch(Match match), String onNonMatch(String nonMatch)) { |
| 229 int patternLength = pattern.length; | 237 int patternLength = pattern.length; |
| 230 if (patternLength == 0) { | 238 if (patternLength == 0) { |
| 231 return stringReplaceAllEmptyFuncUnchecked(receiver, onMatch, onNonMatch); | 239 return stringReplaceAllEmptyFuncUnchecked(receiver, onMatch, onNonMatch); |
| 232 } | 240 } |
| 233 int length = receiver.length; | 241 int length = receiver.length; |
| 234 StringBuffer buffer = new StringBuffer(); | 242 StringBuffer buffer = new StringBuffer(); |
| 235 int startIndex = 0; | 243 int startIndex = 0; |
| 236 while (startIndex < length) { | 244 while (startIndex < length) { |
| 237 int position = stringIndexOfStringUnchecked(receiver, pattern, startIndex); | 245 int position = stringIndexOfStringUnchecked(receiver, pattern, startIndex); |
| 238 if (position == -1) { | 246 if (position == -1) { |
| 239 break; | 247 break; |
| 240 } | 248 } |
| 241 buffer.write(onNonMatch(receiver.substring(startIndex, position))); | 249 buffer.write(onNonMatch(receiver.substring(startIndex, position))); |
| 242 buffer.write(onMatch(new StringMatch(position, receiver, pattern))); | 250 buffer.write(onMatch(new StringMatch(position, receiver, pattern))); |
| 243 startIndex = position + patternLength; | 251 startIndex = position + patternLength; |
| 244 } | 252 } |
| 245 buffer.write(onNonMatch(receiver.substring(startIndex))); | 253 buffer.write(onNonMatch(receiver.substring(startIndex))); |
| 246 return buffer.toString(); | 254 return buffer.toString(); |
| 247 } | 255 } |
| 248 | 256 |
| 249 String stringReplaceFirstUnchecked( | 257 @notNull |
| 250 String receiver, Pattern pattern, String replacement, int startIndex) { | 258 String stringReplaceFirstUnchecked(@notNull String receiver, |
| 259 @nullCheck Pattern pattern, String replacement, int startIndex) { |
| 251 if (pattern is String) { | 260 if (pattern is String) { |
| 252 int index = stringIndexOfStringUnchecked(receiver, pattern, startIndex); | 261 int index = stringIndexOfStringUnchecked(receiver, pattern, startIndex); |
| 253 if (index < 0) return receiver; | 262 if (index < 0) return receiver; |
| 254 int end = index + pattern.length; | 263 int end = index + pattern.length; |
| 255 return stringReplaceRangeUnchecked(receiver, index, end, replacement); | 264 return stringReplaceRangeUnchecked(receiver, index, end, replacement); |
| 256 } | 265 } |
| 257 if (pattern is JSSyntaxRegExp) { | 266 if (pattern is JSSyntaxRegExp) { |
| 258 return startIndex == 0 | 267 return startIndex == 0 |
| 259 ? stringReplaceJS(receiver, regExpGetNative(pattern), replacement) | 268 ? stringReplaceJS(receiver, regExpGetNative(pattern), replacement) |
| 260 : stringReplaceFirstRE(receiver, pattern, replacement, startIndex); | 269 : stringReplaceFirstRE(receiver, pattern, replacement, startIndex); |
| 261 } | 270 } |
| 262 checkNull(pattern); | |
| 263 Iterator<Match> matches = pattern.allMatches(receiver, startIndex).iterator; | 271 Iterator<Match> matches = pattern.allMatches(receiver, startIndex).iterator; |
| 264 if (!matches.moveNext()) return receiver; | 272 if (!matches.moveNext()) return receiver; |
| 265 Match match = matches.current; | 273 Match match = matches.current; |
| 266 return receiver.replaceRange(match.start, match.end, replacement); | 274 return receiver.replaceRange(match.start, match.end, replacement); |
| 267 } | 275 } |
| 268 | 276 |
| 277 @notNull |
| 269 String stringReplaceFirstMappedUnchecked(String receiver, Pattern pattern, | 278 String stringReplaceFirstMappedUnchecked(String receiver, Pattern pattern, |
| 270 String replace(Match current), int startIndex) { | 279 String replace(Match current), int startIndex) { |
| 271 Iterator<Match> matches = pattern.allMatches(receiver, startIndex).iterator; | 280 Iterator<Match> matches = pattern.allMatches(receiver, startIndex).iterator; |
| 272 if (!matches.moveNext()) return receiver; | 281 if (!matches.moveNext()) return receiver; |
| 273 Match match = matches.current; | 282 Match match = matches.current; |
| 274 String replacement = "${replace(match)}"; | 283 String replacement = "${replace(match)}"; |
| 275 return receiver.replaceRange(match.start, match.end, replacement); | 284 return receiver.replaceRange(match.start, match.end, replacement); |
| 276 } | 285 } |
| 277 | 286 |
| 287 @notNull |
| 278 String stringJoinUnchecked(array, separator) { | 288 String stringJoinUnchecked(array, separator) { |
| 279 return JS('String', r'#.join(#)', array, separator); | 289 return JS('String', r'#.join(#)', array, separator); |
| 280 } | 290 } |
| 281 | 291 |
| 292 @notNull |
| 282 String stringReplaceRangeUnchecked( | 293 String stringReplaceRangeUnchecked( |
| 283 String receiver, int start, int end, String replacement) { | 294 String receiver, int start, int end, String replacement) { |
| 284 var prefix = JS('String', '#.substring(0, #)', receiver, start); | 295 var prefix = JS('String', '#.substring(0, #)', receiver, start); |
| 285 var suffix = JS('String', '#.substring(#)', receiver, end); | 296 var suffix = JS('String', '#.substring(#)', receiver, end); |
| 286 return "$prefix$replacement$suffix"; | 297 return "$prefix$replacement$suffix"; |
| 287 } | 298 } |
| OLD | NEW |