| 433 | */ |
| 434 | // This is visible for testing. |
| 435 | String replaceAllFunc(String src, ReplaceFunc repl, int maxReplaces) { |
| 436 | int lastMatchEnd = 0; // end position of the most recent match |
| 437 | int searchPos = 0; // position where we next look for a match |
| 438 | StringBuilder buf = new StringBuilder(); |
| 439 | MachineInput input = MachineInput.fromUTF16(src); |
| 440 | int numReplaces = 0; |
| 441 | while (searchPos <= src.length()) { |
| 442 | int[] a = doExecute(input, searchPos, UNANCHORED, 2); |
| 443 | if (a == null || a.length == 0) { |
| 444 | break; // no more matches |
| 445 | } |
| 446 | |
| 447 | // Copy the unmatched characters before this match. |
| 448 | buf.append(src.substring(lastMatchEnd, a[0])); |
| 449 | |
| 450 | // Now insert a copy of the replacement string, but not for a |
| 451 | // match of the empty string immediately after another match. |
| 452 | // (Otherwise, we get double replacement for patterns that |
| 453 | // match both empty and nonempty strings.) |
| 454 | // FIXME(adonovan), FIXME(afrozm) - JDK seems to be doing exactly this |
| 455 | // put a replacement for a pattern that also matches empty and non-empty |
| 456 | // strings. The fix would not just be a[1] >= lastMatchEnd, there are a |
| 457 | // few corner cases in that as well, and there are tests which will fail |
| 458 | // when that case is touched (happens only at the end of the input string |
| 459 | // though). |
| 460 | if (a[1] > lastMatchEnd || a[0] == 0) { |
| 461 | buf.append(repl.replace(src.substring(a[0], a[1]))); |
| 462 | // Increment the replace count. |
| 463 | ++numReplaces; |
| 464 | } |
| 465 | lastMatchEnd = a[1]; |
| 466 | |
| 467 | // Advance past this match; always advance at least one character. |
| 468 | int width = input.step(searchPos) & 0x7; |
| 469 | if (searchPos + width > a[1]) { |
| 470 | searchPos += width; |
| 471 | } else if (searchPos + 1 > a[1]) { |
| 472 | // This clause is only needed at the end of the input |
| 473 | // string. In that case, DecodeRuneInString returns width=0. |
| 474 | searchPos++; |
| 475 | } else { |
| 476 | searchPos = a[1]; |
| 477 | } |
| 478 | if (numReplaces >= maxReplaces) { |
| 479 | // Should never be greater though. |
| 480 | break; |
| 481 | } |
| 482 | } |
| 483 | |
| 484 | // Copy the unmatched characters after the last match. |
| 485 | buf.append(src.substring(lastMatchEnd)); |
| 486 | |
| 487 | return buf.toString(); |
| 488 | } |
| 489 | |
| 490 | /** |
| 491 | * Returns a string that quotes all regular expression metacharacters inside the argument text; |