Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(78)

Side by Side Diff: runtime/lib/string.dart

Issue 9689089: Improve the replaceFirst, replaceAll and split methods. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: address review comments Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2011, 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 /** 5 /**
6 * [StringBase] contains common methods used by concrete String implementations, 6 * [StringBase] contains common methods used by concrete String implementations,
7 * e.g., OneByteString. 7 * e.g., OneByteString.
8 */ 8 */
9 class StringBase { 9 class StringBase {
10 10
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after
184 } 184 }
185 if ((first == 0) && (last == (len - 1))) { 185 if ((first == 0) && (last == (len - 1))) {
186 // Returns this string if it does not have leading or trailing 186 // Returns this string if it does not have leading or trailing
187 // whitespaces. 187 // whitespaces.
188 return this; 188 return this;
189 } else { 189 } else {
190 return substringUnchecked_(first, last + 1); 190 return substringUnchecked_(first, last + 1);
191 } 191 }
192 } 192 }
193 193
194 bool contains(Pattern other, [int startIndex = 0]) { 194 bool contains(Pattern pattern, [int startIndex = 0]) {
195 if (other is String) { 195 if (pattern is String) {
196 return indexOf(other, startIndex) >= 0; 196 return indexOf(pattern, startIndex) >= 0;
197 } 197 }
198 return other.allMatches(this.substring(startIndex)).iterator().hasNext(); 198 return pattern.allMatches(this.substring(startIndex)).iterator().hasNext();
199 } 199 }
200 200
201 String replaceFirst(Pattern pattern, String to) { 201 String replaceFirst(Pattern pattern, String replacement) {
202 if (pattern is RegExp) { 202 if (pattern is! Pattern) {
203 StringBuffer buffer = new StringBuffer(); 203 throw new IllegalArgumentException("${pattern} is not a Pattern");
204 int startIndex = 0;
205 Match match = pattern.firstMatch(this);
206 if (match != null) {
207 buffer.add(this.substring(startIndex, match.start())).add(to);
208 startIndex = match.end();
209 }
210 return buffer.add(this.substring(startIndex)).toString();
211 } 204 }
212 int pos = this.indexOf(pattern, 0); 205 if (replacement is! String) {
213 if (pos < 0) { 206 throw new IllegalArgumentException("${replacement} is not a String");
214 return this;
215 } 207 }
216 String s1 = this.substring(0, pos); 208 StringBuffer buffer = new StringBuffer();
217 String s2 = this.substring(pos + pattern.length, this.length); 209 int startIndex = 0;
218 return s1.concat(to.concat(s2)); 210 Iterator iterator = pattern.allMatches(this).iterator();
211 if (iterator.hasNext()) {
212 Match match = iterator.next();
213 buffer.add(this.substring(startIndex, match.start())).add(replacement);
214 startIndex = match.end();
215 }
216 return buffer.add(this.substring(startIndex)).toString();
219 } 217 }
220 218
221 String replaceAll(Pattern pattern, String to) { 219 String replaceAll(Pattern pattern, String replacement) {
222 if (pattern is RegExp) { 220 if (pattern is! Pattern) {
223 StringBuffer buffer = new StringBuffer(); 221 throw new IllegalArgumentException("${pattern} is not a Pattern");
224 int startIndex = 0;
225 for (Match match in pattern.allMatches(this)) {
226 buffer.add(this.substring(startIndex, match.start())).add(to);
227 startIndex = match.end();
228 }
229 return buffer.add(this.substring(startIndex)).toString();
230 } 222 }
231 String from = pattern; 223 if (replacement is! String) {
232 int fromLength = from.length; 224 throw new IllegalArgumentException("${replacement} is not a String");
233 int toLength = to.length;
234 int thisLength = this.length;
235
236 StringBuffer result = new StringBuffer("");
237 // Special case the empty string replacement where [to] is
238 // inserted in between each character.
239 if (fromLength === 0) {
240 result.add(to);
241 for (int i = 0; i < thisLength; i++) {
242 result.add(this.substring(i, i + 1));
243 result.add(to);
244 }
245 return result.toString();
246 } 225 }
247 226 StringBuffer buffer = new StringBuffer();
248 int index = indexOf(from, 0); 227 int startIndex = 0;
249 if (index < 0) { 228 for (Match match in pattern.allMatches(this)) {
250 return this; 229 buffer.add(this.substring(startIndex, match.start())).add(replacement);
230 startIndex = match.end();
251 } 231 }
252 int startIndex = 0; 232 return buffer.add(this.substring(startIndex)).toString();
253 do {
254 result.add(this.substring(startIndex, index));
255 result.add(to);
256 startIndex = index + fromLength;
257 } while ((index = indexOf(from, startIndex)) >= 0);
258
259 // If there are remaining code points, add them to the string
260 // buffer.
261 if (startIndex < thisLength) {
262 result.add(this.substring(startIndex, thisLength));
263 }
264
265 return result.toString();
266 } 233 }
267 234
268 /** 235 /**
269 * Convert argument obj to string and concat it with this string. 236 * Convert argument obj to string and concat it with this string.
270 * Returns concatenated string. 237 * Returns concatenated string.
271 */ 238 */
272 String operator +(Object obj) { 239 String operator +(Object obj) {
273 return this.concat(obj.toString()); 240 return this.concat(obj.toString());
274 } 241 }
275 242
(...skipping 17 matching lines...) Expand all
293 int strLength = str.length; 260 int strLength = str.length;
294 for (int k = 0; k < strLength; k++) { 261 for (int k = 0; k < strLength; k++) {
295 codepoints[intArrayIx++] = str.charCodeAt(k); 262 codepoints[intArrayIx++] = str.charCodeAt(k);
296 } 263 }
297 } 264 }
298 return StringBase.createFromCharCodes(codepoints); 265 return StringBase.createFromCharCodes(codepoints);
299 } 266 }
300 267
301 Iterable<Match> allMatches(String str) { 268 Iterable<Match> allMatches(String str) {
302 List<Match> result = new List<Match>(); 269 List<Match> result = new List<Match>();
303 if (this.isEmpty()) return result; 270 int length = str.length;
304 int length = this.length; 271 int patternLength = this.length;
305 272 int startIndex = 0;
306 int ix = 0; 273 while (true) {
307 while (ix < str.length) { 274 int position = str.indexOf(this, startIndex);
308 int foundIx = str.indexOf(this, ix); 275 if (position == -1) {
309 if (foundIx < 0) break; 276 break;
310 result.add(new _StringMatch(foundIx, str, this)); 277 }
311 ix = foundIx + length; 278 result.add(new _StringMatch(position, str, this));
279 int endIndex = position + patternLength;
280 if (endIndex == length) {
281 break;
282 } else if (position == endIndex) {
283 ++startIndex; // empty match, advance and restart
284 } else {
285 startIndex = endIndex;
286 }
312 } 287 }
313 return result; 288 return result;
314 } 289 }
315 290
316 List<String> split(Pattern pattern) { 291 List<String> split(Pattern pattern) {
292 int length = this.length;
293 Iterator iterator = pattern.allMatches(this).iterator();
294 if (length == 0 && iterator.hasNext()) {
295 // A matched empty string input returns the empty list.
296 return <String>[];
297 }
317 List<String> result = new List<String>(); 298 List<String> result = new List<String>();
318 if (pattern is RegExp) { 299 int startIndex = 0;
319 int startIndex = 0; 300 int previousIndex = 0;
320 for (Match match in pattern.allMatches(this)) { 301 while (true) {
321 result.add(this.substring(startIndex, match.start())); 302 if (startIndex == length || !iterator.hasNext()) {
322 startIndex = match.end(); 303 result.add(this.substring(previousIndex, length));
323 }
324 result.add(this.substring(startIndex));
325 return result;
326 }
327 if (pattern.isEmpty()) {
328 for (int i = 0; i < this.length; i++) {
329 result.add(this.substring(i, i+1));
330 }
331 return result;
332 }
333 int ix = 0;
334 while (ix < this.length) {
335 int foundIx = this.indexOf(pattern, ix);
336 if (foundIx < 0) {
337 // Not found, add remaining.
338 result.add(this.substring(ix, this.length));
339 break; 304 break;
340 } 305 }
341 result.add(this.substring(ix, foundIx)); 306 Match match = iterator.next();
342 ix = foundIx + pattern.length; 307 if (match.start() == length) {
343 } 308 result.add(this.substring(previousIndex, length));
344 if (ix == this.length) { 309 break;
345 result.add(""); 310 }
311 int endIndex = match.end();
312 if (startIndex == endIndex && endIndex == previousIndex) {
313 ++startIndex; // empty match, advance and restart
314 continue;
315 }
316 result.add(this.substring(previousIndex, match.start()));
317 startIndex = previousIndex = endIndex;
346 } 318 }
347 return result; 319 return result;
348 } 320 }
349 321
350 List<String> splitChars() { 322 List<String> splitChars() {
351 int len = this.length; 323 int len = this.length;
352 final result = new List<String>(len); 324 final result = new List<String>(len);
353 for (int i = 0; i < len; i++) { 325 for (int i = 0; i < len; i++) {
354 result[i] = this[i]; 326 result[i] = this[i];
355 } 327 }
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
424 } 396 }
425 397
426 } 398 }
427 399
428 400
429 class TwoByteString extends StringBase implements String { 401 class TwoByteString extends StringBase implements String {
430 factory TwoByteString._uninstantiable() { 402 factory TwoByteString._uninstantiable() {
431 throw const UnsupportedOperationException( 403 throw const UnsupportedOperationException(
432 "TwoByteString can only be allocated by the VM"); 404 "TwoByteString can only be allocated by the VM");
433 } 405 }
434 406
435 // Checks for one-byte whitespaces only. 407 // Checks for one-byte whitespaces only.
436 // TODO(srdjan): Investigate if 0x85 (NEL) and 0xA0 (NBSP) are valid 408 // TODO(srdjan): Investigate if 0x85 (NEL) and 0xA0 (NBSP) are valid
437 // whitespaces. Add checking for multi-byte whitespace codepoints. 409 // whitespaces. Add checking for multi-byte whitespace codepoints.
438 bool _isWhitespace(int codePoint) { 410 bool _isWhitespace(int codePoint) {
439 return 411 return
440 (codePoint === 32) || // Space. 412 (codePoint === 32) || // Space.
441 ((9 <= codePoint) && (codePoint <= 13)); // CR, LF, TAB, etc. 413 ((9 <= codePoint) && (codePoint <= 13)); // CR, LF, TAB, etc.
442 } 414 }
443 } 415 }
444 416
445 417
446 class FourByteString extends StringBase implements String { 418 class FourByteString extends StringBase implements String {
447 factory FourByteString._uninstantiable() { 419 factory FourByteString._uninstantiable() {
448 throw const UnsupportedOperationException( 420 throw const UnsupportedOperationException(
449 "FourByteString can only be allocated by the VM"); 421 "FourByteString can only be allocated by the VM");
450 } 422 }
451 423
452 // Checks for one-byte whitespaces only. 424 // Checks for one-byte whitespaces only.
453 // TODO(srdjan): Investigate if 0x85 (NEL) and 0xA0 (NBSP) are valid 425 // TODO(srdjan): Investigate if 0x85 (NEL) and 0xA0 (NBSP) are valid
454 // whitespaces. Add checking for multi-byte whitespace codepoints. 426 // whitespaces. Add checking for multi-byte whitespace codepoints.
455 bool _isWhitespace(int codePoint) { 427 bool _isWhitespace(int codePoint) {
456 return 428 return
457 (codePoint === 32) || // Space. 429 (codePoint === 32) || // Space.
458 ((9 <= codePoint) && (codePoint <= 13)); // CR, LF, TAB, etc. 430 ((9 <= codePoint) && (codePoint <= 13)); // CR, LF, TAB, etc.
459 } 431 }
460 } 432 }
461 433
462 434
463 class ExternalOneByteString extends StringBase implements String { 435 class ExternalOneByteString extends StringBase implements String {
464 factory ExternalOneByteString._uninstantiable() { 436 factory ExternalOneByteString._uninstantiable() {
465 throw const UnsupportedOperationException( 437 throw const UnsupportedOperationException(
466 "ExternalOneByteString can only be allocated by the VM"); 438 "ExternalOneByteString can only be allocated by the VM");
467 } 439 }
468 440
469 // Checks for one-byte whitespaces only. 441 // Checks for one-byte whitespaces only.
470 // TODO(srdjan): Investigate if 0x85 (NEL) and 0xA0 (NBSP) are valid 442 // TODO(srdjan): Investigate if 0x85 (NEL) and 0xA0 (NBSP) are valid
471 // whitespaces for one byte strings. 443 // whitespaces for one byte strings.
472 bool _isWhitespace(int codePoint) { 444 bool _isWhitespace(int codePoint) {
473 return 445 return
474 (codePoint === 32) || // Space. 446 (codePoint === 32) || // Space.
475 ((9 <= codePoint) && (codePoint <= 13)); // CR, LF, TAB, etc. 447 ((9 <= codePoint) && (codePoint <= 13)); // CR, LF, TAB, etc.
476 } 448 }
477 } 449 }
478 450
479 451
480 class ExternalTwoByteString extends StringBase implements String { 452 class ExternalTwoByteString extends StringBase implements String {
481 factory ExternalTwoByteString._uninstantiable() { 453 factory ExternalTwoByteString._uninstantiable() {
482 throw const UnsupportedOperationException( 454 throw const UnsupportedOperationException(
483 "ExternalTwoByteString can only be allocated by the VM"); 455 "ExternalTwoByteString can only be allocated by the VM");
484 } 456 }
485 457
486 // Checks for one-byte whitespaces only. 458 // Checks for one-byte whitespaces only.
487 // TODO(srdjan): Investigate if 0x85 (NEL) and 0xA0 (NBSP) are valid 459 // TODO(srdjan): Investigate if 0x85 (NEL) and 0xA0 (NBSP) are valid
488 // whitespaces. Add checking for multi-byte whitespace codepoints. 460 // whitespaces. Add checking for multi-byte whitespace codepoints.
489 bool _isWhitespace(int codePoint) { 461 bool _isWhitespace(int codePoint) {
490 return 462 return
491 (codePoint === 32) || // Space. 463 (codePoint === 32) || // Space.
492 ((9 <= codePoint) && (codePoint <= 13)); // CR, LF, TAB, etc. 464 ((9 <= codePoint) && (codePoint <= 13)); // CR, LF, TAB, etc.
493 } 465 }
494 } 466 }
495 467
496 468
497 class ExternalFourByteString extends StringBase implements String { 469 class ExternalFourByteString extends StringBase implements String {
498 factory ExternalFourByteString._uninstantiable() { 470 factory ExternalFourByteString._uninstantiable() {
499 throw const UnsupportedOperationException( 471 throw const UnsupportedOperationException(
500 "ExternalFourByteString can only be allocated by the VM"); 472 "ExternalFourByteString can only be allocated by the VM");
501 } 473 }
502 474
503 // Checks for one-byte whitespaces only. 475 // Checks for one-byte whitespaces only.
504 // TODO(srdjan): Investigate if 0x85 (NEL) and 0xA0 (NBSP) are valid 476 // TODO(srdjan): Investigate if 0x85 (NEL) and 0xA0 (NBSP) are valid
505 // whitespaces. Add checking for multi-byte whitespace codepoints. 477 // whitespaces. Add checking for multi-byte whitespace codepoints.
506 bool _isWhitespace(int codePoint) { 478 bool _isWhitespace(int codePoint) {
507 return 479 return
508 (codePoint === 32) || // Space. 480 (codePoint === 32) || // Space.
509 ((9 <= codePoint) && (codePoint <= 13)); // CR, LF, TAB, etc. 481 ((9 <= codePoint) && (codePoint <= 13)); // CR, LF, TAB, etc.
510 } 482 }
511 } 483 }
512 484
(...skipping 20 matching lines...) Expand all
533 for (int g in groups) { 505 for (int g in groups) {
534 result.add(group(g)); 506 result.add(group(g));
535 } 507 }
536 return result; 508 return result;
537 } 509 }
538 510
539 final int _start; 511 final int _start;
540 final String str; 512 final String str;
541 final String pattern; 513 final String pattern;
542 } 514 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698