1 /*
2 * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.util;
27
28 import java.nio.file.Path;
29 import java.nio.file.Files;
30 import java.util.regex.*;
31 import java.io.*;
32 import java.math.*;
33 import java.nio.*;
34 import java.nio.channels.*;
35 import java.nio.charset.*;
36 import java.text.*;
37 import java.util.Locale;
38
39 import sun.misc.LRUCache;
40
41 /**
42 * A simple text scanner which can parse primitive types and strings using
43 * regular expressions.
44 *
45 * <p>A {@code Scanner} breaks its input into tokens using a
46 * delimiter pattern, which by default matches whitespace. The resulting
47 * tokens may then be converted into values of different types using the
48 * various {@code next} methods.
49 *
50 * <p>For example, this code allows a user to read a number from
51 * {@code System.in}:
52 * <blockquote><pre>{@code
53 * Scanner sc = new Scanner(System.in);
54 * int i = sc.nextInt();
55 * }</pre></blockquote>
56 *
57 * <p>As another example, this code allows {@code long} types to be
79 * <blockquote><pre>{@code
80 * 1
81 * 2
82 * red
83 * blue
84 * }</pre></blockquote>
85 *
86 * <p>The same output can be generated with this code, which uses a regular
87 * expression to parse all four tokens at once:
88 * <blockquote><pre>{@code
89 * String input = "1 fish 2 fish red fish blue fish";
90 * Scanner s = new Scanner(input);
91 * s.findInLine("(\\d+) fish (\\d+) fish (\\w+) fish (\\w+)");
92 * MatchResult result = s.match();
93 * for (int i=1; i<=result.groupCount(); i++)
94 * System.out.println(result.group(i));
95 * s.close();
96 * }</pre></blockquote>
97 *
98 * <p>The <a name="default-delimiter">default whitespace delimiter</a> used
99 * by a scanner is as recognized by {@link java.lang.Character}.{@link
100 * java.lang.Character#isWhitespace(char) isWhitespace}. The {@link #reset}
101 * method will reset the value of the scanner's delimiter to the default
102 * whitespace delimiter regardless of whether it was previously changed.
103 *
104 * <p>A scanning operation may block waiting for input.
105 *
106 * <p>The {@link #next} and {@link #hasNext} methods and their
107 * primitive-type companion methods (such as {@link #nextInt} and
108 * {@link #hasNextInt}) first skip any input that matches the delimiter
109 * pattern, and then attempt to return the next token. Both {@code hasNext}
110 * and {@code next} methods may block waiting for further input. Whether a
111 * {@code hasNext} method blocks has no connection to whether or not its
112 * associated {@code next} method will block.
113 *
114 * <p> The {@link #findInLine}, {@link #findWithinHorizon}, and {@link #skip}
115 * methods operate independently of the delimiter pattern. These methods will
116 * attempt to match the specified pattern with no regard to delimiters in the
117 * input and thus can be used in special circumstances where delimiters are
118 * not relevant. These methods may block waiting for more input.
119 *
120 * <p>When a scanner throws an {@link InputMismatchException}, the scanner
121 * will not pass the token that caused the exception, so that it may be
122 * retrieved or skipped via some other method.
123 *
124 * <p>Depending upon the type of delimiting pattern, empty tokens may be
125 * returned. For example, the pattern {@code "\\s+"} will return no empty
126 * tokens since it matches multiple instances of the delimiter. The delimiting
127 * pattern {@code "\\s"} could return empty tokens since it only passes one
128 * space at a time.
129 *
130 * <p> A scanner can read text from any object which implements the {@link
131 * java.lang.Readable} interface. If an invocation of the underlying
132 * readable's {@link java.lang.Readable#read} method throws an {@link
133 * java.io.IOException} then the scanner assumes that the end of the input
134 * has been reached. The most recent {@code IOException} thrown by the
135 * underlying readable can be retrieved via the {@link #ioException} method.
136 *
137 * <p>When a {@code Scanner} is closed, it will close its input source
138 * if the source implements the {@link java.io.Closeable} interface.
139 *
140 * <p>A {@code Scanner} is not safe for multithreaded use without
141 * external synchronization.
142 *
143 * <p>Unless otherwise mentioned, passing a {@code null} parameter into
144 * any method of a {@code Scanner} will cause a
145 * {@code NullPointerException} to be thrown.
146 *
147 * <p>A scanner will default to interpreting numbers as decimal unless a
148 * different radix has been set by using the {@link #useRadix} method. The
149 * {@link #reset} method will reset the value of the scanner's radix to
150 * {@code 10} regardless of whether it was previously changed.
151 *
152 * <h3> <a name="localized-numbers">Localized numbers</a> </h3>
153 *
154 * <p> An instance of this class is capable of scanning numbers in the standard
155 * formats as well as in the formats of the scanner's locale. A scanner's
156 * <a name="initial-locale">initial locale </a>is the value returned by the {@link
157 * java.util.Locale#getDefault(Locale.Category)
158 * Locale.getDefault(Locale.Category.FORMAT)} method; it may be changed via the {@link
159 * #useLocale} method. The {@link #reset} method will reset the value of the
160 * scanner's locale to the initial locale regardless of whether it was
161 * previously changed.
162 *
163 * <p>The localized formats are defined in terms of the following parameters,
164 * which for a particular locale are taken from that locale's {@link
165 * java.text.DecimalFormat DecimalFormat} object, {@code df}, and its and
166 * {@link java.text.DecimalFormatSymbols DecimalFormatSymbols} object,
167 * {@code dfs}.
168 *
169 * <blockquote><dl>
170 * <dt><i>LocalGroupSeparator </i>
171 * <dd>The character used to separate thousands groups,
172 * <i>i.e.,</i> {@code dfs.}{@link
173 * java.text.DecimalFormatSymbols#getGroupingSeparator
174 * getGroupingSeparator()}
175 * <dt><i>LocalDecimalSeparator </i>
176 * <dd>The character used for the decimal point,
177 * <i>i.e.,</i> {@code dfs.}{@link
178 * java.text.DecimalFormatSymbols#getDecimalSeparator
179 * getDecimalSeparator()}
357 // The default radix for this scanner
358 private int defaultRadix = 10;
359
360 // The locale used by this scanner
361 private Locale locale = null;
362
363 // A cache of the last few recently used Patterns
364 private LRUCache<String,Pattern> patternCache =
365 new LRUCache<String,Pattern>(7) {
366 protected Pattern create(String s) {
367 return Pattern.compile(s);
368 }
369 protected boolean hasName(Pattern p, String s) {
370 return p.pattern().equals(s);
371 }
372 };
373
374 // A holder of the last IOException encountered
375 private IOException lastException;
376
377 // A pattern for java whitespace
378 private static Pattern WHITESPACE_PATTERN = Pattern.compile(
379 "\\p{javaWhitespace}+");
380
381 // A pattern for any token
382 private static Pattern FIND_ANY_PATTERN = Pattern.compile("(?s).*");
383
384 // A pattern for non-ASCII digits
385 private static Pattern NON_ASCII_DIGIT = Pattern.compile(
386 "[\\p{javaDigit}&&[^0-9]]");
387
388 // Fields and methods to support scanning primitive types
389
390 /**
391 * Locale dependent values used to scan numbers
392 */
393 private String groupSeparator = "\\,";
394 private String decimalSeparator = "\\.";
395 private String nanString = "NaN";
396 private String infinityString = "Infinity";
978 }
979 // Last token; Match the pattern here or throw
980 matcher.usePattern(pattern);
981 matcher.region(position, buf.limit());
982 if (matcher.matches()) {
983 String s = matcher.group();
984 position = matcher.end();
985 return s;
986 }
987 // Last piece does not match
988 return null;
989 }
990
991 // There is a partial token in the buffer; must read more
992 // to complete it
993 needInput = true;
994 return null;
995 }
996
997 // Finds the specified pattern in the buffer up to horizon.
998 // Returns a match for the specified input pattern.
999 private String findPatternInBuffer(Pattern pattern, int horizon) {
1000 matchValid = false;
1001 matcher.usePattern(pattern);
1002 int bufferLimit = buf.limit();
1003 int horizonLimit = -1;
1004 int searchLimit = bufferLimit;
1005 if (horizon > 0) {
1006 horizonLimit = position + horizon;
1007 if (horizonLimit < bufferLimit)
1008 searchLimit = horizonLimit;
1009 }
1010 matcher.region(position, searchLimit);
1011 if (matcher.find()) {
1012 if (matcher.hitEnd() && (!sourceClosed)) {
1013 // The match may be longer if didn't hit horizon or real end
1014 if (searchLimit != horizonLimit) {
1015 // Hit an artificial end; try to extend the match
1016 needInput = true;
1017 return null;
1018 }
1019 // The match could go away depending on what is next
1020 if ((searchLimit == horizonLimit) && matcher.requireEnd()) {
1021 // Rare case: we hit the end of input and it happens
1022 // that it is at the horizon and the end of input is
1023 // required for the match.
1024 needInput = true;
1025 return null;
1026 }
1027 }
1028 // Did not hit end, or hit real end, or hit horizon
1029 position = matcher.end();
1030 return matcher.group();
1031 }
1032
1033 if (sourceClosed)
1034 return null;
1035
1036 // If there is no specified horizon, or if we have not searched
1037 // to the specified horizon yet, get more input
1038 if ((horizon == 0) || (searchLimit != horizonLimit))
1039 needInput = true;
1040 return null;
1041 }
1042
1043 // Returns a match for the specified input pattern anchored at
1044 // the current position
1045 private String matchPatternInBuffer(Pattern pattern) {
1046 matchValid = false;
1047 matcher.usePattern(pattern);
1048 matcher.region(position, buf.limit());
1049 if (matcher.lookingAt()) {
1050 if (matcher.hitEnd() && (!sourceClosed)) {
1051 // Get more input and try again
1052 needInput = true;
1053 return null;
1054 }
1055 position = matcher.end();
1056 return matcher.group();
1057 }
1058
1059 if (sourceClosed)
1060 return null;
1061
1062 // Read more to find pattern
1063 needInput = true;
1064 return null;
1065 }
1066
1067 // Throws if the scanner is closed
1068 private void ensureOpen() {
1069 if (closed)
1070 throw new IllegalStateException("Scanner closed");
1071 }
1072
1073 // Public methods
1074
1075 /**
1076 * Closes this scanner.
1077 *
1078 * <p> If this scanner has not yet been closed then if its underlying
1079 * {@linkplain java.lang.Readable readable} also implements the {@link
1080 * java.io.Closeable} interface then the readable's {@code close} method
1081 * will be invoked. If this scanner is already closed then invoking this
1082 * method will have no effect.
1083 *
1084 * <p>Attempting to perform search operations after a scanner has
1111 return lastException;
1112 }
1113
1114 /**
1115 * Returns the {@code Pattern} this {@code Scanner} is currently
1116 * using to match delimiters.
1117 *
1118 * @return this scanner's delimiting pattern.
1119 */
1120 public Pattern delimiter() {
1121 return delimPattern;
1122 }
1123
1124 /**
1125 * Sets this scanner's delimiting pattern to the specified pattern.
1126 *
1127 * @param pattern A delimiting pattern
1128 * @return this scanner
1129 */
1130 public Scanner useDelimiter(Pattern pattern) {
1131 delimPattern = pattern;
1132 return this;
1133 }
1134
1135 /**
1136 * Sets this scanner's delimiting pattern to a pattern constructed from
1137 * the specified {@code String}.
1138 *
1139 * <p> An invocation of this method of the form
1140 * {@code useDelimiter(pattern)} behaves in exactly the same way as the
1141 * invocation {@code useDelimiter(Pattern.compile(pattern))}.
1142 *
1143 * <p> Invoking the {@link #reset} method will set the scanner's delimiter
1144 * to the <a href= "#default-delimiter">default</a>.
1145 *
1146 * @param pattern A string specifying a delimiting pattern
1147 * @return this scanner
1148 */
1149 public Scanner useDelimiter(String pattern) {
1150 delimPattern = patternCache.forName(pattern);
1151 return this;
1152 }
1153
1154 /**
1155 * Returns this scanner's locale.
1156 *
1157 * <p>A scanner's locale affects many elements of its default
1158 * primitive matching regular expressions; see
1159 * <a href= "#localized-numbers">localized numbers</a> above.
1160 *
1161 * @return this scanner's locale
1162 */
1163 public Locale locale() {
1164 return this.locale;
1165 }
1166
1167 /**
1168 * Sets this scanner's locale to the specified locale.
1169 *
1170 * <p>A scanner's locale affects many elements of its default
1171 * primitive matching regular expressions; see
1172 * <a href= "#localized-numbers">localized numbers</a> above.
1173 *
1174 * <p>Invoking the {@link #reset} method will set the scanner's locale to
1175 * the <a href= "#initial-locale">initial locale</a>.
1176 *
1177 * @param locale A string specifying the locale to use
1178 * @return this scanner
1179 */
1180 public Scanner useLocale(Locale locale) {
1181 if (locale.equals(this.locale))
1182 return this;
1183
1184 this.locale = locale;
1185 DecimalFormat df =
1186 (DecimalFormat)NumberFormat.getNumberInstance(locale);
1187 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(locale);
1188
1189 // These must be literalized to avoid collision with regex
1190 // metacharacters such as dot or parenthesis
1191 groupSeparator = "\\" + dfs.getGroupingSeparator();
1192 decimalSeparator = "\\" + dfs.getDecimalSeparator();
1193
1194 // Quoting the nonzero length locale-specific things
1195 // to avoid potential conflict with metacharacters
1196 nanString = "\\Q" + dfs.getNaN() + "\\E";
1197 infinityString = "\\Q" + dfs.getInfinity() + "\\E";
1198 positivePrefix = df.getPositivePrefix();
1199 if (positivePrefix.length() > 0)
1200 positivePrefix = "\\Q" + positivePrefix + "\\E";
1201 negativePrefix = df.getNegativePrefix();
1202 if (negativePrefix.length() > 0)
1203 negativePrefix = "\\Q" + negativePrefix + "\\E";
1219 /**
1220 * Returns this scanner's default radix.
1221 *
1222 * <p>A scanner's radix affects elements of its default
1223 * number matching regular expressions; see
1224 * <a href= "#localized-numbers">localized numbers</a> above.
1225 *
1226 * @return the default radix of this scanner
1227 */
1228 public int radix() {
1229 return this.defaultRadix;
1230 }
1231
1232 /**
1233 * Sets this scanner's default radix to the specified radix.
1234 *
1235 * <p>A scanner's radix affects elements of its default
1236 * number matching regular expressions; see
1237 * <a href= "#localized-numbers">localized numbers</a> above.
1238 *
1239 * <p>If the radix is less than {@code Character.MIN_RADIX}
1240 * or greater than {@code Character.MAX_RADIX}, then an
1241 * {@code IllegalArgumentException} is thrown.
1242 *
1243 * <p>Invoking the {@link #reset} method will set the scanner's radix to
1244 * {@code 10}.
1245 *
1246 * @param radix The radix to use when scanning numbers
1247 * @return this scanner
1248 * @throws IllegalArgumentException if radix is out of range
1249 */
1250 public Scanner useRadix(int radix) {
1251 if ((radix < Character.MIN_RADIX) || (radix > Character.MAX_RADIX))
1252 throw new IllegalArgumentException("radix:"+radix);
1253
1254 if (this.defaultRadix == radix)
1255 return this;
1256 this.defaultRadix = radix;
1257 // Force rebuilding and recompilation of radix dependent patterns
1258 integerPattern = null;
1259 return this;
1260 }
1261
1262 // The next operation should occur in the specified radix but
1263 // the default is left untouched.
1264 private void setRadix(int radix) {
1265 if (this.radix != radix) {
1266 // Force rebuilding and recompilation of radix dependent patterns
1267 integerPattern = null;
1268 this.radix = radix;
1269 }
1270 }
1271
1272 /**
1273 * Returns the match result of the last scanning operation performed
1274 * by this scanner. This method throws {@code IllegalStateException}
1275 * if no match has been performed, or if the last match was
1276 * not successful.
1277 *
1278 * <p>The various {@code next}methods of {@code Scanner}
1279 * make a match result available if they complete without throwing an
1280 * exception. For instance, after an invocation of the {@link #nextInt}
1281 * method that returned an int, this method returns a
1282 * {@code MatchResult} for the search of the
1283 * <a href="#Integer-regex"><i>Integer</i></a> regular expression
1284 * defined above. Similarly the {@link #findInLine},
1285 * {@link #findWithinHorizon}, and {@link #skip} methods will make a
1286 * match available if they succeed.
1287 *
1288 * @return a match result for the last match operation
1289 * @throws IllegalStateException If no match result is available
1290 */
1291 public MatchResult match() {
1292 if (!matchValid)
1293 throw new IllegalStateException("No match result available");
1294 return matcher.toMatchResult();
1295 }
1296
1297 /**
1298 * <p>Returns the string representation of this {@code Scanner}. The
1299 * string representation of a {@code Scanner} contains information
1300 * that may be useful for debugging. The exact format is unspecified.
1301 *
1302 * @return The string representation of this scanner
1303 */
1304 public String toString() {
1305 StringBuilder sb = new StringBuilder();
1306 sb.append("java.util.Scanner");
1316 sb.append("[negative prefix=" + negativePrefix + "]");
1317 sb.append("[positive suffix=" + positiveSuffix + "]");
1318 sb.append("[negative suffix=" + negativeSuffix + "]");
1319 sb.append("[NaN string=" + nanString + "]");
1320 sb.append("[infinity string=" + infinityString + "]");
1321 return sb.toString();
1322 }
1323
1324 /**
1325 * Returns true if this scanner has another token in its input.
1326 * This method may block while waiting for input to scan.
1327 * The scanner does not advance past any input.
1328 *
1329 * @return true if and only if this scanner has another token
1330 * @throws IllegalStateException if this scanner is closed
1331 * @see java.util.Iterator
1332 */
1333 public boolean hasNext() {
1334 ensureOpen();
1335 saveState();
1336 while (!sourceClosed) {
1337 if (hasTokenInBuffer())
1338 return revertState(true);
1339 readInput();
1340 }
1341 boolean result = hasTokenInBuffer();
1342 return revertState(result);
1343 }
1344
1345 /**
1346 * Finds and returns the next complete token from this scanner.
1347 * A complete token is preceded and followed by input that matches
1348 * the delimiter pattern. This method may block while waiting for input
1349 * to scan, even if a previous invocation of {@link #hasNext} returned
1350 * {@code true}.
1351 *
1352 * @return the next token
1353 * @throws NoSuchElementException if no more tokens are available
1354 * @throws IllegalStateException if this scanner is closed
1355 * @see java.util.Iterator
1356 */
1357 public String next() {
1358 ensureOpen();
1359 clearCaches();
1360
1361 while (true) {
1362 String token = getCompleteTokenInBuffer(null);
1363 if (token != null) {
1364 matchValid = true;
1365 skipped = false;
1366 return token;
1367 }
1368 if (needInput)
1369 readInput();
1370 else
1371 throwFor();
1372 }
1373 }
1374
1375 /**
1376 * The remove operation is not supported by this implementation of
1377 * {@code Iterator}.
1378 *
1379 * @throws UnsupportedOperationException if this method is invoked.
1418 return next(patternCache.forName(pattern));
1419 }
1420
1421 /**
1422 * Returns true if the next complete token matches the specified pattern.
1423 * A complete token is prefixed and postfixed by input that matches
1424 * the delimiter pattern. This method may block while waiting for input.
1425 * The scanner does not advance past any input.
1426 *
1427 * @param pattern the pattern to scan for
1428 * @return true if and only if this scanner has another token matching
1429 * the specified pattern
1430 * @throws IllegalStateException if this scanner is closed
1431 */
1432 public boolean hasNext(Pattern pattern) {
1433 ensureOpen();
1434 if (pattern == null)
1435 throw new NullPointerException();
1436 hasNextPattern = null;
1437 saveState();
1438
1439 while (true) {
1440 if (getCompleteTokenInBuffer(pattern) != null) {
1441 matchValid = true;
1442 cacheResult();
1443 return revertState(true);
1444 }
1445 if (needInput)
1446 readInput();
1447 else
1448 return revertState(false);
1449 }
1450 }
1451
1452 /**
1453 * Returns the next token if it matches the specified pattern. This
1454 * method may block while waiting for input to scan, even if a previous
1455 * invocation of {@link #hasNext(Pattern)} returned {@code true}.
1456 * If the match is successful, the scanner advances past the input that
1457 * matched the pattern.
1458 *
1459 * @param pattern the pattern to scan for
1460 * @return the next token
1461 * @throws NoSuchElementException if no more tokens are available
1462 * @throws IllegalStateException if this scanner is closed
1463 */
1464 public String next(Pattern pattern) {
1465 ensureOpen();
1466 if (pattern == null)
1467 throw new NullPointerException();
1468
1469 // Did we already find this pattern?
1470 if (hasNextPattern == pattern)
1471 return getCachedResult();
1472 clearCaches();
1473
1474 // Search for the pattern
1475 while (true) {
1476 String token = getCompleteTokenInBuffer(pattern);
1477 if (token != null) {
1478 matchValid = true;
1479 skipped = false;
1480 return token;
1481 }
1482 if (needInput)
1483 readInput();
1484 else
1485 throwFor();
1486 }
1487 }
1488
1489 /**
1490 * Returns true if there is another line in the input of this scanner.
1491 * This method may block while waiting for input. The scanner does not
1492 * advance past any input.
1493 *
1494 * @return true if and only if this scanner has another line of input
1495 * @throws IllegalStateException if this scanner is closed
1496 */
1497 public boolean hasNextLine() {
1498 saveState();
1499
1500 String result = findWithinHorizon(linePattern(), 0);
1501 if (result != null) {
1502 MatchResult mr = this.match();
1503 String lineSep = mr.group(1);
1504 if (lineSep != null) {
1505 result = result.substring(0, result.length() -
1506 lineSep.length());
1507 cacheResult(result);
1508
1509 } else {
1510 cacheResult();
1511 }
1512 }
1513 revertState();
1514 return (result != null);
1515 }
1516
1517 /**
1518 * Advances this scanner past the current line and returns the input
1519 * that was skipped.
1520 *
1521 * This method returns the rest of the current line, excluding any line
1522 * separator at the end. The position is set to the beginning of the next
1523 * line.
1524 *
1525 * <p>Since this method continues to search through the input looking
1526 * for a line separator, it may buffer all of the input searching for
1527 * the line to skip if no line separators are present.
1528 *
1529 * @return the line that was skipped
1530 * @throws NoSuchElementException if no line was found
1531 * @throws IllegalStateException if this scanner is closed
1532 */
1533 public String nextLine() {
1534 if (hasNextPattern == linePattern())
1535 return getCachedResult();
1536 clearCaches();
1537
1538 String result = findWithinHorizon(linePattern, 0);
1539 if (result == null)
1540 throw new NoSuchElementException("No line found");
1541 MatchResult mr = this.match();
1542 String lineSep = mr.group(1);
1543 if (lineSep != null)
1544 result = result.substring(0, result.length() - lineSep.length());
1545 if (result == null)
1546 throw new NoSuchElementException();
1547 else
1548 return result;
1549 }
1550
1551 // Public methods that ignore delimiters
1552
1553 /**
1572 * scanner advances past the input that matched and returns the string that
1573 * matched the pattern.
1574 * If no such pattern is detected in the input up to the next line
1575 * separator, then {@code null} is returned and the scanner's
1576 * position is unchanged. This method may block waiting for input that
1577 * matches the pattern.
1578 *
1579 * <p>Since this method continues to search through the input looking
1580 * for the specified pattern, it may buffer all of the input searching for
1581 * the desired token if no line separators are present.
1582 *
1583 * @param pattern the pattern to scan for
1584 * @return the text that matched the specified pattern
1585 * @throws IllegalStateException if this scanner is closed
1586 */
1587 public String findInLine(Pattern pattern) {
1588 ensureOpen();
1589 if (pattern == null)
1590 throw new NullPointerException();
1591 clearCaches();
1592 // Expand buffer to include the next newline or end of input
1593 int endPosition = 0;
1594 saveState();
1595 while (true) {
1596 String token = findPatternInBuffer(separatorPattern(), 0);
1597 if (token != null) {
1598 endPosition = matcher.start();
1599 break; // up to next newline
1600 }
1601 if (needInput) {
1602 readInput();
1603 } else {
1604 endPosition = buf.limit();
1605 break; // up to end of input
1606 }
1607 }
1608 revertState();
1609 int horizonForLine = endPosition - position;
1610 // If there is nothing between the current pos and the next
1611 // newline simply return null, invoking findWithinHorizon
1612 // with "horizon=0" will scan beyond the line bound.
1613 if (horizonForLine == 0)
1614 return null;
1615 // Search for the pattern
1616 return findWithinHorizon(pattern, horizonForLine);
1617 }
1618
1619 /**
1620 * Attempts to find the next occurrence of a pattern constructed from the
1621 * specified string, ignoring delimiters.
1622 *
1623 * <p>An invocation of this method of the form
1624 * {@code findWithinHorizon(pattern)} behaves in exactly the same way as
1625 * the invocation
1626 * {@code findWithinHorizon(Pattern.compile(pattern, horizon))}.
1627 *
1628 * @param pattern a string specifying the pattern to search for
1629 * @param horizon the search horizon
1630 * @return the text that matched the specified pattern
1631 * @throws IllegalStateException if this scanner is closed
1632 * @throws IllegalArgumentException if horizon is negative
1633 */
1634 public String findWithinHorizon(String pattern, int horizon) {
1635 return findWithinHorizon(patternCache.forName(pattern), horizon);
1636 }
1637
1638 /**
1639 * Attempts to find the next occurrence of the specified pattern.
1640 *
1641 * <p>This method searches through the input up to the specified
1642 * search horizon, ignoring delimiters. If the pattern is found the
1643 * scanner advances past the input that matched and returns the string
1644 * that matched the pattern. If no such pattern is detected then the
1645 * null is returned and the scanner's position remains unchanged. This
1646 * method may block waiting for input that matches the pattern.
1656 * this method continues to search through the input looking for the
1657 * specified pattern without bound. In this case it may buffer all of
1658 * the input searching for the pattern.
1659 *
1660 * <p>If horizon is negative, then an IllegalArgumentException is
1661 * thrown.
1662 *
1663 * @param pattern the pattern to scan for
1664 * @param horizon the search horizon
1665 * @return the text that matched the specified pattern
1666 * @throws IllegalStateException if this scanner is closed
1667 * @throws IllegalArgumentException if horizon is negative
1668 */
1669 public String findWithinHorizon(Pattern pattern, int horizon) {
1670 ensureOpen();
1671 if (pattern == null)
1672 throw new NullPointerException();
1673 if (horizon < 0)
1674 throw new IllegalArgumentException("horizon < 0");
1675 clearCaches();
1676
1677 // Search for the pattern
1678 while (true) {
1679 String token = findPatternInBuffer(pattern, horizon);
1680 if (token != null) {
1681 matchValid = true;
1682 return token;
1683 }
1684 if (needInput)
1685 readInput();
1686 else
1687 break; // up to end of input
1688 }
1689 return null;
1690 }
1691
1692 /**
1693 * Skips input that matches the specified pattern, ignoring delimiters.
1694 * This method will skip input if an anchored match of the specified
1695 * pattern succeeds.
1696 *
1697 * <p>If a match to the specified pattern is not found at the
1698 * current position, then no input is skipped and a
1699 * {@code NoSuchElementException} is thrown.
1700 *
1701 * <p>Since this method seeks to match the specified pattern starting at
1702 * the scanner's current position, patterns that can match a lot of
1703 * input (".*", for example) may cause the scanner to buffer a large
1704 * amount of input.
1705 *
1706 * <p>Note that it is possible to skip something without risking a
1707 * {@code NoSuchElementException} by using a pattern that can
1708 * match nothing, e.g., {@code sc.skip("[ \t]*")}.
1709 *
1710 * @param pattern a string specifying the pattern to skip over
1711 * @return this scanner
1712 * @throws NoSuchElementException if the specified pattern is not found
1713 * @throws IllegalStateException if this scanner is closed
1714 */
1715 public Scanner skip(Pattern pattern) {
1716 ensureOpen();
1717 if (pattern == null)
1718 throw new NullPointerException();
1719 clearCaches();
1720
1721 // Search for the pattern
1722 while (true) {
1723 String token = matchPatternInBuffer(pattern);
1724 if (token != null) {
1725 matchValid = true;
1726 position = matcher.end();
1727 return this;
1728 }
1729 if (needInput)
1730 readInput();
1731 else
1732 throw new NoSuchElementException();
1733 }
1734 }
1735
1736 /**
1737 * Skips input that matches a pattern constructed from the specified
1738 * string.
1739 *
1740 * <p> An invocation of this method of the form {@code skip(pattern)}
1741 * behaves in exactly the same way as the invocation
1742 * {@code skip(Pattern.compile(pattern))}.
1743 *
1744 * @param pattern a string specifying the pattern to skip over
1915 setRadix(radix);
1916 boolean result = hasNext(integerPattern());
1917 if (result) { // Cache it
1918 try {
1919 String s = (matcher.group(SIMPLE_GROUP_INDEX) == null) ?
1920 processIntegerToken(hasNextResult) :
1921 hasNextResult;
1922 typeCache = Short.parseShort(s, radix);
1923 } catch (NumberFormatException nfe) {
1924 result = false;
1925 }
1926 }
1927 return result;
1928 }
1929
1930 /**
1931 * Scans the next token of the input as a {@code short}.
1932 *
1933 * <p> An invocation of this method of the form
1934 * {@code nextShort()} behaves in exactly the same way as the
1935 * invocation {@code nextShort(radix)}, where {@code radix}
1936 * is the default radix of this scanner.
1937 *
1938 * @return the {@code short} scanned from the input
1939 * @throws InputMismatchException
1940 * if the next token does not match the <i>Integer</i>
1941 * regular expression, or is out of range
1942 * @throws NoSuchElementException if input is exhausted
1943 * @throws IllegalStateException if this scanner is closed
1944 */
1945 public short nextShort() {
1946 return nextShort(defaultRadix);
1947 }
1948
1949 /**
1950 * Scans the next token of the input as a {@code short}.
1951 * This method will throw {@code InputMismatchException}
1952 * if the next token cannot be translated into a valid short value as
1953 * described below. If the translation is successful, the scanner advances
1954 * past the input that matched.
1955 *
2573 BigDecimal val = (BigDecimal)typeCache;
2574 useTypeCache();
2575 return val;
2576 }
2577 setRadix(10);
2578 clearCaches();
2579 // Search for next float
2580 try {
2581 String s = processFloatToken(next(decimalPattern()));
2582 return new BigDecimal(s);
2583 } catch (NumberFormatException nfe) {
2584 position = matcher.start(); // don't skip bad token
2585 throw new InputMismatchException(nfe.getMessage());
2586 }
2587 }
2588
2589 /**
2590 * Resets this scanner.
2591 *
2592 * <p> Resetting a scanner discards all of its explicit state
2593 * information which may have been changed by invocations of {@link
2594 * #useDelimiter}, {@link #useLocale}, or {@link #useRadix}.
2595 *
2596 * <p> An invocation of this method of the form
2597 * {@code scanner.reset()} behaves in exactly the same way as the
2598 * invocation
2599 *
2600 * <blockquote><pre>{@code
2601 * scanner.useDelimiter("\\p{javaWhitespace}+")
2602 * .useLocale(Locale.getDefault(Locale.Category.FORMAT))
2603 * .useRadix(10);
2604 * }</pre></blockquote>
2605 *
2606 * @return this scanner
2607 *
2608 * @since 1.6
2609 */
2610 public Scanner reset() {
2611 delimPattern = WHITESPACE_PATTERN;
2612 useLocale(Locale.getDefault(Locale.Category.FORMAT));
2613 useRadix(10);
2614 clearCaches();
2615 return this;
2616 }
2617 }
|
1 /*
2 * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.util;
27
28 import java.io.*;
29 import java.math.*;
30 import java.nio.*;
31 import java.nio.channels.*;
32 import java.nio.charset.*;
33 import java.nio.file.Path;
34 import java.nio.file.Files;
35 import java.text.*;
36 import java.util.function.Consumer;
37 import java.util.regex.*;
38 import java.util.stream.Stream;
39 import java.util.stream.StreamSupport;
40
41 import sun.misc.LRUCache;
42
43 /**
44 * A simple text scanner which can parse primitive types and strings using
45 * regular expressions.
46 *
47 * <p>A {@code Scanner} breaks its input into tokens using a
48 * delimiter pattern, which by default matches whitespace. The resulting
49 * tokens may then be converted into values of different types using the
50 * various {@code next} methods.
51 *
52 * <p>For example, this code allows a user to read a number from
53 * {@code System.in}:
54 * <blockquote><pre>{@code
55 * Scanner sc = new Scanner(System.in);
56 * int i = sc.nextInt();
57 * }</pre></blockquote>
58 *
59 * <p>As another example, this code allows {@code long} types to be
81 * <blockquote><pre>{@code
82 * 1
83 * 2
84 * red
85 * blue
86 * }</pre></blockquote>
87 *
88 * <p>The same output can be generated with this code, which uses a regular
89 * expression to parse all four tokens at once:
90 * <blockquote><pre>{@code
91 * String input = "1 fish 2 fish red fish blue fish";
92 * Scanner s = new Scanner(input);
93 * s.findInLine("(\\d+) fish (\\d+) fish (\\w+) fish (\\w+)");
94 * MatchResult result = s.match();
95 * for (int i=1; i<=result.groupCount(); i++)
96 * System.out.println(result.group(i));
97 * s.close();
98 * }</pre></blockquote>
99 *
100 * <p>The <a name="default-delimiter">default whitespace delimiter</a> used
101 * by a scanner is as recognized by {@link Character#isWhitespace(char)
102 * Character.isWhitespace()}. The {@link #reset reset()}
103 * method will reset the value of the scanner's delimiter to the default
104 * whitespace delimiter regardless of whether it was previously changed.
105 *
106 * <p>A scanning operation may block waiting for input.
107 *
108 * <p>The {@link #next} and {@link #hasNext} methods and their
109 * companion methods (such as {@link #nextInt} and
110 * {@link #hasNextInt}) first skip any input that matches the delimiter
111 * pattern, and then attempt to return the next token. Both {@code hasNext()}
112 * and {@code next()} methods may block waiting for further input. Whether a
113 * {@code hasNext()} method blocks has no connection to whether or not its
114 * associated {@code next()} method will block. The {@link #tokens} method
115 * may also block waiting for input.
116 *
117 * <p>The {@link #findInLine findInLine()},
118 * {@link #findWithinHorizon findWithinHorizon()},
119 * {@link #skip skip()}, and {@link #findAll findAll()}
120 * methods operate independently of the delimiter pattern. These methods will
121 * attempt to match the specified pattern with no regard to delimiters in the
122 * input and thus can be used in special circumstances where delimiters are
123 * not relevant. These methods may block waiting for more input.
124 *
125 * <p>When a scanner throws an {@link InputMismatchException}, the scanner
126 * will not pass the token that caused the exception, so that it may be
127 * retrieved or skipped via some other method.
128 *
129 * <p>Depending upon the type of delimiting pattern, empty tokens may be
130 * returned. For example, the pattern {@code "\\s+"} will return no empty
131 * tokens since it matches multiple instances of the delimiter. The delimiting
132 * pattern {@code "\\s"} could return empty tokens since it only passes one
133 * space at a time.
134 *
135 * <p> A scanner can read text from any object which implements the {@link
136 * java.lang.Readable} interface. If an invocation of the underlying
137 * readable's {@link java.lang.Readable#read read()} method throws an {@link
138 * java.io.IOException} then the scanner assumes that the end of the input
139 * has been reached. The most recent {@code IOException} thrown by the
140 * underlying readable can be retrieved via the {@link #ioException} method.
141 *
142 * <p>When a {@code Scanner} is closed, it will close its input source
143 * if the source implements the {@link java.io.Closeable} interface.
144 *
145 * <p>A {@code Scanner} is not safe for multithreaded use without
146 * external synchronization.
147 *
148 * <p>Unless otherwise mentioned, passing a {@code null} parameter into
149 * any method of a {@code Scanner} will cause a
150 * {@code NullPointerException} to be thrown.
151 *
152 * <p>A scanner will default to interpreting numbers as decimal unless a
153 * different radix has been set by using the {@link #useRadix} method. The
154 * {@link #reset} method will reset the value of the scanner's radix to
155 * {@code 10} regardless of whether it was previously changed.
156 *
157 * <h3> <a name="localized-numbers">Localized numbers</a> </h3>
158 *
159 * <p> An instance of this class is capable of scanning numbers in the standard
160 * formats as well as in the formats of the scanner's locale. A scanner's
161 * <a name="initial-locale">initial locale </a>is the value returned by the {@link
162 * java.util.Locale#getDefault(Locale.Category)
163 * Locale.getDefault(Locale.Category.FORMAT)} method; it may be changed via the {@link
164 * #useLocale useLocale()} method. The {@link #reset} method will reset the value of the
165 * scanner's locale to the initial locale regardless of whether it was
166 * previously changed.
167 *
168 * <p>The localized formats are defined in terms of the following parameters,
169 * which for a particular locale are taken from that locale's {@link
170 * java.text.DecimalFormat DecimalFormat} object, {@code df}, and its and
171 * {@link java.text.DecimalFormatSymbols DecimalFormatSymbols} object,
172 * {@code dfs}.
173 *
174 * <blockquote><dl>
175 * <dt><i>LocalGroupSeparator </i>
176 * <dd>The character used to separate thousands groups,
177 * <i>i.e.,</i> {@code dfs.}{@link
178 * java.text.DecimalFormatSymbols#getGroupingSeparator
179 * getGroupingSeparator()}
180 * <dt><i>LocalDecimalSeparator </i>
181 * <dd>The character used for the decimal point,
182 * <i>i.e.,</i> {@code dfs.}{@link
183 * java.text.DecimalFormatSymbols#getDecimalSeparator
184 * getDecimalSeparator()}
362 // The default radix for this scanner
363 private int defaultRadix = 10;
364
365 // The locale used by this scanner
366 private Locale locale = null;
367
368 // A cache of the last few recently used Patterns
369 private LRUCache<String,Pattern> patternCache =
370 new LRUCache<String,Pattern>(7) {
371 protected Pattern create(String s) {
372 return Pattern.compile(s);
373 }
374 protected boolean hasName(Pattern p, String s) {
375 return p.pattern().equals(s);
376 }
377 };
378
379 // A holder of the last IOException encountered
380 private IOException lastException;
381
382 // Number of times this scanner's state has been modified.
383 // Generally incremented on most public APIs and checked
384 // within spliterator implementations.
385 int modCount;
386
387 // A pattern for java whitespace
388 private static Pattern WHITESPACE_PATTERN = Pattern.compile(
389 "\\p{javaWhitespace}+");
390
391 // A pattern for any token
392 private static Pattern FIND_ANY_PATTERN = Pattern.compile("(?s).*");
393
394 // A pattern for non-ASCII digits
395 private static Pattern NON_ASCII_DIGIT = Pattern.compile(
396 "[\\p{javaDigit}&&[^0-9]]");
397
398 // Fields and methods to support scanning primitive types
399
400 /**
401 * Locale dependent values used to scan numbers
402 */
403 private String groupSeparator = "\\,";
404 private String decimalSeparator = "\\.";
405 private String nanString = "NaN";
406 private String infinityString = "Infinity";
988 }
989 // Last token; Match the pattern here or throw
990 matcher.usePattern(pattern);
991 matcher.region(position, buf.limit());
992 if (matcher.matches()) {
993 String s = matcher.group();
994 position = matcher.end();
995 return s;
996 }
997 // Last piece does not match
998 return null;
999 }
1000
1001 // There is a partial token in the buffer; must read more
1002 // to complete it
1003 needInput = true;
1004 return null;
1005 }
1006
1007 // Finds the specified pattern in the buffer up to horizon.
1008 // Returns true if the specified input pattern was matched,
1009 // and leaves the matcher field with the current match state.
1010 private boolean findPatternInBuffer(Pattern pattern, int horizon) {
1011 matchValid = false;
1012 matcher.usePattern(pattern);
1013 int bufferLimit = buf.limit();
1014 int horizonLimit = -1;
1015 int searchLimit = bufferLimit;
1016 if (horizon > 0) {
1017 horizonLimit = position + horizon;
1018 if (horizonLimit < bufferLimit)
1019 searchLimit = horizonLimit;
1020 }
1021 matcher.region(position, searchLimit);
1022 if (matcher.find()) {
1023 if (matcher.hitEnd() && (!sourceClosed)) {
1024 // The match may be longer if didn't hit horizon or real end
1025 if (searchLimit != horizonLimit) {
1026 // Hit an artificial end; try to extend the match
1027 needInput = true;
1028 return false;
1029 }
1030 // The match could go away depending on what is next
1031 if ((searchLimit == horizonLimit) && matcher.requireEnd()) {
1032 // Rare case: we hit the end of input and it happens
1033 // that it is at the horizon and the end of input is
1034 // required for the match.
1035 needInput = true;
1036 return false;
1037 }
1038 }
1039 // Did not hit end, or hit real end, or hit horizon
1040 position = matcher.end();
1041 return true;
1042 }
1043
1044 if (sourceClosed)
1045 return false;
1046
1047 // If there is no specified horizon, or if we have not searched
1048 // to the specified horizon yet, get more input
1049 if ((horizon == 0) || (searchLimit != horizonLimit))
1050 needInput = true;
1051 return false;
1052 }
1053
1054 // Attempts to match a pattern anchored at the current position.
1055 // Returns true if the specified input pattern was matched,
1056 // and leaves the matcher field with the current match state.
1057 private boolean matchPatternInBuffer(Pattern pattern) {
1058 matchValid = false;
1059 matcher.usePattern(pattern);
1060 matcher.region(position, buf.limit());
1061 if (matcher.lookingAt()) {
1062 if (matcher.hitEnd() && (!sourceClosed)) {
1063 // Get more input and try again
1064 needInput = true;
1065 return false;
1066 }
1067 position = matcher.end();
1068 return true;
1069 }
1070
1071 if (sourceClosed)
1072 return false;
1073
1074 // Read more to find pattern
1075 needInput = true;
1076 return false;
1077 }
1078
1079 // Throws if the scanner is closed
1080 private void ensureOpen() {
1081 if (closed)
1082 throw new IllegalStateException("Scanner closed");
1083 }
1084
1085 // Public methods
1086
1087 /**
1088 * Closes this scanner.
1089 *
1090 * <p> If this scanner has not yet been closed then if its underlying
1091 * {@linkplain java.lang.Readable readable} also implements the {@link
1092 * java.io.Closeable} interface then the readable's {@code close} method
1093 * will be invoked. If this scanner is already closed then invoking this
1094 * method will have no effect.
1095 *
1096 * <p>Attempting to perform search operations after a scanner has
1123 return lastException;
1124 }
1125
1126 /**
1127 * Returns the {@code Pattern} this {@code Scanner} is currently
1128 * using to match delimiters.
1129 *
1130 * @return this scanner's delimiting pattern.
1131 */
1132 public Pattern delimiter() {
1133 return delimPattern;
1134 }
1135
1136 /**
1137 * Sets this scanner's delimiting pattern to the specified pattern.
1138 *
1139 * @param pattern A delimiting pattern
1140 * @return this scanner
1141 */
1142 public Scanner useDelimiter(Pattern pattern) {
1143 modCount++;
1144 delimPattern = pattern;
1145 return this;
1146 }
1147
1148 /**
1149 * Sets this scanner's delimiting pattern to a pattern constructed from
1150 * the specified {@code String}.
1151 *
1152 * <p> An invocation of this method of the form
1153 * {@code useDelimiter(pattern)} behaves in exactly the same way as the
1154 * invocation {@code useDelimiter(Pattern.compile(pattern))}.
1155 *
1156 * <p> Invoking the {@link #reset} method will set the scanner's delimiter
1157 * to the <a href= "#default-delimiter">default</a>.
1158 *
1159 * @param pattern A string specifying a delimiting pattern
1160 * @return this scanner
1161 */
1162 public Scanner useDelimiter(String pattern) {
1163 modCount++;
1164 delimPattern = patternCache.forName(pattern);
1165 return this;
1166 }
1167
1168 /**
1169 * Returns this scanner's locale.
1170 *
1171 * <p>A scanner's locale affects many elements of its default
1172 * primitive matching regular expressions; see
1173 * <a href= "#localized-numbers">localized numbers</a> above.
1174 *
1175 * @return this scanner's locale
1176 */
1177 public Locale locale() {
1178 return this.locale;
1179 }
1180
1181 /**
1182 * Sets this scanner's locale to the specified locale.
1183 *
1184 * <p>A scanner's locale affects many elements of its default
1185 * primitive matching regular expressions; see
1186 * <a href= "#localized-numbers">localized numbers</a> above.
1187 *
1188 * <p>Invoking the {@link #reset} method will set the scanner's locale to
1189 * the <a href= "#initial-locale">initial locale</a>.
1190 *
1191 * @param locale A string specifying the locale to use
1192 * @return this scanner
1193 */
1194 public Scanner useLocale(Locale locale) {
1195 if (locale.equals(this.locale))
1196 return this;
1197
1198 modCount++;
1199 this.locale = locale;
1200 DecimalFormat df =
1201 (DecimalFormat)NumberFormat.getNumberInstance(locale);
1202 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(locale);
1203
1204 // These must be literalized to avoid collision with regex
1205 // metacharacters such as dot or parenthesis
1206 groupSeparator = "\\" + dfs.getGroupingSeparator();
1207 decimalSeparator = "\\" + dfs.getDecimalSeparator();
1208
1209 // Quoting the nonzero length locale-specific things
1210 // to avoid potential conflict with metacharacters
1211 nanString = "\\Q" + dfs.getNaN() + "\\E";
1212 infinityString = "\\Q" + dfs.getInfinity() + "\\E";
1213 positivePrefix = df.getPositivePrefix();
1214 if (positivePrefix.length() > 0)
1215 positivePrefix = "\\Q" + positivePrefix + "\\E";
1216 negativePrefix = df.getNegativePrefix();
1217 if (negativePrefix.length() > 0)
1218 negativePrefix = "\\Q" + negativePrefix + "\\E";
1234 /**
1235 * Returns this scanner's default radix.
1236 *
1237 * <p>A scanner's radix affects elements of its default
1238 * number matching regular expressions; see
1239 * <a href= "#localized-numbers">localized numbers</a> above.
1240 *
1241 * @return the default radix of this scanner
1242 */
1243 public int radix() {
1244 return this.defaultRadix;
1245 }
1246
1247 /**
1248 * Sets this scanner's default radix to the specified radix.
1249 *
1250 * <p>A scanner's radix affects elements of its default
1251 * number matching regular expressions; see
1252 * <a href= "#localized-numbers">localized numbers</a> above.
1253 *
1254 * <p>If the radix is less than {@link Character#MIN_RADIX Character.MIN_RADIX}
1255 * or greater than {@link Character#MAX_RADIX Character.MAX_RADIX}, then an
1256 * {@code IllegalArgumentException} is thrown.
1257 *
1258 * <p>Invoking the {@link #reset} method will set the scanner's radix to
1259 * {@code 10}.
1260 *
1261 * @param radix The radix to use when scanning numbers
1262 * @return this scanner
1263 * @throws IllegalArgumentException if radix is out of range
1264 */
1265 public Scanner useRadix(int radix) {
1266 if ((radix < Character.MIN_RADIX) || (radix > Character.MAX_RADIX))
1267 throw new IllegalArgumentException("radix:"+radix);
1268
1269 if (this.defaultRadix == radix)
1270 return this;
1271 modCount++;
1272 this.defaultRadix = radix;
1273 // Force rebuilding and recompilation of radix dependent patterns
1274 integerPattern = null;
1275 return this;
1276 }
1277
1278 // The next operation should occur in the specified radix but
1279 // the default is left untouched.
1280 private void setRadix(int radix) {
1281 if (this.radix != radix) {
1282 // Force rebuilding and recompilation of radix dependent patterns
1283 integerPattern = null;
1284 this.radix = radix;
1285 }
1286 }
1287
1288 /**
1289 * Returns the match result of the last scanning operation performed
1290 * by this scanner. This method throws {@code IllegalStateException}
1291 * if no match has been performed, or if the last match was
1292 * not successful.
1293 *
1294 * <p>The various {@code next} methods of {@code Scanner}
1295 * make a match result available if they complete without throwing an
1296 * exception. For instance, after an invocation of the {@link #nextInt}
1297 * method that returned an int, this method returns a
1298 * {@code MatchResult} for the search of the
1299 * <a href="#Integer-regex"><i>Integer</i></a> regular expression
1300 * defined above. Similarly the {@link #findInLine findInLine()},
1301 * {@link #findWithinHorizon findWithinHorizon()}, and {@link #skip skip()}
1302 * methods will make a match available if they succeed.
1303 *
1304 * @return a match result for the last match operation
1305 * @throws IllegalStateException If no match result is available
1306 */
1307 public MatchResult match() {
1308 if (!matchValid)
1309 throw new IllegalStateException("No match result available");
1310 return matcher.toMatchResult();
1311 }
1312
1313 /**
1314 * <p>Returns the string representation of this {@code Scanner}. The
1315 * string representation of a {@code Scanner} contains information
1316 * that may be useful for debugging. The exact format is unspecified.
1317 *
1318 * @return The string representation of this scanner
1319 */
1320 public String toString() {
1321 StringBuilder sb = new StringBuilder();
1322 sb.append("java.util.Scanner");
1332 sb.append("[negative prefix=" + negativePrefix + "]");
1333 sb.append("[positive suffix=" + positiveSuffix + "]");
1334 sb.append("[negative suffix=" + negativeSuffix + "]");
1335 sb.append("[NaN string=" + nanString + "]");
1336 sb.append("[infinity string=" + infinityString + "]");
1337 return sb.toString();
1338 }
1339
1340 /**
1341 * Returns true if this scanner has another token in its input.
1342 * This method may block while waiting for input to scan.
1343 * The scanner does not advance past any input.
1344 *
1345 * @return true if and only if this scanner has another token
1346 * @throws IllegalStateException if this scanner is closed
1347 * @see java.util.Iterator
1348 */
1349 public boolean hasNext() {
1350 ensureOpen();
1351 saveState();
1352 modCount++;
1353 while (!sourceClosed) {
1354 if (hasTokenInBuffer())
1355 return revertState(true);
1356 readInput();
1357 }
1358 boolean result = hasTokenInBuffer();
1359 return revertState(result);
1360 }
1361
1362 /**
1363 * Finds and returns the next complete token from this scanner.
1364 * A complete token is preceded and followed by input that matches
1365 * the delimiter pattern. This method may block while waiting for input
1366 * to scan, even if a previous invocation of {@link #hasNext} returned
1367 * {@code true}.
1368 *
1369 * @return the next token
1370 * @throws NoSuchElementException if no more tokens are available
1371 * @throws IllegalStateException if this scanner is closed
1372 * @see java.util.Iterator
1373 */
1374 public String next() {
1375 ensureOpen();
1376 clearCaches();
1377 modCount++;
1378
1379 while (true) {
1380 String token = getCompleteTokenInBuffer(null);
1381 if (token != null) {
1382 matchValid = true;
1383 skipped = false;
1384 return token;
1385 }
1386 if (needInput)
1387 readInput();
1388 else
1389 throwFor();
1390 }
1391 }
1392
1393 /**
1394 * The remove operation is not supported by this implementation of
1395 * {@code Iterator}.
1396 *
1397 * @throws UnsupportedOperationException if this method is invoked.
1436 return next(patternCache.forName(pattern));
1437 }
1438
1439 /**
1440 * Returns true if the next complete token matches the specified pattern.
1441 * A complete token is prefixed and postfixed by input that matches
1442 * the delimiter pattern. This method may block while waiting for input.
1443 * The scanner does not advance past any input.
1444 *
1445 * @param pattern the pattern to scan for
1446 * @return true if and only if this scanner has another token matching
1447 * the specified pattern
1448 * @throws IllegalStateException if this scanner is closed
1449 */
1450 public boolean hasNext(Pattern pattern) {
1451 ensureOpen();
1452 if (pattern == null)
1453 throw new NullPointerException();
1454 hasNextPattern = null;
1455 saveState();
1456 modCount++;
1457
1458 while (true) {
1459 if (getCompleteTokenInBuffer(pattern) != null) {
1460 matchValid = true;
1461 cacheResult();
1462 return revertState(true);
1463 }
1464 if (needInput)
1465 readInput();
1466 else
1467 return revertState(false);
1468 }
1469 }
1470
1471 /**
1472 * Returns the next token if it matches the specified pattern. This
1473 * method may block while waiting for input to scan, even if a previous
1474 * invocation of {@link #hasNext(Pattern)} returned {@code true}.
1475 * If the match is successful, the scanner advances past the input that
1476 * matched the pattern.
1477 *
1478 * @param pattern the pattern to scan for
1479 * @return the next token
1480 * @throws NoSuchElementException if no more tokens are available
1481 * @throws IllegalStateException if this scanner is closed
1482 */
1483 public String next(Pattern pattern) {
1484 ensureOpen();
1485 if (pattern == null)
1486 throw new NullPointerException();
1487
1488 modCount++;
1489 // Did we already find this pattern?
1490 if (hasNextPattern == pattern)
1491 return getCachedResult();
1492 clearCaches();
1493
1494 // Search for the pattern
1495 while (true) {
1496 String token = getCompleteTokenInBuffer(pattern);
1497 if (token != null) {
1498 matchValid = true;
1499 skipped = false;
1500 return token;
1501 }
1502 if (needInput)
1503 readInput();
1504 else
1505 throwFor();
1506 }
1507 }
1508
1509 /**
1510 * Returns true if there is another line in the input of this scanner.
1511 * This method may block while waiting for input. The scanner does not
1512 * advance past any input.
1513 *
1514 * @return true if and only if this scanner has another line of input
1515 * @throws IllegalStateException if this scanner is closed
1516 */
1517 public boolean hasNextLine() {
1518 saveState();
1519
1520 modCount++;
1521 String result = findWithinHorizon(linePattern(), 0);
1522 if (result != null) {
1523 MatchResult mr = this.match();
1524 String lineSep = mr.group(1);
1525 if (lineSep != null) {
1526 result = result.substring(0, result.length() -
1527 lineSep.length());
1528 cacheResult(result);
1529
1530 } else {
1531 cacheResult();
1532 }
1533 }
1534 revertState();
1535 return (result != null);
1536 }
1537
1538 /**
1539 * Advances this scanner past the current line and returns the input
1540 * that was skipped.
1541 *
1542 * This method returns the rest of the current line, excluding any line
1543 * separator at the end. The position is set to the beginning of the next
1544 * line.
1545 *
1546 * <p>Since this method continues to search through the input looking
1547 * for a line separator, it may buffer all of the input searching for
1548 * the line to skip if no line separators are present.
1549 *
1550 * @return the line that was skipped
1551 * @throws NoSuchElementException if no line was found
1552 * @throws IllegalStateException if this scanner is closed
1553 */
1554 public String nextLine() {
1555 modCount++;
1556 if (hasNextPattern == linePattern())
1557 return getCachedResult();
1558 clearCaches();
1559
1560 String result = findWithinHorizon(linePattern, 0);
1561 if (result == null)
1562 throw new NoSuchElementException("No line found");
1563 MatchResult mr = this.match();
1564 String lineSep = mr.group(1);
1565 if (lineSep != null)
1566 result = result.substring(0, result.length() - lineSep.length());
1567 if (result == null)
1568 throw new NoSuchElementException();
1569 else
1570 return result;
1571 }
1572
1573 // Public methods that ignore delimiters
1574
1575 /**
1594 * scanner advances past the input that matched and returns the string that
1595 * matched the pattern.
1596 * If no such pattern is detected in the input up to the next line
1597 * separator, then {@code null} is returned and the scanner's
1598 * position is unchanged. This method may block waiting for input that
1599 * matches the pattern.
1600 *
1601 * <p>Since this method continues to search through the input looking
1602 * for the specified pattern, it may buffer all of the input searching for
1603 * the desired token if no line separators are present.
1604 *
1605 * @param pattern the pattern to scan for
1606 * @return the text that matched the specified pattern
1607 * @throws IllegalStateException if this scanner is closed
1608 */
1609 public String findInLine(Pattern pattern) {
1610 ensureOpen();
1611 if (pattern == null)
1612 throw new NullPointerException();
1613 clearCaches();
1614 modCount++;
1615 // Expand buffer to include the next newline or end of input
1616 int endPosition = 0;
1617 saveState();
1618 while (true) {
1619 if (findPatternInBuffer(separatorPattern(), 0)) {
1620 endPosition = matcher.start();
1621 break; // up to next newline
1622 }
1623 if (needInput) {
1624 readInput();
1625 } else {
1626 endPosition = buf.limit();
1627 break; // up to end of input
1628 }
1629 }
1630 revertState();
1631 int horizonForLine = endPosition - position;
1632 // If there is nothing between the current pos and the next
1633 // newline simply return null, invoking findWithinHorizon
1634 // with "horizon=0" will scan beyond the line bound.
1635 if (horizonForLine == 0)
1636 return null;
1637 // Search for the pattern
1638 return findWithinHorizon(pattern, horizonForLine);
1639 }
1640
1641 /**
1642 * Attempts to find the next occurrence of a pattern constructed from the
1643 * specified string, ignoring delimiters.
1644 *
1645 * <p>An invocation of this method of the form
1646 * {@code findWithinHorizon(pattern)} behaves in exactly the same way as
1647 * the invocation
1648 * {@code findWithinHorizon(Pattern.compile(pattern), horizon)}.
1649 *
1650 * @param pattern a string specifying the pattern to search for
1651 * @param horizon the search horizon
1652 * @return the text that matched the specified pattern
1653 * @throws IllegalStateException if this scanner is closed
1654 * @throws IllegalArgumentException if horizon is negative
1655 */
1656 public String findWithinHorizon(String pattern, int horizon) {
1657 return findWithinHorizon(patternCache.forName(pattern), horizon);
1658 }
1659
1660 /**
1661 * Attempts to find the next occurrence of the specified pattern.
1662 *
1663 * <p>This method searches through the input up to the specified
1664 * search horizon, ignoring delimiters. If the pattern is found the
1665 * scanner advances past the input that matched and returns the string
1666 * that matched the pattern. If no such pattern is detected then the
1667 * null is returned and the scanner's position remains unchanged. This
1668 * method may block waiting for input that matches the pattern.
1678 * this method continues to search through the input looking for the
1679 * specified pattern without bound. In this case it may buffer all of
1680 * the input searching for the pattern.
1681 *
1682 * <p>If horizon is negative, then an IllegalArgumentException is
1683 * thrown.
1684 *
1685 * @param pattern the pattern to scan for
1686 * @param horizon the search horizon
1687 * @return the text that matched the specified pattern
1688 * @throws IllegalStateException if this scanner is closed
1689 * @throws IllegalArgumentException if horizon is negative
1690 */
1691 public String findWithinHorizon(Pattern pattern, int horizon) {
1692 ensureOpen();
1693 if (pattern == null)
1694 throw new NullPointerException();
1695 if (horizon < 0)
1696 throw new IllegalArgumentException("horizon < 0");
1697 clearCaches();
1698 modCount++;
1699
1700 // Search for the pattern
1701 while (true) {
1702 if (findPatternInBuffer(pattern, horizon)) {
1703 matchValid = true;
1704 return matcher.group();
1705 }
1706 if (needInput)
1707 readInput();
1708 else
1709 break; // up to end of input
1710 }
1711 return null;
1712 }
1713
1714 /**
1715 * Skips input that matches the specified pattern, ignoring delimiters.
1716 * This method will skip input if an anchored match of the specified
1717 * pattern succeeds.
1718 *
1719 * <p>If a match to the specified pattern is not found at the
1720 * current position, then no input is skipped and a
1721 * {@code NoSuchElementException} is thrown.
1722 *
1723 * <p>Since this method seeks to match the specified pattern starting at
1724 * the scanner's current position, patterns that can match a lot of
1725 * input (".*", for example) may cause the scanner to buffer a large
1726 * amount of input.
1727 *
1728 * <p>Note that it is possible to skip something without risking a
1729 * {@code NoSuchElementException} by using a pattern that can
1730 * match nothing, e.g., {@code sc.skip("[ \t]*")}.
1731 *
1732 * @param pattern a string specifying the pattern to skip over
1733 * @return this scanner
1734 * @throws NoSuchElementException if the specified pattern is not found
1735 * @throws IllegalStateException if this scanner is closed
1736 */
1737 public Scanner skip(Pattern pattern) {
1738 ensureOpen();
1739 if (pattern == null)
1740 throw new NullPointerException();
1741 clearCaches();
1742 modCount++;
1743
1744 // Search for the pattern
1745 while (true) {
1746 if (matchPatternInBuffer(pattern)) {
1747 matchValid = true;
1748 position = matcher.end();
1749 return this;
1750 }
1751 if (needInput)
1752 readInput();
1753 else
1754 throw new NoSuchElementException();
1755 }
1756 }
1757
1758 /**
1759 * Skips input that matches a pattern constructed from the specified
1760 * string.
1761 *
1762 * <p> An invocation of this method of the form {@code skip(pattern)}
1763 * behaves in exactly the same way as the invocation
1764 * {@code skip(Pattern.compile(pattern))}.
1765 *
1766 * @param pattern a string specifying the pattern to skip over
1937 setRadix(radix);
1938 boolean result = hasNext(integerPattern());
1939 if (result) { // Cache it
1940 try {
1941 String s = (matcher.group(SIMPLE_GROUP_INDEX) == null) ?
1942 processIntegerToken(hasNextResult) :
1943 hasNextResult;
1944 typeCache = Short.parseShort(s, radix);
1945 } catch (NumberFormatException nfe) {
1946 result = false;
1947 }
1948 }
1949 return result;
1950 }
1951
1952 /**
1953 * Scans the next token of the input as a {@code short}.
1954 *
1955 * <p> An invocation of this method of the form
1956 * {@code nextShort()} behaves in exactly the same way as the
1957 * invocation {@link #nextShort(int) nextShort(radix)}, where {@code radix}
1958 * is the default radix of this scanner.
1959 *
1960 * @return the {@code short} scanned from the input
1961 * @throws InputMismatchException
1962 * if the next token does not match the <i>Integer</i>
1963 * regular expression, or is out of range
1964 * @throws NoSuchElementException if input is exhausted
1965 * @throws IllegalStateException if this scanner is closed
1966 */
1967 public short nextShort() {
1968 return nextShort(defaultRadix);
1969 }
1970
1971 /**
1972 * Scans the next token of the input as a {@code short}.
1973 * This method will throw {@code InputMismatchException}
1974 * if the next token cannot be translated into a valid short value as
1975 * described below. If the translation is successful, the scanner advances
1976 * past the input that matched.
1977 *
2595 BigDecimal val = (BigDecimal)typeCache;
2596 useTypeCache();
2597 return val;
2598 }
2599 setRadix(10);
2600 clearCaches();
2601 // Search for next float
2602 try {
2603 String s = processFloatToken(next(decimalPattern()));
2604 return new BigDecimal(s);
2605 } catch (NumberFormatException nfe) {
2606 position = matcher.start(); // don't skip bad token
2607 throw new InputMismatchException(nfe.getMessage());
2608 }
2609 }
2610
2611 /**
2612 * Resets this scanner.
2613 *
2614 * <p> Resetting a scanner discards all of its explicit state
2615 * information which may have been changed by invocations of
2616 * {@link #useDelimiter useDelimiter()},
2617 * {@link #useLocale useLocale()}, or
2618 * {@link #useRadix useRadix()}.
2619 *
2620 * <p> An invocation of this method of the form
2621 * {@code scanner.reset()} behaves in exactly the same way as the
2622 * invocation
2623 *
2624 * <blockquote><pre>{@code
2625 * scanner.useDelimiter("\\p{javaWhitespace}+")
2626 * .useLocale(Locale.getDefault(Locale.Category.FORMAT))
2627 * .useRadix(10);
2628 * }</pre></blockquote>
2629 *
2630 * @return this scanner
2631 *
2632 * @since 1.6
2633 */
2634 public Scanner reset() {
2635 delimPattern = WHITESPACE_PATTERN;
2636 useLocale(Locale.getDefault(Locale.Category.FORMAT));
2637 useRadix(10);
2638 clearCaches();
2639 modCount++;
2640 return this;
2641 }
2642
2643 /**
2644 * Returns a stream of delimiter-separated tokens from this scanner. The
2645 * stream contains the same tokens that would be returned, starting from
2646 * this scanner's current state, by calling the {@link #next} method
2647 * repeatedly until the {@link #hasNext} method returns false.
2648 *
2649 * <p>The resulting stream is sequential and ordered. All stream elements are
2650 * non-null.
2651 *
2652 * <p>Scanning starts upon initiation of the terminal stream operation, using the
2653 * current state of this scanner. Subsequent calls to any methods on this scanner
2654 * other than {@link #close} and {@link #ioException} may return undefined results
2655 * or may cause undefined effects on the returned stream. The returned stream's source
2656 * {@code Spliterator} is <em>fail-fast</em> and will, on a best-effort basis, throw a
2657 * {@link java.util.ConcurrentModificationException} if any such calls are detected
2658 * during stream pipeline execution.
2659 *
2660 * <p>After stream pipeline execution completes, this scanner is left in an indeterminate
2661 * state and cannot be reused.
2662 *
2663 * <p>If this scanner contains a resource that must be released, this scanner
2664 * should be closed, either by calling its {@link #close} method, or by
2665 * closing the returned stream. Closing the stream will close the underlying scanner.
2666 * {@code IllegalStateException} is thrown if the scanner has been closed when this
2667 * method is called, or if this scanner is closed during stream pipeline execution.
2668 *
2669 * <p>This method might block waiting for more input.
2670 *
2671 * @apiNote
2672 * For example, the following code will create a list of
2673 * comma-delimited tokens from a string:
2674 *
2675 * <pre>{@code
2676 * List<String> result = new Scanner("abc,def,,ghi")
2677 * .useDelimiter(",")
2678 * .tokens()
2679 * .collect(Collectors.toList());
2680 * }</pre>
2681 *
2682 * <p>The resulting list would contain {@code "abc"}, {@code "def"},
2683 * the empty string, and {@code "ghi"}.
2684 *
2685 * @return a sequential stream of token strings
2686 * @throws IllegalStateException if this scanner is closed
2687 * @since 1.9
2688 */
2689 public Stream<String> tokens() {
2690 ensureOpen();
2691 Stream<String> stream = StreamSupport.stream(new TokenSpliterator(), false);
2692 return stream.onClose(this::close);
2693 }
2694
2695 class TokenSpliterator extends Spliterators.AbstractSpliterator<String> {
2696 int expectedCount = -1;
2697
2698 TokenSpliterator() {
2699 super(Long.MAX_VALUE,
2700 Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.ORDERED);
2701 }
2702
2703 @Override
2704 public boolean tryAdvance(Consumer<? super String> cons) {
2705 if (expectedCount >= 0 && expectedCount != modCount) {
2706 throw new ConcurrentModificationException();
2707 }
2708
2709 if (hasNext()) {
2710 String token = next();
2711 expectedCount = modCount;
2712 cons.accept(token);
2713 if (expectedCount != modCount) {
2714 throw new ConcurrentModificationException();
2715 }
2716 return true;
2717 } else {
2718 expectedCount = modCount;
2719 return false;
2720 }
2721 }
2722 }
2723
2724 /**
2725 * Returns a stream of match results from this scanner. The stream
2726 * contains the same results in the same order that would be returned by
2727 * calling {@code findWithinHorizon(pattern, 0)} and then {@link #match}
2728 * successively as long as {@link #findWithinHorizon findWithinHorizon()}
2729 * finds matches.
2730 *
2731 * <p>The resulting stream is sequential and ordered. All stream elements are
2732 * non-null.
2733 *
2734 * <p>Scanning starts upon initiation of the terminal stream operation, using the
2735 * current state of this scanner. Subsequent calls to any methods on this scanner
2736 * other than {@link #close} and {@link #ioException} may return undefined results
2737 * or may cause undefined effects on the returned stream. The returned stream's source
2738 * {@code Spliterator} is <em>fail-fast</em> and will, on a best-effort basis, throw a
2739 * {@link java.util.ConcurrentModificationException} if any such calls are detected
2740 * during stream pipeline execution.
2741 *
2742 * <p>After stream pipeline execution completes, this scanner is left in an indeterminate
2743 * state and cannot be reused.
2744 *
2745 * <p>If this scanner contains a resource that must be released, this scanner
2746 * should be closed, either by calling its {@link #close} method, or by
2747 * closing the returned stream. Closing the stream will close the underlying scanner.
2748 * {@code IllegalStateException} is thrown if the scanner has been closed when this
2749 * method is called, or if this scanner is closed during stream pipeline execution.
2750 *
2751 * <p>As with the {@link #findWithinHorizon findWithinHorizon()} methods, this method
2752 * might block waiting for additional input, and it might buffer an unbounded amount of
2753 * input searching for a match.
2754 *
2755 * @apiNote
2756 * For example, the following code will read a file and return a list
2757 * of all sequences of characters consisting of seven or more Latin capital
2758 * letters:
2759 *
2760 * <pre>{@code
2761 * try (Scanner sc = new Scanner(Paths.get("input.txt"))) {
2762 * Pattern pat = Pattern.compile("[A-Z]{7,}");
2763 * List<String> capWords = sc.findAll(pat)
2764 * .map(MatchResult::group)
2765 * .collect(Collectors.toList());
2766 * }
2767 * }</pre>
2768 *
2769 * @param pattern the pattern to be matched
2770 * @return a sequential stream of match results
2771 * @throws NullPointerException if pattern is null
2772 * @throws IllegalStateException if this scanner is closed
2773 * @since 1.9
2774 */
2775 public Stream<MatchResult> findAll(Pattern pattern) {
2776 Objects.requireNonNull(pattern);
2777 ensureOpen();
2778 Stream<MatchResult> stream = StreamSupport.stream(new FindSpliterator(pattern), false);
2779 return stream.onClose(this::close);
2780 }
2781
2782 /**
2783 * Returns a stream of match results that match the provided pattern string.
2784 * The effect is equivalent to the following code:
2785 *
2786 * <pre>{@code
2787 * scanner.findAll(Pattern.compile(patString))
2788 * }</pre>
2789 *
2790 * @param patString the pattern string
2791 * @return a sequential stream of match results
2792 * @throws NullPointerException if patString is null
2793 * @throws IllegalStateException if this scanner is closed
2794 * @throws PatternSyntaxException if the regular expression's syntax is invalid
2795 * @since 1.9
2796 * @see java.util.regex.Pattern
2797 */
2798 public Stream<MatchResult> findAll(String patString) {
2799 Objects.requireNonNull(patString);
2800 ensureOpen();
2801 return findAll(patternCache.forName(patString));
2802 }
2803
2804 class FindSpliterator extends Spliterators.AbstractSpliterator<MatchResult> {
2805 final Pattern pattern;
2806 int expectedCount = -1;
2807
2808 FindSpliterator(Pattern pattern) {
2809 super(Long.MAX_VALUE,
2810 Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.ORDERED);
2811 this.pattern = pattern;
2812 }
2813
2814 @Override
2815 public boolean tryAdvance(Consumer<? super MatchResult> cons) {
2816 ensureOpen();
2817 if (expectedCount >= 0) {
2818 if (expectedCount != modCount) {
2819 throw new ConcurrentModificationException();
2820 }
2821 } else {
2822 expectedCount = modCount;
2823 }
2824
2825 while (true) {
2826 // assert expectedCount == modCount
2827 if (findPatternInBuffer(pattern, 0)) { // doesn't increment modCount
2828 cons.accept(matcher.toMatchResult());
2829 if (expectedCount != modCount) {
2830 throw new ConcurrentModificationException();
2831 }
2832 return true;
2833 }
2834 if (needInput)
2835 readInput(); // doesn't increment modCount
2836 else
2837 return false; // reached end of input
2838 }
2839 }
2840 }
2841 }
|