1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/rt/emul/compact/src/main/java/java/util/regex/Matcher.java Mon Oct 07 16:13:27 2013 +0200
1.3 @@ -0,0 +1,1256 @@
1.4 +/*
1.5 + * Copyright (c) 1999, 2009, Oracle and/or its affiliates. All rights reserved.
1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
1.7 + *
1.8 + * This code is free software; you can redistribute it and/or modify it
1.9 + * under the terms of the GNU General Public License version 2 only, as
1.10 + * published by the Free Software Foundation. Oracle designates this
1.11 + * particular file as subject to the "Classpath" exception as provided
1.12 + * by Oracle in the LICENSE file that accompanied this code.
1.13 + *
1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
1.17 + * version 2 for more details (a copy is included in the LICENSE file that
1.18 + * accompanied this code).
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License version
1.21 + * 2 along with this work; if not, write to the Free Software Foundation,
1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1.23 + *
1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
1.25 + * or visit www.oracle.com if you need additional information or have any
1.26 + * questions.
1.27 + */
1.28 +
1.29 +package java.util.regex;
1.30 +
1.31 +
1.32 +/**
1.33 + * An engine that performs match operations on a {@link java.lang.CharSequence
1.34 + * </code>character sequence<code>} by interpreting a {@link Pattern}.
1.35 + *
1.36 + * <p> A matcher is created from a pattern by invoking the pattern's {@link
1.37 + * Pattern#matcher matcher} method. Once created, a matcher can be used to
1.38 + * perform three different kinds of match operations:
1.39 + *
1.40 + * <ul>
1.41 + *
1.42 + * <li><p> The {@link #matches matches} method attempts to match the entire
1.43 + * input sequence against the pattern. </p></li>
1.44 + *
1.45 + * <li><p> The {@link #lookingAt lookingAt} method attempts to match the
1.46 + * input sequence, starting at the beginning, against the pattern. </p></li>
1.47 + *
1.48 + * <li><p> The {@link #find find} method scans the input sequence looking for
1.49 + * the next subsequence that matches the pattern. </p></li>
1.50 + *
1.51 + * </ul>
1.52 + *
1.53 + * <p> Each of these methods returns a boolean indicating success or failure.
1.54 + * More information about a successful match can be obtained by querying the
1.55 + * state of the matcher.
1.56 + *
1.57 + * <p> A matcher finds matches in a subset of its input called the
1.58 + * <i>region</i>. By default, the region contains all of the matcher's input.
1.59 + * The region can be modified via the{@link #region region} method and queried
1.60 + * via the {@link #regionStart regionStart} and {@link #regionEnd regionEnd}
1.61 + * methods. The way that the region boundaries interact with some pattern
1.62 + * constructs can be changed. See {@link #useAnchoringBounds
1.63 + * useAnchoringBounds} and {@link #useTransparentBounds useTransparentBounds}
1.64 + * for more details.
1.65 + *
1.66 + * <p> This class also defines methods for replacing matched subsequences with
1.67 + * new strings whose contents can, if desired, be computed from the match
1.68 + * result. The {@link #appendReplacement appendReplacement} and {@link
1.69 + * #appendTail appendTail} methods can be used in tandem in order to collect
1.70 + * the result into an existing string buffer, or the more convenient {@link
1.71 + * #replaceAll replaceAll} method can be used to create a string in which every
1.72 + * matching subsequence in the input sequence is replaced.
1.73 + *
1.74 + * <p> The explicit state of a matcher includes the start and end indices of
1.75 + * the most recent successful match. It also includes the start and end
1.76 + * indices of the input subsequence captured by each <a
1.77 + * href="Pattern.html#cg">capturing group</a> in the pattern as well as a total
1.78 + * count of such subsequences. As a convenience, methods are also provided for
1.79 + * returning these captured subsequences in string form.
1.80 + *
1.81 + * <p> The explicit state of a matcher is initially undefined; attempting to
1.82 + * query any part of it before a successful match will cause an {@link
1.83 + * IllegalStateException} to be thrown. The explicit state of a matcher is
1.84 + * recomputed by every match operation.
1.85 + *
1.86 + * <p> The implicit state of a matcher includes the input character sequence as
1.87 + * well as the <i>append position</i>, which is initially zero and is updated
1.88 + * by the {@link #appendReplacement appendReplacement} method.
1.89 + *
1.90 + * <p> A matcher may be reset explicitly by invoking its {@link #reset()}
1.91 + * method or, if a new input sequence is desired, its {@link
1.92 + * #reset(java.lang.CharSequence) reset(CharSequence)} method. Resetting a
1.93 + * matcher discards its explicit state information and sets the append position
1.94 + * to zero.
1.95 + *
1.96 + * <p> Instances of this class are not safe for use by multiple concurrent
1.97 + * threads. </p>
1.98 + *
1.99 + *
1.100 + * @author Mike McCloskey
1.101 + * @author Mark Reinhold
1.102 + * @author JSR-51 Expert Group
1.103 + * @since 1.4
1.104 + * @spec JSR-51
1.105 + */
1.106 +
1.107 +public final class Matcher implements MatchResult {
1.108 +
1.109 + /**
1.110 + * The Pattern object that created this Matcher.
1.111 + */
1.112 + Pattern parentPattern;
1.113 +
1.114 + /**
1.115 + * The storage used by groups. They may contain invalid values if
1.116 + * a group was skipped during the matching.
1.117 + */
1.118 + int[] groups;
1.119 +
1.120 + /**
1.121 + * The range within the sequence that is to be matched. Anchors
1.122 + * will match at these "hard" boundaries. Changing the region
1.123 + * changes these values.
1.124 + */
1.125 + int from, to;
1.126 +
1.127 + /**
1.128 + * Lookbehind uses this value to ensure that the subexpression
1.129 + * match ends at the point where the lookbehind was encountered.
1.130 + */
1.131 + int lookbehindTo;
1.132 +
1.133 + /**
1.134 + * The original string being matched.
1.135 + */
1.136 + CharSequence text;
1.137 +
1.138 + /**
1.139 + * Matcher state used by the last node. NOANCHOR is used when a
1.140 + * match does not have to consume all of the input. ENDANCHOR is
1.141 + * the mode used for matching all the input.
1.142 + */
1.143 + static final int ENDANCHOR = 1;
1.144 + static final int NOANCHOR = 0;
1.145 + int acceptMode = NOANCHOR;
1.146 +
1.147 + /**
1.148 + * The range of string that last matched the pattern. If the last
1.149 + * match failed then first is -1; last initially holds 0 then it
1.150 + * holds the index of the end of the last match (which is where the
1.151 + * next search starts).
1.152 + */
1.153 + int first = -1, last = 0;
1.154 +
1.155 + /**
1.156 + * The end index of what matched in the last match operation.
1.157 + */
1.158 + int oldLast = -1;
1.159 +
1.160 + /**
1.161 + * The index of the last position appended in a substitution.
1.162 + */
1.163 + int lastAppendPosition = 0;
1.164 +
1.165 + /**
1.166 + * Storage used by nodes to tell what repetition they are on in
1.167 + * a pattern, and where groups begin. The nodes themselves are stateless,
1.168 + * so they rely on this field to hold state during a match.
1.169 + */
1.170 + int[] locals;
1.171 +
1.172 + /**
1.173 + * Boolean indicating whether or not more input could change
1.174 + * the results of the last match.
1.175 + *
1.176 + * If hitEnd is true, and a match was found, then more input
1.177 + * might cause a different match to be found.
1.178 + * If hitEnd is true and a match was not found, then more
1.179 + * input could cause a match to be found.
1.180 + * If hitEnd is false and a match was found, then more input
1.181 + * will not change the match.
1.182 + * If hitEnd is false and a match was not found, then more
1.183 + * input will not cause a match to be found.
1.184 + */
1.185 + boolean hitEnd;
1.186 +
1.187 + /**
1.188 + * Boolean indicating whether or not more input could change
1.189 + * a positive match into a negative one.
1.190 + *
1.191 + * If requireEnd is true, and a match was found, then more
1.192 + * input could cause the match to be lost.
1.193 + * If requireEnd is false and a match was found, then more
1.194 + * input might change the match but the match won't be lost.
1.195 + * If a match was not found, then requireEnd has no meaning.
1.196 + */
1.197 + boolean requireEnd;
1.198 +
1.199 + /**
1.200 + * If transparentBounds is true then the boundaries of this
1.201 + * matcher's region are transparent to lookahead, lookbehind,
1.202 + * and boundary matching constructs that try to see beyond them.
1.203 + */
1.204 + boolean transparentBounds = false;
1.205 +
1.206 + /**
1.207 + * If anchoringBounds is true then the boundaries of this
1.208 + * matcher's region match anchors such as ^ and $.
1.209 + */
1.210 + boolean anchoringBounds = true;
1.211 +
1.212 + /**
1.213 + * No default constructor.
1.214 + */
1.215 + Matcher() {
1.216 + }
1.217 +
1.218 + /**
1.219 + * All matchers have the state used by Pattern during a match.
1.220 + */
1.221 + Matcher(Pattern parent, CharSequence text) {
1.222 + this.parentPattern = parent;
1.223 + this.text = text;
1.224 +
1.225 + // Allocate state storage
1.226 + int parentGroupCount = Math.max(parent.capturingGroupCount, 10);
1.227 + groups = new int[parentGroupCount * 2];
1.228 + locals = new int[parent.localCount];
1.229 +
1.230 + // Put fields into initial states
1.231 + reset();
1.232 + }
1.233 +
1.234 + /**
1.235 + * Returns the pattern that is interpreted by this matcher.
1.236 + *
1.237 + * @return The pattern for which this matcher was created
1.238 + */
1.239 + public Pattern pattern() {
1.240 + return parentPattern;
1.241 + }
1.242 +
1.243 + /**
1.244 + * Returns the match state of this matcher as a {@link MatchResult}.
1.245 + * The result is unaffected by subsequent operations performed upon this
1.246 + * matcher.
1.247 + *
1.248 + * @return a <code>MatchResult</code> with the state of this matcher
1.249 + * @since 1.5
1.250 + */
1.251 + public MatchResult toMatchResult() {
1.252 + Matcher result = new Matcher(this.parentPattern, text.toString());
1.253 + result.first = this.first;
1.254 + result.last = this.last;
1.255 + result.groups = this.groups.clone();
1.256 + return result;
1.257 + }
1.258 +
1.259 + /**
1.260 + * Changes the <tt>Pattern</tt> that this <tt>Matcher</tt> uses to
1.261 + * find matches with.
1.262 + *
1.263 + * <p> This method causes this matcher to lose information
1.264 + * about the groups of the last match that occurred. The
1.265 + * matcher's position in the input is maintained and its
1.266 + * last append position is unaffected.</p>
1.267 + *
1.268 + * @param newPattern
1.269 + * The new pattern used by this matcher
1.270 + * @return This matcher
1.271 + * @throws IllegalArgumentException
1.272 + * If newPattern is <tt>null</tt>
1.273 + * @since 1.5
1.274 + */
1.275 + public Matcher usePattern(Pattern newPattern) {
1.276 + if (newPattern == null)
1.277 + throw new IllegalArgumentException("Pattern cannot be null");
1.278 + parentPattern = newPattern;
1.279 +
1.280 + // Reallocate state storage
1.281 + int parentGroupCount = Math.max(newPattern.capturingGroupCount, 10);
1.282 + groups = new int[parentGroupCount * 2];
1.283 + locals = new int[newPattern.localCount];
1.284 + for (int i = 0; i < groups.length; i++)
1.285 + groups[i] = -1;
1.286 + for (int i = 0; i < locals.length; i++)
1.287 + locals[i] = -1;
1.288 + return this;
1.289 + }
1.290 +
1.291 + /**
1.292 + * Resets this matcher.
1.293 + *
1.294 + * <p> Resetting a matcher discards all of its explicit state information
1.295 + * and sets its append position to zero. The matcher's region is set to the
1.296 + * default region, which is its entire character sequence. The anchoring
1.297 + * and transparency of this matcher's region boundaries are unaffected.
1.298 + *
1.299 + * @return This matcher
1.300 + */
1.301 + public Matcher reset() {
1.302 + first = -1;
1.303 + last = 0;
1.304 + oldLast = -1;
1.305 + for(int i=0; i<groups.length; i++)
1.306 + groups[i] = -1;
1.307 + for(int i=0; i<locals.length; i++)
1.308 + locals[i] = -1;
1.309 + lastAppendPosition = 0;
1.310 + from = 0;
1.311 + to = getTextLength();
1.312 + return this;
1.313 + }
1.314 +
1.315 + /**
1.316 + * Resets this matcher with a new input sequence.
1.317 + *
1.318 + * <p> Resetting a matcher discards all of its explicit state information
1.319 + * and sets its append position to zero. The matcher's region is set to
1.320 + * the default region, which is its entire character sequence. The
1.321 + * anchoring and transparency of this matcher's region boundaries are
1.322 + * unaffected.
1.323 + *
1.324 + * @param input
1.325 + * The new input character sequence
1.326 + *
1.327 + * @return This matcher
1.328 + */
1.329 + public Matcher reset(CharSequence input) {
1.330 + text = input;
1.331 + return reset();
1.332 + }
1.333 +
1.334 + /**
1.335 + * Returns the start index of the previous match. </p>
1.336 + *
1.337 + * @return The index of the first character matched
1.338 + *
1.339 + * @throws IllegalStateException
1.340 + * If no match has yet been attempted,
1.341 + * or if the previous match operation failed
1.342 + */
1.343 + public int start() {
1.344 + if (first < 0)
1.345 + throw new IllegalStateException("No match available");
1.346 + return first;
1.347 + }
1.348 +
1.349 + /**
1.350 + * Returns the start index of the subsequence captured by the given group
1.351 + * during the previous match operation.
1.352 + *
1.353 + * <p> <a href="Pattern.html#cg">Capturing groups</a> are indexed from left
1.354 + * to right, starting at one. Group zero denotes the entire pattern, so
1.355 + * the expression <i>m.</i><tt>start(0)</tt> is equivalent to
1.356 + * <i>m.</i><tt>start()</tt>. </p>
1.357 + *
1.358 + * @param group
1.359 + * The index of a capturing group in this matcher's pattern
1.360 + *
1.361 + * @return The index of the first character captured by the group,
1.362 + * or <tt>-1</tt> if the match was successful but the group
1.363 + * itself did not match anything
1.364 + *
1.365 + * @throws IllegalStateException
1.366 + * If no match has yet been attempted,
1.367 + * or if the previous match operation failed
1.368 + *
1.369 + * @throws IndexOutOfBoundsException
1.370 + * If there is no capturing group in the pattern
1.371 + * with the given index
1.372 + */
1.373 + public int start(int group) {
1.374 + if (first < 0)
1.375 + throw new IllegalStateException("No match available");
1.376 + if (group > groupCount())
1.377 + throw new IndexOutOfBoundsException("No group " + group);
1.378 + return groups[group * 2];
1.379 + }
1.380 +
1.381 + /**
1.382 + * Returns the offset after the last character matched. </p>
1.383 + *
1.384 + * @return The offset after the last character matched
1.385 + *
1.386 + * @throws IllegalStateException
1.387 + * If no match has yet been attempted,
1.388 + * or if the previous match operation failed
1.389 + */
1.390 + public int end() {
1.391 + if (first < 0)
1.392 + throw new IllegalStateException("No match available");
1.393 + return last;
1.394 + }
1.395 +
1.396 + /**
1.397 + * Returns the offset after the last character of the subsequence
1.398 + * captured by the given group during the previous match operation.
1.399 + *
1.400 + * <p> <a href="Pattern.html#cg">Capturing groups</a> are indexed from left
1.401 + * to right, starting at one. Group zero denotes the entire pattern, so
1.402 + * the expression <i>m.</i><tt>end(0)</tt> is equivalent to
1.403 + * <i>m.</i><tt>end()</tt>. </p>
1.404 + *
1.405 + * @param group
1.406 + * The index of a capturing group in this matcher's pattern
1.407 + *
1.408 + * @return The offset after the last character captured by the group,
1.409 + * or <tt>-1</tt> if the match was successful
1.410 + * but the group itself did not match anything
1.411 + *
1.412 + * @throws IllegalStateException
1.413 + * If no match has yet been attempted,
1.414 + * or if the previous match operation failed
1.415 + *
1.416 + * @throws IndexOutOfBoundsException
1.417 + * If there is no capturing group in the pattern
1.418 + * with the given index
1.419 + */
1.420 + public int end(int group) {
1.421 + if (first < 0)
1.422 + throw new IllegalStateException("No match available");
1.423 + if (group > groupCount())
1.424 + throw new IndexOutOfBoundsException("No group " + group);
1.425 + return groups[group * 2 + 1];
1.426 + }
1.427 +
1.428 + /**
1.429 + * Returns the input subsequence matched by the previous match.
1.430 + *
1.431 + * <p> For a matcher <i>m</i> with input sequence <i>s</i>,
1.432 + * the expressions <i>m.</i><tt>group()</tt> and
1.433 + * <i>s.</i><tt>substring(</tt><i>m.</i><tt>start(),</tt> <i>m.</i><tt>end())</tt>
1.434 + * are equivalent. </p>
1.435 + *
1.436 + * <p> Note that some patterns, for example <tt>a*</tt>, match the empty
1.437 + * string. This method will return the empty string when the pattern
1.438 + * successfully matches the empty string in the input. </p>
1.439 + *
1.440 + * @return The (possibly empty) subsequence matched by the previous match,
1.441 + * in string form
1.442 + *
1.443 + * @throws IllegalStateException
1.444 + * If no match has yet been attempted,
1.445 + * or if the previous match operation failed
1.446 + */
1.447 + public String group() {
1.448 + return group(0);
1.449 + }
1.450 +
1.451 + /**
1.452 + * Returns the input subsequence captured by the given group during the
1.453 + * previous match operation.
1.454 + *
1.455 + * <p> For a matcher <i>m</i>, input sequence <i>s</i>, and group index
1.456 + * <i>g</i>, the expressions <i>m.</i><tt>group(</tt><i>g</i><tt>)</tt> and
1.457 + * <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>
1.458 + * are equivalent. </p>
1.459 + *
1.460 + * <p> <a href="Pattern.html#cg">Capturing groups</a> are indexed from left
1.461 + * to right, starting at one. Group zero denotes the entire pattern, so
1.462 + * the expression <tt>m.group(0)</tt> is equivalent to <tt>m.group()</tt>.
1.463 + * </p>
1.464 + *
1.465 + * <p> If the match was successful but the group specified failed to match
1.466 + * any part of the input sequence, then <tt>null</tt> is returned. Note
1.467 + * that some groups, for example <tt>(a*)</tt>, match the empty string.
1.468 + * This method will return the empty string when such a group successfully
1.469 + * matches the empty string in the input. </p>
1.470 + *
1.471 + * @param group
1.472 + * The index of a capturing group in this matcher's pattern
1.473 + *
1.474 + * @return The (possibly empty) subsequence captured by the group
1.475 + * during the previous match, or <tt>null</tt> if the group
1.476 + * failed to match part of the input
1.477 + *
1.478 + * @throws IllegalStateException
1.479 + * If no match has yet been attempted,
1.480 + * or if the previous match operation failed
1.481 + *
1.482 + * @throws IndexOutOfBoundsException
1.483 + * If there is no capturing group in the pattern
1.484 + * with the given index
1.485 + */
1.486 + public String group(int group) {
1.487 + if (first < 0)
1.488 + throw new IllegalStateException("No match found");
1.489 + if (group < 0 || group > groupCount())
1.490 + throw new IndexOutOfBoundsException("No group " + group);
1.491 + if ((groups[group*2] == -1) || (groups[group*2+1] == -1))
1.492 + return null;
1.493 + return getSubSequence(groups[group * 2], groups[group * 2 + 1]).toString();
1.494 + }
1.495 +
1.496 + /**
1.497 + * Returns the input subsequence captured by the given
1.498 + * <a href="Pattern.html#groupname">named-capturing group</a> during the previous
1.499 + * match operation.
1.500 + *
1.501 + * <p> If the match was successful but the group specified failed to match
1.502 + * any part of the input sequence, then <tt>null</tt> is returned. Note
1.503 + * that some groups, for example <tt>(a*)</tt>, match the empty string.
1.504 + * This method will return the empty string when such a group successfully
1.505 + * matches the empty string in the input. </p>
1.506 + *
1.507 + * @param name
1.508 + * The name of a named-capturing group in this matcher's pattern
1.509 + *
1.510 + * @return The (possibly empty) subsequence captured by the named group
1.511 + * during the previous match, or <tt>null</tt> if the group
1.512 + * failed to match part of the input
1.513 + *
1.514 + * @throws IllegalStateException
1.515 + * If no match has yet been attempted,
1.516 + * or if the previous match operation failed
1.517 + *
1.518 + * @throws IllegalArgumentException
1.519 + * If there is no capturing group in the pattern
1.520 + * with the given name
1.521 + */
1.522 + public String group(String name) {
1.523 + if (name == null)
1.524 + throw new NullPointerException("Null group name");
1.525 + if (first < 0)
1.526 + throw new IllegalStateException("No match found");
1.527 + if (!parentPattern.namedGroups().containsKey(name))
1.528 + throw new IllegalArgumentException("No group with name <" + name + ">");
1.529 + int group = parentPattern.namedGroups().get(name);
1.530 + if ((groups[group*2] == -1) || (groups[group*2+1] == -1))
1.531 + return null;
1.532 + return getSubSequence(groups[group * 2], groups[group * 2 + 1]).toString();
1.533 + }
1.534 +
1.535 + /**
1.536 + * Returns the number of capturing groups in this matcher's pattern.
1.537 + *
1.538 + * <p> Group zero denotes the entire pattern by convention. It is not
1.539 + * included in this count.
1.540 + *
1.541 + * <p> Any non-negative integer smaller than or equal to the value
1.542 + * returned by this method is guaranteed to be a valid group index for
1.543 + * this matcher. </p>
1.544 + *
1.545 + * @return The number of capturing groups in this matcher's pattern
1.546 + */
1.547 + public int groupCount() {
1.548 + return parentPattern.capturingGroupCount - 1;
1.549 + }
1.550 +
1.551 + /**
1.552 + * Attempts to match the entire region against the pattern.
1.553 + *
1.554 + * <p> If the match succeeds then more information can be obtained via the
1.555 + * <tt>start</tt>, <tt>end</tt>, and <tt>group</tt> methods. </p>
1.556 + *
1.557 + * @return <tt>true</tt> if, and only if, the entire region sequence
1.558 + * matches this matcher's pattern
1.559 + */
1.560 + public boolean matches() {
1.561 + return match(from, ENDANCHOR);
1.562 + }
1.563 +
1.564 + /**
1.565 + * Attempts to find the next subsequence of the input sequence that matches
1.566 + * the pattern.
1.567 + *
1.568 + * <p> This method starts at the beginning of this matcher's region, or, if
1.569 + * a previous invocation of the method was successful and the matcher has
1.570 + * not since been reset, at the first character not matched by the previous
1.571 + * match.
1.572 + *
1.573 + * <p> If the match succeeds then more information can be obtained via the
1.574 + * <tt>start</tt>, <tt>end</tt>, and <tt>group</tt> methods. </p>
1.575 + *
1.576 + * @return <tt>true</tt> if, and only if, a subsequence of the input
1.577 + * sequence matches this matcher's pattern
1.578 + */
1.579 + public boolean find() {
1.580 + int nextSearchIndex = last;
1.581 + if (nextSearchIndex == first)
1.582 + nextSearchIndex++;
1.583 +
1.584 + // If next search starts before region, start it at region
1.585 + if (nextSearchIndex < from)
1.586 + nextSearchIndex = from;
1.587 +
1.588 + // If next search starts beyond region then it fails
1.589 + if (nextSearchIndex > to) {
1.590 + for (int i = 0; i < groups.length; i++)
1.591 + groups[i] = -1;
1.592 + return false;
1.593 + }
1.594 + return search(nextSearchIndex);
1.595 + }
1.596 +
1.597 + /**
1.598 + * Resets this matcher and then attempts to find the next subsequence of
1.599 + * the input sequence that matches the pattern, starting at the specified
1.600 + * index.
1.601 + *
1.602 + * <p> If the match succeeds then more information can be obtained via the
1.603 + * <tt>start</tt>, <tt>end</tt>, and <tt>group</tt> methods, and subsequent
1.604 + * invocations of the {@link #find()} method will start at the first
1.605 + * character not matched by this match. </p>
1.606 + *
1.607 + * @throws IndexOutOfBoundsException
1.608 + * If start is less than zero or if start is greater than the
1.609 + * length of the input sequence.
1.610 + *
1.611 + * @return <tt>true</tt> if, and only if, a subsequence of the input
1.612 + * sequence starting at the given index matches this matcher's
1.613 + * pattern
1.614 + */
1.615 + public boolean find(int start) {
1.616 + int limit = getTextLength();
1.617 + if ((start < 0) || (start > limit))
1.618 + throw new IndexOutOfBoundsException("Illegal start index");
1.619 + reset();
1.620 + return search(start);
1.621 + }
1.622 +
1.623 + /**
1.624 + * Attempts to match the input sequence, starting at the beginning of the
1.625 + * region, against the pattern.
1.626 + *
1.627 + * <p> Like the {@link #matches matches} method, this method always starts
1.628 + * at the beginning of the region; unlike that method, it does not
1.629 + * require that the entire region be matched.
1.630 + *
1.631 + * <p> If the match succeeds then more information can be obtained via the
1.632 + * <tt>start</tt>, <tt>end</tt>, and <tt>group</tt> methods. </p>
1.633 + *
1.634 + * @return <tt>true</tt> if, and only if, a prefix of the input
1.635 + * sequence matches this matcher's pattern
1.636 + */
1.637 + public boolean lookingAt() {
1.638 + return match(from, NOANCHOR);
1.639 + }
1.640 +
1.641 + /**
1.642 + * Returns a literal replacement <code>String</code> for the specified
1.643 + * <code>String</code>.
1.644 + *
1.645 + * This method produces a <code>String</code> that will work
1.646 + * as a literal replacement <code>s</code> in the
1.647 + * <code>appendReplacement</code> method of the {@link Matcher} class.
1.648 + * The <code>String</code> produced will match the sequence of characters
1.649 + * in <code>s</code> treated as a literal sequence. Slashes ('\') and
1.650 + * dollar signs ('$') will be given no special meaning.
1.651 + *
1.652 + * @param s The string to be literalized
1.653 + * @return A literal string replacement
1.654 + * @since 1.5
1.655 + */
1.656 + public static String quoteReplacement(String s) {
1.657 + if ((s.indexOf('\\') == -1) && (s.indexOf('$') == -1))
1.658 + return s;
1.659 + StringBuilder sb = new StringBuilder();
1.660 + for (int i=0; i<s.length(); i++) {
1.661 + char c = s.charAt(i);
1.662 + if (c == '\\' || c == '$') {
1.663 + sb.append('\\');
1.664 + }
1.665 + sb.append(c);
1.666 + }
1.667 + return sb.toString();
1.668 + }
1.669 +
1.670 + /**
1.671 + * Implements a non-terminal append-and-replace step.
1.672 + *
1.673 + * <p> This method performs the following actions: </p>
1.674 + *
1.675 + * <ol>
1.676 + *
1.677 + * <li><p> It reads characters from the input sequence, starting at the
1.678 + * append position, and appends them to the given string buffer. It
1.679 + * stops after reading the last character preceding the previous match,
1.680 + * that is, the character at index {@link
1.681 + * #start()} <tt>-</tt> <tt>1</tt>. </p></li>
1.682 + *
1.683 + * <li><p> It appends the given replacement string to the string buffer.
1.684 + * </p></li>
1.685 + *
1.686 + * <li><p> It sets the append position of this matcher to the index of
1.687 + * the last character matched, plus one, that is, to {@link #end()}.
1.688 + * </p></li>
1.689 + *
1.690 + * </ol>
1.691 + *
1.692 + * <p> The replacement string may contain references to subsequences
1.693 + * captured during the previous match: Each occurrence of
1.694 + * <tt>${</tt><i>name</i><tt>}</tt> or <tt>$</tt><i>g</i>
1.695 + * will be replaced by the result of evaluating the corresponding
1.696 + * {@link #group(String) group(name)} or {@link #group(int) group(g)</tt>}
1.697 + * respectively. For <tt>$</tt><i>g</i><tt></tt>,
1.698 + * the first number after the <tt>$</tt> is always treated as part of
1.699 + * the group reference. Subsequent numbers are incorporated into g if
1.700 + * they would form a legal group reference. Only the numerals '0'
1.701 + * through '9' are considered as potential components of the group
1.702 + * reference. If the second group matched the string <tt>"foo"</tt>, for
1.703 + * example, then passing the replacement string <tt>"$2bar"</tt> would
1.704 + * cause <tt>"foobar"</tt> to be appended to the string buffer. A dollar
1.705 + * sign (<tt>$</tt>) may be included as a literal in the replacement
1.706 + * string by preceding it with a backslash (<tt>\$</tt>).
1.707 + *
1.708 + * <p> Note that backslashes (<tt>\</tt>) and dollar signs (<tt>$</tt>) in
1.709 + * the replacement string may cause the results to be different than if it
1.710 + * were being treated as a literal replacement string. Dollar signs may be
1.711 + * treated as references to captured subsequences as described above, and
1.712 + * backslashes are used to escape literal characters in the replacement
1.713 + * string.
1.714 + *
1.715 + * <p> This method is intended to be used in a loop together with the
1.716 + * {@link #appendTail appendTail} and {@link #find find} methods. The
1.717 + * following code, for example, writes <tt>one dog two dogs in the
1.718 + * yard</tt> to the standard-output stream: </p>
1.719 + *
1.720 + * <blockquote><pre>
1.721 + * Pattern p = Pattern.compile("cat");
1.722 + * Matcher m = p.matcher("one cat two cats in the yard");
1.723 + * StringBuffer sb = new StringBuffer();
1.724 + * while (m.find()) {
1.725 + * m.appendReplacement(sb, "dog");
1.726 + * }
1.727 + * m.appendTail(sb);
1.728 + * System.out.println(sb.toString());</pre></blockquote>
1.729 + *
1.730 + * @param sb
1.731 + * The target string buffer
1.732 + *
1.733 + * @param replacement
1.734 + * The replacement string
1.735 + *
1.736 + * @return This matcher
1.737 + *
1.738 + * @throws IllegalStateException
1.739 + * If no match has yet been attempted,
1.740 + * or if the previous match operation failed
1.741 + *
1.742 + * @throws IllegalArgumentException
1.743 + * If the replacement string refers to a named-capturing
1.744 + * group that does not exist in the pattern
1.745 + *
1.746 + * @throws IndexOutOfBoundsException
1.747 + * If the replacement string refers to a capturing group
1.748 + * that does not exist in the pattern
1.749 + */
1.750 + public Matcher appendReplacement(StringBuffer sb, String replacement) {
1.751 +
1.752 + // If no match, return error
1.753 + if (first < 0)
1.754 + throw new IllegalStateException("No match available");
1.755 +
1.756 + // Process substitution string to replace group references with groups
1.757 + int cursor = 0;
1.758 + StringBuilder result = new StringBuilder();
1.759 +
1.760 + while (cursor < replacement.length()) {
1.761 + char nextChar = replacement.charAt(cursor);
1.762 + if (nextChar == '\\') {
1.763 + cursor++;
1.764 + nextChar = replacement.charAt(cursor);
1.765 + result.append(nextChar);
1.766 + cursor++;
1.767 + } else if (nextChar == '$') {
1.768 + // Skip past $
1.769 + cursor++;
1.770 + // A StringIndexOutOfBoundsException is thrown if
1.771 + // this "$" is the last character in replacement
1.772 + // string in current implementation, a IAE might be
1.773 + // more appropriate.
1.774 + nextChar = replacement.charAt(cursor);
1.775 + int refNum = -1;
1.776 + if (nextChar == '{') {
1.777 + cursor++;
1.778 + StringBuilder gsb = new StringBuilder();
1.779 + while (cursor < replacement.length()) {
1.780 + nextChar = replacement.charAt(cursor);
1.781 + if (ASCII.isLower(nextChar) ||
1.782 + ASCII.isUpper(nextChar) ||
1.783 + ASCII.isDigit(nextChar)) {
1.784 + gsb.append(nextChar);
1.785 + cursor++;
1.786 + } else {
1.787 + break;
1.788 + }
1.789 + }
1.790 + if (gsb.length() == 0)
1.791 + throw new IllegalArgumentException(
1.792 + "named capturing group has 0 length name");
1.793 + if (nextChar != '}')
1.794 + throw new IllegalArgumentException(
1.795 + "named capturing group is missing trailing '}'");
1.796 + String gname = gsb.toString();
1.797 + if (ASCII.isDigit(gname.charAt(0)))
1.798 + throw new IllegalArgumentException(
1.799 + "capturing group name {" + gname +
1.800 + "} starts with digit character");
1.801 + if (!parentPattern.namedGroups().containsKey(gname))
1.802 + throw new IllegalArgumentException(
1.803 + "No group with name {" + gname + "}");
1.804 + refNum = parentPattern.namedGroups().get(gname);
1.805 + cursor++;
1.806 + } else {
1.807 + // The first number is always a group
1.808 + refNum = (int)nextChar - '0';
1.809 + if ((refNum < 0)||(refNum > 9))
1.810 + throw new IllegalArgumentException(
1.811 + "Illegal group reference");
1.812 + cursor++;
1.813 + // Capture the largest legal group string
1.814 + boolean done = false;
1.815 + while (!done) {
1.816 + if (cursor >= replacement.length()) {
1.817 + break;
1.818 + }
1.819 + int nextDigit = replacement.charAt(cursor) - '0';
1.820 + if ((nextDigit < 0)||(nextDigit > 9)) { // not a number
1.821 + break;
1.822 + }
1.823 + int newRefNum = (refNum * 10) + nextDigit;
1.824 + if (groupCount() < newRefNum) {
1.825 + done = true;
1.826 + } else {
1.827 + refNum = newRefNum;
1.828 + cursor++;
1.829 + }
1.830 + }
1.831 + }
1.832 + // Append group
1.833 + if (start(refNum) != -1 && end(refNum) != -1)
1.834 + result.append(text, start(refNum), end(refNum));
1.835 + } else {
1.836 + result.append(nextChar);
1.837 + cursor++;
1.838 + }
1.839 + }
1.840 + // Append the intervening text
1.841 + sb.append(text, lastAppendPosition, first);
1.842 + // Append the match substitution
1.843 + sb.append(result);
1.844 +
1.845 + lastAppendPosition = last;
1.846 + return this;
1.847 + }
1.848 +
1.849 + /**
1.850 + * Implements a terminal append-and-replace step.
1.851 + *
1.852 + * <p> This method reads characters from the input sequence, starting at
1.853 + * the append position, and appends them to the given string buffer. It is
1.854 + * intended to be invoked after one or more invocations of the {@link
1.855 + * #appendReplacement appendReplacement} method in order to copy the
1.856 + * remainder of the input sequence. </p>
1.857 + *
1.858 + * @param sb
1.859 + * The target string buffer
1.860 + *
1.861 + * @return The target string buffer
1.862 + */
1.863 + public StringBuffer appendTail(StringBuffer sb) {
1.864 + sb.append(text, lastAppendPosition, getTextLength());
1.865 + return sb;
1.866 + }
1.867 +
1.868 + /**
1.869 + * Replaces every subsequence of the input sequence that matches the
1.870 + * pattern with the given replacement string.
1.871 + *
1.872 + * <p> This method first resets this matcher. It then scans the input
1.873 + * sequence looking for matches of the pattern. Characters that are not
1.874 + * part of any match are appended directly to the result string; each match
1.875 + * is replaced in the result by the replacement string. The replacement
1.876 + * string may contain references to captured subsequences as in the {@link
1.877 + * #appendReplacement appendReplacement} method.
1.878 + *
1.879 + * <p> Note that backslashes (<tt>\</tt>) and dollar signs (<tt>$</tt>) in
1.880 + * the replacement string may cause the results to be different than if it
1.881 + * were being treated as a literal replacement string. Dollar signs may be
1.882 + * treated as references to captured subsequences as described above, and
1.883 + * backslashes are used to escape literal characters in the replacement
1.884 + * string.
1.885 + *
1.886 + * <p> Given the regular expression <tt>a*b</tt>, the input
1.887 + * <tt>"aabfooaabfooabfoob"</tt>, and the replacement string
1.888 + * <tt>"-"</tt>, an invocation of this method on a matcher for that
1.889 + * expression would yield the string <tt>"-foo-foo-foo-"</tt>.
1.890 + *
1.891 + * <p> Invoking this method changes this matcher's state. If the matcher
1.892 + * is to be used in further matching operations then it should first be
1.893 + * reset. </p>
1.894 + *
1.895 + * @param replacement
1.896 + * The replacement string
1.897 + *
1.898 + * @return The string constructed by replacing each matching subsequence
1.899 + * by the replacement string, substituting captured subsequences
1.900 + * as needed
1.901 + */
1.902 + public String replaceAll(String replacement) {
1.903 + reset();
1.904 + boolean result = find();
1.905 + if (result) {
1.906 + StringBuffer sb = new StringBuffer();
1.907 + do {
1.908 + appendReplacement(sb, replacement);
1.909 + result = find();
1.910 + } while (result);
1.911 + appendTail(sb);
1.912 + return sb.toString();
1.913 + }
1.914 + return text.toString();
1.915 + }
1.916 +
1.917 + /**
1.918 + * Replaces the first subsequence of the input sequence that matches the
1.919 + * pattern with the given replacement string.
1.920 + *
1.921 + * <p> This method first resets this matcher. It then scans the input
1.922 + * sequence looking for a match of the pattern. Characters that are not
1.923 + * part of the match are appended directly to the result string; the match
1.924 + * is replaced in the result by the replacement string. The replacement
1.925 + * string may contain references to captured subsequences as in the {@link
1.926 + * #appendReplacement appendReplacement} method.
1.927 + *
1.928 + * <p>Note that backslashes (<tt>\</tt>) and dollar signs (<tt>$</tt>) in
1.929 + * the replacement string may cause the results to be different than if it
1.930 + * were being treated as a literal replacement string. Dollar signs may be
1.931 + * treated as references to captured subsequences as described above, and
1.932 + * backslashes are used to escape literal characters in the replacement
1.933 + * string.
1.934 + *
1.935 + * <p> Given the regular expression <tt>dog</tt>, the input
1.936 + * <tt>"zzzdogzzzdogzzz"</tt>, and the replacement string
1.937 + * <tt>"cat"</tt>, an invocation of this method on a matcher for that
1.938 + * expression would yield the string <tt>"zzzcatzzzdogzzz"</tt>. </p>
1.939 + *
1.940 + * <p> Invoking this method changes this matcher's state. If the matcher
1.941 + * is to be used in further matching operations then it should first be
1.942 + * reset. </p>
1.943 + *
1.944 + * @param replacement
1.945 + * The replacement string
1.946 + * @return The string constructed by replacing the first matching
1.947 + * subsequence by the replacement string, substituting captured
1.948 + * subsequences as needed
1.949 + */
1.950 + public String replaceFirst(String replacement) {
1.951 + if (replacement == null)
1.952 + throw new NullPointerException("replacement");
1.953 + reset();
1.954 + if (!find())
1.955 + return text.toString();
1.956 + StringBuffer sb = new StringBuffer();
1.957 + appendReplacement(sb, replacement);
1.958 + appendTail(sb);
1.959 + return sb.toString();
1.960 + }
1.961 +
1.962 + /**
1.963 + * Sets the limits of this matcher's region. The region is the part of the
1.964 + * input sequence that will be searched to find a match. Invoking this
1.965 + * method resets the matcher, and then sets the region to start at the
1.966 + * index specified by the <code>start</code> parameter and end at the
1.967 + * index specified by the <code>end</code> parameter.
1.968 + *
1.969 + * <p>Depending on the transparency and anchoring being used (see
1.970 + * {@link #useTransparentBounds useTransparentBounds} and
1.971 + * {@link #useAnchoringBounds useAnchoringBounds}), certain constructs such
1.972 + * as anchors may behave differently at or around the boundaries of the
1.973 + * region.
1.974 + *
1.975 + * @param start
1.976 + * The index to start searching at (inclusive)
1.977 + * @param end
1.978 + * The index to end searching at (exclusive)
1.979 + * @throws IndexOutOfBoundsException
1.980 + * If start or end is less than zero, if
1.981 + * start is greater than the length of the input sequence, if
1.982 + * end is greater than the length of the input sequence, or if
1.983 + * start is greater than end.
1.984 + * @return this matcher
1.985 + * @since 1.5
1.986 + */
1.987 + public Matcher region(int start, int end) {
1.988 + if ((start < 0) || (start > getTextLength()))
1.989 + throw new IndexOutOfBoundsException("start");
1.990 + if ((end < 0) || (end > getTextLength()))
1.991 + throw new IndexOutOfBoundsException("end");
1.992 + if (start > end)
1.993 + throw new IndexOutOfBoundsException("start > end");
1.994 + reset();
1.995 + from = start;
1.996 + to = end;
1.997 + return this;
1.998 + }
1.999 +
1.1000 + /**
1.1001 + * Reports the start index of this matcher's region. The
1.1002 + * searches this matcher conducts are limited to finding matches
1.1003 + * within {@link #regionStart regionStart} (inclusive) and
1.1004 + * {@link #regionEnd regionEnd} (exclusive).
1.1005 + *
1.1006 + * @return The starting point of this matcher's region
1.1007 + * @since 1.5
1.1008 + */
1.1009 + public int regionStart() {
1.1010 + return from;
1.1011 + }
1.1012 +
1.1013 + /**
1.1014 + * Reports the end index (exclusive) of this matcher's region.
1.1015 + * The searches this matcher conducts are limited to finding matches
1.1016 + * within {@link #regionStart regionStart} (inclusive) and
1.1017 + * {@link #regionEnd regionEnd} (exclusive).
1.1018 + *
1.1019 + * @return the ending point of this matcher's region
1.1020 + * @since 1.5
1.1021 + */
1.1022 + public int regionEnd() {
1.1023 + return to;
1.1024 + }
1.1025 +
1.1026 + /**
1.1027 + * Queries the transparency of region bounds for this matcher.
1.1028 + *
1.1029 + * <p> This method returns <tt>true</tt> if this matcher uses
1.1030 + * <i>transparent</i> bounds, <tt>false</tt> if it uses <i>opaque</i>
1.1031 + * bounds.
1.1032 + *
1.1033 + * <p> See {@link #useTransparentBounds useTransparentBounds} for a
1.1034 + * description of transparent and opaque bounds.
1.1035 + *
1.1036 + * <p> By default, a matcher uses opaque region boundaries.
1.1037 + *
1.1038 + * @return <tt>true</tt> iff this matcher is using transparent bounds,
1.1039 + * <tt>false</tt> otherwise.
1.1040 + * @see java.util.regex.Matcher#useTransparentBounds(boolean)
1.1041 + * @since 1.5
1.1042 + */
1.1043 + public boolean hasTransparentBounds() {
1.1044 + return transparentBounds;
1.1045 + }
1.1046 +
1.1047 + /**
1.1048 + * Sets the transparency of region bounds for this matcher.
1.1049 + *
1.1050 + * <p> Invoking this method with an argument of <tt>true</tt> will set this
1.1051 + * matcher to use <i>transparent</i> bounds. If the boolean
1.1052 + * argument is <tt>false</tt>, then <i>opaque</i> bounds will be used.
1.1053 + *
1.1054 + * <p> Using transparent bounds, the boundaries of this
1.1055 + * matcher's region are transparent to lookahead, lookbehind,
1.1056 + * and boundary matching constructs. Those constructs can see beyond the
1.1057 + * boundaries of the region to see if a match is appropriate.
1.1058 + *
1.1059 + * <p> Using opaque bounds, the boundaries of this matcher's
1.1060 + * region are opaque to lookahead, lookbehind, and boundary matching
1.1061 + * constructs that may try to see beyond them. Those constructs cannot
1.1062 + * look past the boundaries so they will fail to match anything outside
1.1063 + * of the region.
1.1064 + *
1.1065 + * <p> By default, a matcher uses opaque bounds.
1.1066 + *
1.1067 + * @param b a boolean indicating whether to use opaque or transparent
1.1068 + * regions
1.1069 + * @return this matcher
1.1070 + * @see java.util.regex.Matcher#hasTransparentBounds
1.1071 + * @since 1.5
1.1072 + */
1.1073 + public Matcher useTransparentBounds(boolean b) {
1.1074 + transparentBounds = b;
1.1075 + return this;
1.1076 + }
1.1077 +
1.1078 + /**
1.1079 + * Queries the anchoring of region bounds for this matcher.
1.1080 + *
1.1081 + * <p> This method returns <tt>true</tt> if this matcher uses
1.1082 + * <i>anchoring</i> bounds, <tt>false</tt> otherwise.
1.1083 + *
1.1084 + * <p> See {@link #useAnchoringBounds useAnchoringBounds} for a
1.1085 + * description of anchoring bounds.
1.1086 + *
1.1087 + * <p> By default, a matcher uses anchoring region boundaries.
1.1088 + *
1.1089 + * @return <tt>true</tt> iff this matcher is using anchoring bounds,
1.1090 + * <tt>false</tt> otherwise.
1.1091 + * @see java.util.regex.Matcher#useAnchoringBounds(boolean)
1.1092 + * @since 1.5
1.1093 + */
1.1094 + public boolean hasAnchoringBounds() {
1.1095 + return anchoringBounds;
1.1096 + }
1.1097 +
1.1098 + /**
1.1099 + * Sets the anchoring of region bounds for this matcher.
1.1100 + *
1.1101 + * <p> Invoking this method with an argument of <tt>true</tt> will set this
1.1102 + * matcher to use <i>anchoring</i> bounds. If the boolean
1.1103 + * argument is <tt>false</tt>, then <i>non-anchoring</i> bounds will be
1.1104 + * used.
1.1105 + *
1.1106 + * <p> Using anchoring bounds, the boundaries of this
1.1107 + * matcher's region match anchors such as ^ and $.
1.1108 + *
1.1109 + * <p> Without anchoring bounds, the boundaries of this
1.1110 + * matcher's region will not match anchors such as ^ and $.
1.1111 + *
1.1112 + * <p> By default, a matcher uses anchoring region boundaries.
1.1113 + *
1.1114 + * @param b a boolean indicating whether or not to use anchoring bounds.
1.1115 + * @return this matcher
1.1116 + * @see java.util.regex.Matcher#hasAnchoringBounds
1.1117 + * @since 1.5
1.1118 + */
1.1119 + public Matcher useAnchoringBounds(boolean b) {
1.1120 + anchoringBounds = b;
1.1121 + return this;
1.1122 + }
1.1123 +
1.1124 + /**
1.1125 + * <p>Returns the string representation of this matcher. The
1.1126 + * string representation of a <code>Matcher</code> contains information
1.1127 + * that may be useful for debugging. The exact format is unspecified.
1.1128 + *
1.1129 + * @return The string representation of this matcher
1.1130 + * @since 1.5
1.1131 + */
1.1132 + public String toString() {
1.1133 + StringBuilder sb = new StringBuilder();
1.1134 + sb.append("java.util.regex.Matcher");
1.1135 + sb.append("[pattern=" + pattern());
1.1136 + sb.append(" region=");
1.1137 + sb.append(regionStart() + "," + regionEnd());
1.1138 + sb.append(" lastmatch=");
1.1139 + if ((first >= 0) && (group() != null)) {
1.1140 + sb.append(group());
1.1141 + }
1.1142 + sb.append("]");
1.1143 + return sb.toString();
1.1144 + }
1.1145 +
1.1146 + /**
1.1147 + * <p>Returns true if the end of input was hit by the search engine in
1.1148 + * the last match operation performed by this matcher.
1.1149 + *
1.1150 + * <p>When this method returns true, then it is possible that more input
1.1151 + * would have changed the result of the last search.
1.1152 + *
1.1153 + * @return true iff the end of input was hit in the last match; false
1.1154 + * otherwise
1.1155 + * @since 1.5
1.1156 + */
1.1157 + public boolean hitEnd() {
1.1158 + return hitEnd;
1.1159 + }
1.1160 +
1.1161 + /**
1.1162 + * <p>Returns true if more input could change a positive match into a
1.1163 + * negative one.
1.1164 + *
1.1165 + * <p>If this method returns true, and a match was found, then more
1.1166 + * input could cause the match to be lost. If this method returns false
1.1167 + * and a match was found, then more input might change the match but the
1.1168 + * match won't be lost. If a match was not found, then requireEnd has no
1.1169 + * meaning.
1.1170 + *
1.1171 + * @return true iff more input could change a positive match into a
1.1172 + * negative one.
1.1173 + * @since 1.5
1.1174 + */
1.1175 + public boolean requireEnd() {
1.1176 + return requireEnd;
1.1177 + }
1.1178 +
1.1179 + /**
1.1180 + * Initiates a search to find a Pattern within the given bounds.
1.1181 + * The groups are filled with default values and the match of the root
1.1182 + * of the state machine is called. The state machine will hold the state
1.1183 + * of the match as it proceeds in this matcher.
1.1184 + *
1.1185 + * Matcher.from is not set here, because it is the "hard" boundary
1.1186 + * of the start of the search which anchors will set to. The from param
1.1187 + * is the "soft" boundary of the start of the search, meaning that the
1.1188 + * regex tries to match at that index but ^ won't match there. Subsequent
1.1189 + * calls to the search methods start at a new "soft" boundary which is
1.1190 + * the end of the previous match.
1.1191 + */
1.1192 + boolean search(int from) {
1.1193 + this.hitEnd = false;
1.1194 + this.requireEnd = false;
1.1195 + from = from < 0 ? 0 : from;
1.1196 + this.first = from;
1.1197 + this.oldLast = oldLast < 0 ? from : oldLast;
1.1198 + for (int i = 0; i < groups.length; i++)
1.1199 + groups[i] = -1;
1.1200 + acceptMode = NOANCHOR;
1.1201 + boolean result = parentPattern.root.match(this, from, text);
1.1202 + if (!result)
1.1203 + this.first = -1;
1.1204 + this.oldLast = this.last;
1.1205 + return result;
1.1206 + }
1.1207 +
1.1208 + /**
1.1209 + * Initiates a search for an anchored match to a Pattern within the given
1.1210 + * bounds. The groups are filled with default values and the match of the
1.1211 + * root of the state machine is called. The state machine will hold the
1.1212 + * state of the match as it proceeds in this matcher.
1.1213 + */
1.1214 + boolean match(int from, int anchor) {
1.1215 + this.hitEnd = false;
1.1216 + this.requireEnd = false;
1.1217 + from = from < 0 ? 0 : from;
1.1218 + this.first = from;
1.1219 + this.oldLast = oldLast < 0 ? from : oldLast;
1.1220 + for (int i = 0; i < groups.length; i++)
1.1221 + groups[i] = -1;
1.1222 + acceptMode = anchor;
1.1223 + boolean result = parentPattern.matchRoot.match(this, from, text);
1.1224 + if (!result)
1.1225 + this.first = -1;
1.1226 + this.oldLast = this.last;
1.1227 + return result;
1.1228 + }
1.1229 +
1.1230 + /**
1.1231 + * Returns the end index of the text.
1.1232 + *
1.1233 + * @return the index after the last character in the text
1.1234 + */
1.1235 + int getTextLength() {
1.1236 + return text.length();
1.1237 + }
1.1238 +
1.1239 + /**
1.1240 + * Generates a String from this Matcher's input in the specified range.
1.1241 + *
1.1242 + * @param beginIndex the beginning index, inclusive
1.1243 + * @param endIndex the ending index, exclusive
1.1244 + * @return A String generated from this Matcher's input
1.1245 + */
1.1246 + CharSequence getSubSequence(int beginIndex, int endIndex) {
1.1247 + return text.subSequence(beginIndex, endIndex);
1.1248 + }
1.1249 +
1.1250 + /**
1.1251 + * Returns this Matcher's input character at index i.
1.1252 + *
1.1253 + * @return A char from the specified index
1.1254 + */
1.1255 + char charAt(int i) {
1.1256 + return text.charAt(i);
1.1257 + }
1.1258 +
1.1259 +}