1. /*
  2. * @(#)Matcher.java 1.46 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.util.regex;
  8. /**
  9. * An engine that performs match operations on a {@link java.lang.CharSequence
  10. * </code>character sequence<code>} by interpreting a {@link Pattern}.
  11. *
  12. * <p> A matcher is created from a pattern by invoking the pattern's {@link
  13. * Pattern#matcher matcher} method. Once created, a matcher can be used to
  14. * perform three different kinds of match operations:
  15. *
  16. * <ul>
  17. *
  18. * <li><p> The {@link #matches matches} method attempts to match the entire
  19. * input sequence against the pattern. </p></li>
  20. *
  21. * <li><p> The {@link #lookingAt lookingAt} method attempts to match the
  22. * input sequence, starting at the beginning, against the pattern. </p></li>
  23. *
  24. * <li><p> The {@link #find find} method scans the input sequence looking for
  25. * the next subsequence that matches the pattern. </p></li>
  26. *
  27. * </ul>
  28. *
  29. * <p> Each of these methods returns a boolean indicating success or failure.
  30. * More information about a successful match can be obtained by querying the
  31. * state of the matcher.
  32. *
  33. * <p> This class also defines methods for replacing matched subsequences with
  34. * new strings whose contents can, if desired, be computed from the match
  35. * result. The {@link #appendReplacement appendReplacement} and {@link
  36. * #appendTail appendTail} methods can be used in tandem in order to collect
  37. * the result into an existing string buffer, or the more convenient {@link
  38. * #replaceAll replaceAll} method can be used to create a string in which every
  39. * matching subsequence in the input sequence is replaced.
  40. *
  41. * <p> The explicit state of a matcher includes the start and end indices of
  42. * the most recent successful match. It also includes the start and end
  43. * indices of the input subsequence captured by each <a
  44. * href="Pattern.html#cg">capturing group</a> in the pattern as well as a total
  45. * count of such subsequences. As a convenience, methods are also provided for
  46. * returning these captured subsequences in string form.
  47. *
  48. * <p> The explicit state of a matcher is initially undefined; attempting to
  49. * query any part of it before a successful match will cause an {@link
  50. * IllegalStateException} to be thrown. The explicit state of a matcher is
  51. * recomputed by every match operation.
  52. *
  53. * <p> The implicit state of a matcher includes the input character sequence as
  54. * well as the <i>append position</i>, which is initially zero and is updated
  55. * by the {@link #appendReplacement appendReplacement} method.
  56. *
  57. * <p> A matcher may be reset explicitly by invoking its {@link #reset()}
  58. * method or, if a new input sequence is desired, its {@link
  59. * #reset(java.lang.CharSequence) reset(CharSequence)} method. Resetting a
  60. * matcher discards its explicit state information and sets the append position
  61. * to zero.
  62. *
  63. * <p> Instances of this class are not safe for use by multiple concurrent
  64. * threads. </p>
  65. *
  66. *
  67. * @author Mike McCloskey
  68. * @author Mark Reinhold
  69. * @author JSR-51 Expert Group
  70. * @version 1.46, 03/01/23
  71. * @since 1.4
  72. * @spec JSR-51
  73. */
  74. public final class Matcher {
  75. /**
  76. * The Pattern object that created this Matcher.
  77. */
  78. Pattern parentPattern;
  79. /**
  80. * The storage used by groups. They may contain invalid values if
  81. * a group was skipped during the matching.
  82. */
  83. int[] groups;
  84. /**
  85. * The range within the string that is to be matched.
  86. */
  87. int from, to;
  88. /**
  89. * The original string being matched.
  90. */
  91. CharSequence text;
  92. /**
  93. * Matcher state used by the last node. NOANCHOR is used when a
  94. * match does not have to consume all of the input. ENDANCHOR is
  95. * the mode used for matching all the input.
  96. */
  97. static final int ENDANCHOR = 1;
  98. static final int NOANCHOR = 0;
  99. int acceptMode = NOANCHOR;
  100. /**
  101. * The range of string that last matched the pattern.
  102. */
  103. int first = -1, last = -1;
  104. /**
  105. * The end index of what matched in the last match operation.
  106. */
  107. int oldLast = -1;
  108. /**
  109. * The index of the last position appended in a substitution.
  110. */
  111. int lastAppendPosition = 0;
  112. /**
  113. * Storage used by nodes to tell what repetition they are on in
  114. * a pattern, and where groups begin. The nodes themselves are stateless,
  115. * so they rely on this field to hold state during a match.
  116. */
  117. int[] locals;
  118. /**
  119. * No default constructor.
  120. */
  121. Matcher() {
  122. }
  123. /**
  124. * All matchers have the state used by Pattern during a match.
  125. */
  126. Matcher(Pattern parent, CharSequence text) {
  127. this.parentPattern = parent;
  128. this.text = text;
  129. // Allocate state storage
  130. int parentGroupCount = Math.max(parent.groupCount, 10);
  131. groups = new int[parentGroupCount * 2];
  132. locals = new int[parent.localCount];
  133. // Put fields into initial states
  134. reset();
  135. }
  136. /**
  137. * Returns the pattern that is interpreted by this matcher. </p>
  138. *
  139. * @return The pattern for which this matcher was created
  140. */
  141. public Pattern pattern() {
  142. return parentPattern;
  143. }
  144. /**
  145. * Resets this matcher.
  146. *
  147. * <p> Resetting a matcher discards all of its explicit state information
  148. * and sets its append position to zero. </p>
  149. *
  150. * @return This matcher
  151. */
  152. public Matcher reset() {
  153. first = -1;
  154. last = -1;
  155. oldLast = -1;
  156. for(int i=0; i<groups.length; i++)
  157. groups[i] = -1;
  158. for(int i=0; i<locals.length; i++)
  159. locals[i] = -1;
  160. lastAppendPosition = 0;
  161. return this;
  162. }
  163. /**
  164. * Resets this matcher with a new input sequence.
  165. *
  166. * <p> Resetting a matcher discards all of its explicit state information
  167. * and sets its append position to zero. </p>
  168. *
  169. * @param input
  170. * The new input character sequence
  171. *
  172. * @return This matcher
  173. */
  174. public Matcher reset(CharSequence input) {
  175. text = input;
  176. return reset();
  177. }
  178. /**
  179. * Returns the start index of the previous match. </p>
  180. *
  181. * @return The index of the first character matched
  182. *
  183. * @throws IllegalStateException
  184. * If no match has yet been attempted,
  185. * or if the previous match operation failed
  186. */
  187. public int start() {
  188. if (first < 0)
  189. throw new IllegalStateException("No match available");
  190. return first;
  191. }
  192. /**
  193. * Returns the start index of the subsequence captured by the given group
  194. * during the previous match operation.
  195. *
  196. * <p> <a href="Pattern.html#cg">Capturing groups</a> are indexed from left
  197. * to right, starting at one. Group zero denotes the entire pattern, so
  198. * the expression <i>m.</i><tt>start(0)</tt> is equivalent to
  199. * <i>m.</i><tt>start()</tt>. </p>
  200. *
  201. * @param group
  202. * The index of a capturing group in this matcher's pattern
  203. *
  204. * @return The index of the first character captured by the group,
  205. * or <tt>-1</tt> if the match was successful but the group
  206. * itself did not match anything
  207. *
  208. * @throws IllegalStateException
  209. * If no match has yet been attempted,
  210. * or if the previous match operation failed
  211. *
  212. * @throws IndexOutOfBoundsException
  213. * If there is no capturing group in the pattern
  214. * with the given index
  215. */
  216. public int start(int group) {
  217. if (first < 0)
  218. throw new IllegalStateException("No match available");
  219. if (group > groupCount())
  220. throw new IndexOutOfBoundsException("No group " + group);
  221. return groups[group * 2];
  222. }
  223. /**
  224. * Returns the index of the last character matched, plus one. </p>
  225. *
  226. * @return The index of the last character matched, plus one
  227. *
  228. * @throws IllegalStateException
  229. * If no match has yet been attempted,
  230. * or if the previous match operation failed
  231. */
  232. public int end() {
  233. if (first < 0)
  234. throw new IllegalStateException("No match available");
  235. return last;
  236. }
  237. /**
  238. * Returns the index of the last character, plus one, of the subsequence
  239. * captured by the given group during the previous match operation.
  240. *
  241. * <p> <a href="Pattern.html#cg">Capturing groups</a> are indexed from left
  242. * to right, starting at one. Group zero denotes the entire pattern, so
  243. * the expression <i>m.</i><tt>end(0)</tt> is equivalent to
  244. * <i>m.</i><tt>end()</tt>. </p>
  245. *
  246. * @param group
  247. * The index of a capturing group in this matcher's pattern
  248. *
  249. * @return The index of the last character captured by the group,
  250. * plus one, or <tt>-1</tt> if the match was successful
  251. * but the group itself did not match anything
  252. *
  253. * @throws IllegalStateException
  254. * If no match has yet been attempted,
  255. * or if the previous match operation failed
  256. *
  257. * @throws IndexOutOfBoundsException
  258. * If there is no capturing group in the pattern
  259. * with the given index
  260. */
  261. public int end(int group) {
  262. if (first < 0)
  263. throw new IllegalStateException("No match available");
  264. if (group > groupCount())
  265. throw new IndexOutOfBoundsException("No group " + group);
  266. return groups[group * 2 + 1];
  267. }
  268. /**
  269. * Returns the input subsequence matched by the previous match.
  270. *
  271. * <p> For a matcher <i>m</i> with input sequence <i>s</i>,
  272. * the expressions <i>m.</i><tt>group()</tt> and
  273. * <i>s.</i><tt>substring(</tt><i>m.</i><tt>start(),</tt> <i>m.</i><tt>end())</tt>
  274. * are equivalent. </p>
  275. *
  276. * <p> Note that some patterns, for example <tt>a*</tt>, match the empty
  277. * string. This method will return the empty string when the pattern
  278. * successfully matches the empty string in the input. </p>
  279. *
  280. * @return The (possibly empty) subsequence matched by the previous match,
  281. * in string form
  282. *
  283. * @throws IllegalStateException
  284. * If no match has yet been attempted,
  285. * or if the previous match operation failed
  286. */
  287. public String group() {
  288. return group(0);
  289. }
  290. /**
  291. * Returns the input subsequence captured by the given group during the
  292. * previous match operation.
  293. *
  294. * <p> For a matcher <i>m</i>, input sequence <i>s</i>, and group index
  295. * <i>g</i>, the expressions <i>m.</i><tt>group(</tt><i>g</i><tt>)</tt> and
  296. * <i>s.</i><tt>substring(</tt><i>m.</i><tt>start(</tt><i>g</i><tt>),</tt> <i>m.</i><tt>end(</tt><i>g</i><tt>))</tt>
  297. * are equivalent. </p>
  298. *
  299. * <p> <a href="Pattern.html#cg">Capturing groups</a> are indexed from left
  300. * to right, starting at one. Group zero denotes the entire pattern, so
  301. * the expression <tt>m.group(0)</tt> is equivalent to <tt>m.group()</tt>.
  302. * </p>
  303. *
  304. * <p> If the match was successful but the group specified failed to match
  305. * any part of the input sequence, then <tt>null</tt> is returned. Note
  306. * that some groups, for example <tt>(a*)</tt>, match the empty string.
  307. * This method will return the empty string when such a group successfully
  308. * matches the emtpy string in the input. </p>
  309. *
  310. * @param group
  311. * The index of a capturing group in this matcher's pattern
  312. *
  313. * @return The (possibly empty) subsequence captured by the group
  314. * during the previous match, or <tt>null</tt> if the group
  315. * failed to match part of the input
  316. *
  317. * @throws IllegalStateException
  318. * If no match has yet been attempted,
  319. * or if the previous match operation failed
  320. *
  321. * @throws IndexOutOfBoundsException
  322. * If there is no capturing group in the pattern
  323. * with the given index
  324. */
  325. public String group(int group) {
  326. if (first < 0)
  327. throw new IllegalStateException("No match found");
  328. if (group < 0 || group > groupCount())
  329. throw new IndexOutOfBoundsException("No group " + group);
  330. if ((groups[group*2] == -1) || (groups[group*2+1] == -1))
  331. return null;
  332. return getSubSequence(groups[group * 2], groups[group * 2 + 1]).toString();
  333. }
  334. /**
  335. * Returns the number of capturing groups in this matcher's pattern.
  336. *
  337. * <p> Group zero denotes the entire pattern by convention. It is not
  338. * included in this count.
  339. *
  340. * <p> Any non-negative integer smaller than or equal to the value
  341. * returned by this method is guaranteed to be a valid group index for
  342. * this matcher. </p>
  343. *
  344. * @return The number of capturing groups in this matcher's pattern
  345. */
  346. public int groupCount() {
  347. return parentPattern.groupCount - 1;
  348. }
  349. /**
  350. * Attempts to match the entire input sequence against the pattern.
  351. *
  352. * <p> If the match succeeds then more information can be obtained via the
  353. * <tt>start</tt>, <tt>end</tt>, and <tt>group</tt> methods. </p>
  354. *
  355. * @return <tt>true</tt> if, and only if, the entire input sequence
  356. * matches this matcher's pattern
  357. */
  358. public boolean matches() {
  359. reset();
  360. return match(0, getTextLength(), ENDANCHOR);
  361. }
  362. /**
  363. * Attempts to find the next subsequence of the input sequence that matches
  364. * the pattern.
  365. *
  366. * <p> This method starts at the beginning of the input sequence or, if a
  367. * previous invocation of the method was successful and the matcher has not
  368. * since been reset, at the first character not matched by the previous
  369. * match.
  370. *
  371. * <p> If the match succeeds then more information can be obtained via the
  372. * <tt>start</tt>, <tt>end</tt>, and <tt>group</tt> methods. </p>
  373. *
  374. * @return <tt>true</tt> if, and only if, a subsequence of the input
  375. * sequence matches this matcher's pattern
  376. */
  377. public boolean find() {
  378. if (last == first)
  379. last++;
  380. if (last > to) {
  381. for (int i = 0; i < groups.length; i++)
  382. groups[i] = -1;
  383. return false;
  384. }
  385. return find(last, getTextLength());
  386. }
  387. /**
  388. * Resets this matcher and then attempts to find the next subsequence of
  389. * the input sequence that matches the pattern, starting at the specified
  390. * index.
  391. *
  392. * <p> If the match succeeds then more information can be obtained via the
  393. * <tt>start</tt>, <tt>end</tt>, and <tt>group</tt> methods, and subsequent
  394. * invocations of the {@link #find()} method will start at the first
  395. * character not matched by this match. </p>
  396. *
  397. * @throws IndexOutOfBoundsException
  398. * If start is less than zero or if start is greater than the
  399. * length of the input sequence.
  400. *
  401. * @return <tt>true</tt> if, and only if, a subsequence of the input
  402. * sequence starting at the given index matches this matcher's
  403. * pattern
  404. */
  405. public boolean find(int start) {
  406. int limit = getTextLength();
  407. if ((start < 0) || (start > limit))
  408. throw new IndexOutOfBoundsException("Illegal start index");
  409. reset();
  410. return find(start, limit);
  411. }
  412. /**
  413. * Attempts to match the input sequence, starting at the beginning, against
  414. * the pattern.
  415. *
  416. * <p> Like the {@link #matches matches} method, this method always starts
  417. * at the beginning of the input sequence; unlike that method, it does not
  418. * require that the entire input sequence be matched.
  419. *
  420. * <p> If the match succeeds then more information can be obtained via the
  421. * <tt>start</tt>, <tt>end</tt>, and <tt>group</tt> methods. </p>
  422. *
  423. * @return <tt>true</tt> if, and only if, a prefix of the input
  424. * sequence matches this matcher's pattern
  425. */
  426. public boolean lookingAt() {
  427. reset();
  428. return match(0, getTextLength(), NOANCHOR);
  429. }
  430. /**
  431. * Implements a non-terminal append-and-replace step.
  432. *
  433. * <p> This method performs the following actions: </p>
  434. *
  435. * <ol>
  436. *
  437. * <li><p> It reads characters from the input sequence, starting at the
  438. * append position, and appends them to the given string buffer. It
  439. * stops after reading the last character preceding the previous match,
  440. * that is, the character at index {@link
  441. * #start()} <tt>-</tt> <tt>1</tt>. </p></li>
  442. *
  443. * <li><p> It appends the given replacement string to the string buffer.
  444. * </p></li>
  445. *
  446. * <li><p> It sets the append position of this matcher to the index of
  447. * the last character matched, plus one, that is, to {@link #end()}.
  448. * </p></li>
  449. *
  450. * </ol>
  451. *
  452. * <p> The replacement string may contain references to subsequences
  453. * captured during the previous match: Each occurrence of
  454. * <tt>$</tt><i>g</i><tt></tt> will be replaced by the result of
  455. * evaluating {@link #group(int) group}<tt>(</tt><i>g</i><tt>)</tt>.
  456. * The first number after the <tt>$</tt> is always treated as part of
  457. * the group reference. Subsequent numbers are incorporated into g if
  458. * they would form a legal group reference. Only the numerals '0'
  459. * through '9' are considered as potential components of the group
  460. * reference. If the second group matched the string <tt>"foo"</tt>, for
  461. * example, then passing the replacement string <tt>"$2bar"</tt> would
  462. * cause <tt>"foobar"</tt> to be appended to the string buffer. A dollar
  463. * sign (<tt>$</tt>) may be included as a literal in the replacement
  464. * string by preceding it with a backslash (<tt>\$</tt>).
  465. *
  466. * <p> Note that backslashes (<tt>\</tt>) and dollar signs (<tt>$</tt>) in
  467. * the replacement string may cause the results to be different than if it
  468. * were being treated as a literal replacement string. Dollar signs may be
  469. * treated as references to captured subsequences as described above, and
  470. * backslashes are used to escape literal characters in the replacement
  471. * string.
  472. *
  473. * <p> This method is intended to be used in a loop together with the
  474. * {@link #appendTail appendTail} and {@link #find find} methods. The
  475. * following code, for example, writes <tt>one dog two dogs in the
  476. * yard</tt> to the standard-output stream: </p>
  477. *
  478. * <blockquote><pre>
  479. * Pattern p = Pattern.compile("cat");
  480. * Matcher m = p.matcher("one cat two cats in the yard");
  481. * StringBuffer sb = new StringBuffer();
  482. * while (m.find()) {
  483. * m.appendReplacement(sb, "dog");
  484. * }
  485. * m.appendTail(sb);
  486. * System.out.println(sb.toString());</pre></blockquote>
  487. *
  488. * @param sb
  489. * The target string buffer
  490. *
  491. * @param replacement
  492. * The replacement string
  493. *
  494. * @return This matcher
  495. *
  496. * @throws IllegalStateException
  497. * If no match has yet been attempted,
  498. * or if the previous match operation failed
  499. *
  500. * @throws IndexOutOfBoundsException
  501. * If the replacement string refers to a capturing group
  502. * that does not exist in the pattern
  503. */
  504. public Matcher appendReplacement(StringBuffer sb, String replacement) {
  505. // If no match, return error
  506. if (first < 0)
  507. throw new IllegalStateException("No match available");
  508. // Process substitution string to replace group references with groups
  509. int cursor = 0;
  510. String s = replacement;
  511. StringBuffer result = new StringBuffer();
  512. while (cursor < replacement.length()) {
  513. char nextChar = replacement.charAt(cursor);
  514. if (nextChar == '\\') {
  515. cursor++;
  516. nextChar = replacement.charAt(cursor);
  517. result.append(nextChar);
  518. cursor++;
  519. } else if (nextChar == '$') {
  520. // Skip past $
  521. cursor++;
  522. // The first number is always a group
  523. int refNum = (int)replacement.charAt(cursor) - '0';
  524. if ((refNum < 0)||(refNum > 9))
  525. throw new IllegalArgumentException(
  526. "Illegal group reference");
  527. cursor++;
  528. // Capture the largest legal group string
  529. boolean done = false;
  530. while (!done) {
  531. if (cursor >= replacement.length()) {
  532. break;
  533. }
  534. int nextDigit = replacement.charAt(cursor) - '0';
  535. if ((nextDigit < 0)||(nextDigit > 9)) { // not a number
  536. break;
  537. }
  538. int newRefNum = (refNum * 10) + nextDigit;
  539. if (groupCount() < newRefNum) {
  540. done = true;
  541. } else {
  542. refNum = newRefNum;
  543. cursor++;
  544. }
  545. }
  546. // Append group
  547. if (group(refNum) != null)
  548. result.append(group(refNum));
  549. } else {
  550. result.append(nextChar);
  551. cursor++;
  552. }
  553. }
  554. // Append the intervening text
  555. sb.append(getSubSequence(lastAppendPosition, first));
  556. // Append the match substitution
  557. sb.append(result.toString());
  558. lastAppendPosition = last;
  559. return this;
  560. }
  561. /**
  562. * Implements a terminal append-and-replace step.
  563. *
  564. * <p> This method reads characters from the input sequence, starting at
  565. * the append position, and appends them to the given string buffer. It is
  566. * intended to be invoked after one or more invocations of the {@link
  567. * #appendReplacement appendReplacement} method in order to copy the
  568. * remainder of the input sequence. </p>
  569. *
  570. * @param sb
  571. * The target string buffer
  572. *
  573. * @return The target string buffer
  574. */
  575. public StringBuffer appendTail(StringBuffer sb) {
  576. sb.append(getSubSequence(lastAppendPosition, getTextLength()).toString());
  577. return sb;
  578. }
  579. /**
  580. * Replaces every subsequence of the input sequence that matches the
  581. * pattern with the given replacement string.
  582. *
  583. * <p> This method first resets this matcher. It then scans the input
  584. * sequence looking for matches of the pattern. Characters that are not
  585. * part of any match are appended directly to the result string; each match
  586. * is replaced in the result by the replacement string. The replacement
  587. * string may contain references to captured subsequences as in the {@link
  588. * #appendReplacement appendReplacement} method.
  589. *
  590. * <p> Note that backslashes (<tt>\</tt>) and dollar signs (<tt>$</tt>) in
  591. * the replacement string may cause the results to be different than if it
  592. * were being treated as a literal replacement string. Dollar signs may be
  593. * treated as references to captured subsequences as described above, and
  594. * backslashes are used to escape literal characters in the replacement
  595. * string.
  596. *
  597. * <p> Given the regular expression <tt>a*b</tt>, the input
  598. * <tt>"aabfooaabfooabfoob"</tt>, and the replacement string
  599. * <tt>"-"</tt>, an invocation of this method on a matcher for that
  600. * expression would yield the string <tt>"-foo-foo-foo-"</tt>.
  601. *
  602. * <p> Invoking this method changes this matcher's state. If the matcher
  603. * is to be used in further matching operations then it should first be
  604. * reset. </p>
  605. *
  606. * @param replacement
  607. * The replacement string
  608. *
  609. * @return The string constructed by replacing each matching subsequence
  610. * by the replacement string, substituting captured subsequences
  611. * as needed
  612. */
  613. public String replaceAll(String replacement) {
  614. reset();
  615. boolean result = find();
  616. if (result) {
  617. StringBuffer sb = new StringBuffer();
  618. do {
  619. appendReplacement(sb, replacement);
  620. result = find();
  621. } while (result);
  622. appendTail(sb);
  623. return sb.toString();
  624. }
  625. return text.toString();
  626. }
  627. /**
  628. * Replaces the first subsequence of the input sequence that matches the
  629. * pattern with the given replacement string.
  630. *
  631. * <p> This method first resets this matcher. It then scans the input
  632. * sequence looking for a match of the pattern. Characters that are not
  633. * part of the match are appended directly to the result string; the match
  634. * is replaced in the result by the replacement string. The replacement
  635. * string may contain references to captured subsequences as in the {@link
  636. * #appendReplacement appendReplacement} method.
  637. *
  638. * <p> Given the regular expression <tt>dog</tt>, the input
  639. * <tt>"zzzdogzzzdogzzz"</tt>, and the replacement string
  640. * <tt>"cat"</tt>, an invocation of this method on a matcher for that
  641. * expression would yield the string <tt>"zzzcatzzzdogzzz"</tt>. </p>
  642. *
  643. * <p> Invoking this method changes this matcher's state. If the matcher
  644. * is to be used in further matching operations then it should first be
  645. * reset. </p>
  646. *
  647. * @param replacement
  648. * The replacement string
  649. *
  650. * @return The string constructed by replacing the first matching
  651. * subsequence by the replacement string, substituting captured
  652. * subsequences as needed
  653. */
  654. public String replaceFirst(String replacement) {
  655. StringBuffer sb = new StringBuffer();
  656. reset();
  657. if (find())
  658. appendReplacement(sb, replacement);
  659. appendTail(sb);
  660. return sb.toString();
  661. }
  662. /**
  663. * Initiates a search to find a Pattern within the given bounds.
  664. * The groups are filled with default values and the match of the root
  665. * of the state machine is called. The state machine will hold the state
  666. * of the match as it proceeds in this matcher.
  667. */
  668. private boolean find(int from, int to) {
  669. from = from < 0 ? 0 : from;
  670. this.to = to;
  671. this.first = from;
  672. this.last = -1;
  673. this.oldLast = oldLast < 0 ? from : oldLast;
  674. for (int i = 0; i < groups.length; i++)
  675. groups[i] = -1;
  676. acceptMode = NOANCHOR;
  677. boolean result = parentPattern.root.match(this, from, text);
  678. if (!result)
  679. this.first = -1;
  680. this.oldLast = this.last;
  681. return result;
  682. }
  683. /**
  684. * Initiates a search for an anchored match to a Pattern within the given
  685. * bounds. The groups are filled with default values and the match of the
  686. * root of the state machine is called. The state machine will hold the
  687. * state of the match as it proceeds in this matcher.
  688. */
  689. private boolean match(int from, int to, int anchor) {
  690. from = from < 0 ? 0 : from;
  691. this.to = to;
  692. this.first = from;
  693. this.last = -1;
  694. this.oldLast = oldLast < 0 ? from : oldLast;
  695. for (int i = 0; i < groups.length; i++)
  696. groups[i] = -1;
  697. acceptMode = anchor;
  698. boolean result = parentPattern.matchRoot.match(this, from, text);
  699. if (!result)
  700. this.first = -1;
  701. this.oldLast = this.last;
  702. return result;
  703. }
  704. /**
  705. * Returns the end index of the text.
  706. *
  707. * @return the index after the last character in the text
  708. */
  709. int getTextLength() {
  710. return text.length();
  711. }
  712. /**
  713. * Generates a String from this Matcher's input in the specified range.
  714. *
  715. * @param beginIndex the beginning index, inclusive
  716. * @param endIndex the ending index, exclusive
  717. * @return A String generated from this Matcher's input
  718. */
  719. CharSequence getSubSequence(int beginIndex, int endIndex) {
  720. return text.subSequence(beginIndex, endIndex);
  721. }
  722. /**
  723. * Returns this Matcher's input character at index i.
  724. *
  725. * @return A char from the specified index
  726. */
  727. char charAt(int i) {
  728. return text.charAt(i);
  729. }
  730. }