1 /* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package java.util; 17 18 import java.io.Closeable; 19 import java.io.File; 20 import java.io.FileInputStream; 21 import java.io.FileNotFoundException; 22 import java.io.IOException; 23 import java.io.InputStream; 24 import java.io.InputStreamReader; 25 import java.io.StringReader; 26 import java.io.UnsupportedEncodingException; 27 import java.math.BigDecimal; 28 import java.math.BigInteger; 29 import java.nio.CharBuffer; 30 import java.nio.channels.Channels; 31 import java.nio.channels.ReadableByteChannel; 32 import java.nio.charset.Charset; 33 import java.text.DecimalFormat; 34 import java.text.NumberFormat; 35 import java.util.regex.MatchResult; 36 import java.util.regex.Matcher; 37 import java.util.regex.Pattern; 38 import libcore.io.IoUtils; 39 40 /** 41 * A parser that parses a text string of primitive types and strings with the 42 * help of regular expressions. It supports localized numbers and various 43 * radixes. The input is broken into tokens by the delimiter pattern, which is 44 * whitespace by default. The primitive types can be obtained via corresponding 45 * next* methods. If the token is not in a valid format, an 46 * {@code InputMismatchException} is thrown. 47 * <p> 48 * For example: 49 * <pre> 50 * Scanner s = new Scanner("1A true"); 51 * System.out.println(s.nextInt(16)); 52 * System.out.println(s.nextBoolean()); 53 * </pre> 54 * <p> 55 * Yields the result: {@code 26 true} 56 * <p>A {@code Scanner} can also find or skip specific patterns without regard for the 57 * delimiter. All these methods and the various next* and hasNext* methods may 58 * block. 59 * <p> 60 * The {@code Scanner} class is not thread-safe. 61 */ 62 public final class Scanner implements Iterator<String> { 63 64 // Default delimiting pattern. 65 private static final Pattern DEFAULT_DELIMITER = Pattern 66 .compile("\\p{javaWhitespace}+"); 67 68 // The boolean's pattern. 69 private static final Pattern BOOLEAN_PATTERN = Pattern.compile( 70 "true|false", Pattern.CASE_INSENSITIVE); 71 72 // Pattern used to recognize line terminator. 73 private static final Pattern LINE_TERMINATOR; 74 75 // Pattern used to recognize multiple line terminators. 76 private static final Pattern MULTI_LINE_TERMINATOR; 77 78 // Pattern used to recognize a line with a line terminator. 79 private static final Pattern LINE_PATTERN; 80 81 static { 82 String NL = "\n|\r\n|\r|\u0085|\u2028|\u2029"; 83 LINE_TERMINATOR = Pattern.compile(NL); 84 MULTI_LINE_TERMINATOR = Pattern.compile("(" + NL + ")+"); 85 LINE_PATTERN = Pattern.compile(".*(" + NL + ")|.+(" + NL + ")?"); 86 } 87 88 // The pattern matches anything. 89 private static final Pattern ANY_PATTERN = Pattern.compile("(?s).*"); 90 91 private static final int DIPLOID = 2; 92 93 // Default radix. 94 private static final int DEFAULT_RADIX = 10; 95 96 private static final int DEFAULT_TRUNK_SIZE = 1024; 97 98 // The input source of scanner. 99 private Readable input; 100 101 private CharBuffer buffer; 102 103 private Pattern delimiter = DEFAULT_DELIMITER; 104 105 private Matcher matcher; 106 107 private int integerRadix = DEFAULT_RADIX; 108 109 private Locale locale = Locale.getDefault(); 110 111 // The position where find begins. 112 private int findStartIndex = 0; 113 114 // The last find start position. 115 private int preStartIndex = findStartIndex; 116 117 // The length of the buffer. 118 private int bufferLength = 0; 119 120 // Record the status of this scanner. True if the scanner 121 // is closed. 122 private boolean closed = false; 123 124 private IOException lastIOException; 125 126 private boolean matchSuccessful = false; 127 128 private DecimalFormat decimalFormat; 129 130 // Records whether the underlying readable has more input. 131 private boolean inputExhausted = false; 132 133 private Object cacheHasNextValue = null; 134 135 private int cachehasNextIndex = -1; 136 137 private enum DataType { 138 /* 139 * Stands for Integer 140 */ 141 INT, 142 /* 143 * Stands for Float 144 */ 145 FLOAT; 146 } 147 148 /** 149 * Creates a {@code Scanner} with the specified {@code File} as input. The default charset 150 * is applied when reading the file. 151 * 152 * @param src 153 * the file to be scanned. 154 * @throws FileNotFoundException 155 * if the specified file does not exist. 156 */ 157 public Scanner(File src) throws FileNotFoundException { 158 this(src, Charset.defaultCharset().name()); 159 } 160 161 /** 162 * Creates a {@code Scanner} with the specified {@code File} as input. The specified charset 163 * is applied when reading the file. 164 * 165 * @param src 166 * the file to be scanned. 167 * @param charsetName 168 * the name of the encoding type of the file. 169 * @throws FileNotFoundException 170 * if the specified file does not exist. 171 * @throws IllegalArgumentException 172 * if the specified coding does not exist. 173 */ 174 public Scanner(File src, String charsetName) throws FileNotFoundException { 175 if (src == null) { 176 throw new NullPointerException("src == null"); 177 } 178 FileInputStream fis = new FileInputStream(src); 179 if (charsetName == null) { 180 throw new IllegalArgumentException("charsetName == null"); 181 } 182 try { 183 input = new InputStreamReader(fis, charsetName); 184 } catch (UnsupportedEncodingException e) { 185 IoUtils.closeQuietly(fis); 186 throw new IllegalArgumentException(e.getMessage()); 187 } 188 initialization(); 189 } 190 191 /** 192 * Creates a {@code Scanner} on the specified string. 193 * 194 * @param src 195 * the string to be scanned. 196 */ 197 public Scanner(String src) { 198 input = new StringReader(src); 199 initialization(); 200 } 201 202 /** 203 * Creates a {@code Scanner} on the specified {@code InputStream}. The default charset is 204 * applied when decoding the input. 205 * 206 * @param src 207 * the {@code InputStream} to be scanned. 208 */ 209 public Scanner(InputStream src) { 210 this(src, Charset.defaultCharset().name()); 211 } 212 213 /** 214 * Creates a {@code Scanner} on the specified {@code InputStream}. The specified charset is 215 * applied when decoding the input. 216 * 217 * @param src 218 * the {@code InputStream} to be scanned. 219 * @param charsetName 220 * the encoding type of the {@code InputStream}. 221 * @throws IllegalArgumentException 222 * if the specified character set is not found. 223 */ 224 public Scanner(InputStream src, String charsetName) { 225 if (src == null) { 226 throw new NullPointerException("src == null"); 227 } 228 try { 229 input = new InputStreamReader(src, charsetName); 230 } catch (UnsupportedEncodingException e) { 231 throw new IllegalArgumentException(e.getMessage()); 232 } 233 initialization(); 234 } 235 236 /** 237 * Creates a {@code Scanner} with the specified {@code Readable} as input. 238 * 239 * @param src 240 * the {@code Readable} to be scanned. 241 */ 242 public Scanner(Readable src) { 243 if (src == null) { 244 throw new NullPointerException(); 245 } 246 input = src; 247 initialization(); 248 } 249 250 /** 251 * Creates a {@code Scanner} with the specified {@code ReadableByteChannel} as 252 * input. The default charset is applied when decoding the input. 253 * 254 * @param src 255 * the {@code ReadableByteChannel} to be scanned. 256 */ 257 public Scanner(ReadableByteChannel src) { 258 this(src, Charset.defaultCharset().name()); 259 } 260 261 /** 262 * Creates a {@code Scanner} with the specified {@code ReadableByteChannel} as 263 * input. The specified charset is applied when decoding the input. 264 * 265 * @param src 266 * the {@code ReadableByteChannel} to be scanned. 267 * @param charsetName 268 * the encoding type of the content. 269 * @throws IllegalArgumentException 270 * if the specified character set is not found. 271 */ 272 public Scanner(ReadableByteChannel src, String charsetName) { 273 if (src == null) { 274 throw new NullPointerException("src == null"); 275 } 276 if (charsetName == null) { 277 throw new IllegalArgumentException("charsetName == null"); 278 } 279 input = Channels.newReader(src, charsetName); 280 initialization(); 281 } 282 283 /** 284 * Closes this {@code Scanner} and the underlying input if the input implements 285 * {@code Closeable}. If the {@code Scanner} has been closed, this method will have 286 * no effect. Any scanning operation called after calling this method will throw 287 * an {@code IllegalStateException}. 288 * 289 * @see Closeable 290 */ 291 public void close() { 292 if (closed) { 293 return; 294 } 295 if (input instanceof Closeable) { 296 try { 297 ((Closeable) input).close(); 298 } catch (IOException e) { 299 lastIOException = e; 300 } 301 } 302 closed = true; 303 } 304 305 /** 306 * Returns the delimiter {@code Pattern} in use by this {@code Scanner}. 307 * 308 * @return the delimiter {@code Pattern} in use by this {@code Scanner}. 309 */ 310 public Pattern delimiter() { 311 return delimiter; 312 } 313 314 /** 315 * Tries to find the pattern in the input. Delimiters are ignored. If the 316 * pattern is found before line terminator, the matched string will be 317 * returned, and the {@code Scanner} will advance to the end of the matched string. 318 * Otherwise, {@code null} will be returned and the {@code Scanner} will not advance. 319 * When waiting for input, the {@code Scanner} may be blocked. All the 320 * input may be cached if no line terminator exists in the buffer. 321 * 322 * @param pattern 323 * the pattern to find in the input. 324 * @return the matched string or {@code null} if the pattern is not found 325 * before the next line terminator. 326 * @throws IllegalStateException 327 * if the {@code Scanner} is closed. 328 */ 329 public String findInLine(Pattern pattern) { 330 checkClosed(); 331 checkNull(pattern); 332 int horizonLineSeparator = 0; 333 334 matcher.usePattern(MULTI_LINE_TERMINATOR); 335 matcher.region(findStartIndex, bufferLength); 336 337 boolean findComplete = false; 338 int terminatorLength = 0; 339 while (!findComplete) { 340 if (matcher.find()) { 341 horizonLineSeparator = matcher.start(); 342 terminatorLength = matcher.end() - matcher.start(); 343 findComplete = true; 344 } else { 345 if (!inputExhausted) { 346 readMore(); 347 resetMatcher(); 348 } else { 349 horizonLineSeparator = bufferLength; 350 findComplete = true; 351 } 352 } 353 } 354 355 matcher.usePattern(pattern); 356 357 /* 358 * TODO The following 2 statements are used to deal with regex's bug. 359 * java.util.regex.Matcher.region(int start, int end) implementation 360 * does not have any effects when called. They will be removed once the 361 * bug is fixed. 362 */ 363 int oldLimit = buffer.limit(); 364 // Considering the look ahead feature, the line terminator should be involved as RI 365 buffer.limit(horizonLineSeparator + terminatorLength); 366 // ========== To deal with regex bug ==================== 367 368 // Considering the look ahead feature, the line terminator should be involved as RI 369 matcher.region(findStartIndex, horizonLineSeparator + terminatorLength); 370 if (matcher.find()) { 371 // The scanner advances past the input that matched 372 findStartIndex = matcher.end(); 373 // If the matched pattern is immediately followed by line 374 // terminator. 375 if (horizonLineSeparator == matcher.end()) { 376 findStartIndex += terminatorLength; 377 } 378 // the line terminator itself should not be a part of 379 // the match result according to the Spec 380 if (horizonLineSeparator != bufferLength 381 && (horizonLineSeparator + terminatorLength == matcher 382 .end())) { 383 // ========== To deal with regex bug ==================== 384 buffer.limit(oldLimit); 385 // ========== To deal with regex bug ==================== 386 387 matchSuccessful = false; 388 return null; 389 } 390 matchSuccessful = true; 391 392 // ========== To deal with regex bug ==================== 393 buffer.limit(oldLimit); 394 // ========== To deal with regex bug ==================== 395 396 return matcher.group(); 397 } 398 399 // ========== To deal with regex bug ==================== 400 buffer.limit(oldLimit); 401 // ========== To deal with regex bug ==================== 402 403 matchSuccessful = false; 404 return null; 405 } 406 407 /** 408 * Compiles the pattern string and tries to find a substing matching it in the input data. The 409 * delimiter will be ignored. This is the same as invoking 410 * {@code findInLine(Pattern.compile(pattern))}. 411 * 412 * @param pattern 413 * a string used to construct a pattern which is in turn used to 414 * match a substring of the input data. 415 * @return the matched string or {@code null} if the pattern is not found 416 * before the next line terminator. 417 * @throws IllegalStateException 418 * if the {@code Scanner} is closed. 419 * @see #findInLine(Pattern) 420 */ 421 public String findInLine(String pattern) { 422 return findInLine(Pattern.compile(pattern)); 423 } 424 425 /** 426 * Tries to find the pattern in the input between the current position and the specified 427 * horizon. Delimiters are ignored. If the pattern is found, the matched 428 * string will be returned, and the {@code Scanner} will advance to the end of the 429 * matched string. Otherwise, null will be returned and {@code Scanner} will not 430 * advance. When waiting for input, the {@code Scanner} may be blocked. 431 * <p> 432 * The {@code Scanner}'s search will never go more than {@code horizon} code points from current 433 * position. The position of {@code horizon} does have an effect on the result of the 434 * match. For example, when the input is "123" and current position is at zero, 435 * <code>findWithinHorizon(Pattern.compile("\\p{Digit}{3}"), 2)</code> 436 * will return {@code null}, while 437 * <code>findWithinHorizon(Pattern.compile("\\p{Digit}{3}"), 3)</code> 438 * will return {@code "123"}. {@code horizon} is treated as a transparent, 439 * non-anchoring bound. (refer to 440 * {@link Matcher#useTransparentBounds(boolean)} and 441 * {@link Matcher#useAnchoringBounds(boolean)}) 442 * <p> 443 * A {@code horizon} whose value is zero will be ignored and the whole input will be 444 * used for search. In this situation, all the input may be cached. 445 * 446 * @param pattern 447 * the pattern used to scan. 448 * @param horizon 449 * the search limit. 450 * @return the matched string or {@code null} if the pattern is not found 451 * within the specified {@code horizon}. 452 * @throws IllegalStateException 453 * if the {@code Scanner} is closed. 454 * @throws IllegalArgumentException 455 * if {@code horizon} is less than zero. 456 */ 457 public String findWithinHorizon(Pattern pattern, int horizon) { 458 checkClosed(); 459 checkNull(pattern); 460 if (horizon < 0) { 461 throw new IllegalArgumentException("horizon < 0"); 462 } 463 matcher.usePattern(pattern); 464 465 String result = null; 466 int findEndIndex = 0; 467 int horizonEndIndex = 0; 468 if (horizon == 0) { 469 horizonEndIndex = Integer.MAX_VALUE; 470 } else { 471 horizonEndIndex = findStartIndex + horizon; 472 } 473 while (true) { 474 findEndIndex = bufferLength; 475 476 // If horizon > 0, then search up to 477 // min( bufferLength, findStartIndex + horizon). 478 // Otherwise search until readable is exhausted. 479 findEndIndex = Math.min(horizonEndIndex, bufferLength); 480 // If horizon == 0, consider horizon as always outside buffer. 481 boolean isHorizonInBuffer = (horizonEndIndex <= bufferLength); 482 // First, try to find pattern within buffer. If pattern can not be 483 // found in buffer, then expand the buffer and try again, 484 // util horizonEndIndex is exceeded or no more input left. 485 matcher.region(findStartIndex, findEndIndex); 486 if (matcher.find()) { 487 if (isHorizonInBuffer || inputExhausted) { 488 result = matcher.group(); 489 break; 490 } 491 } else { 492 // Pattern is not found in buffer while horizonEndIndex is 493 // within buffer, or input is exhausted. Under this situation, 494 // it can be judged that find fails. 495 if (isHorizonInBuffer || inputExhausted) { 496 break; 497 } 498 } 499 500 // Expand buffer and reset matcher if needed. 501 if (!inputExhausted) { 502 readMore(); 503 resetMatcher(); 504 } 505 } 506 if (result != null) { 507 findStartIndex = matcher.end(); 508 matchSuccessful = true; 509 } else { 510 matchSuccessful = false; 511 } 512 return result; 513 } 514 515 /** 516 * Tries to find the pattern in the input between the current position and the specified 517 * {@code horizon}. Delimiters are ignored. This call is the same as invoking 518 * {@code findWithinHorizon(Pattern.compile(pattern))}. 519 * 520 * @param pattern 521 * the pattern used to scan. 522 * @param horizon 523 * the search limit. 524 * @return the matched string, or {@code null} if the pattern is not found 525 * within the specified horizon. 526 * @throws IllegalStateException 527 * if the {@code Scanner} is closed. 528 * @throws IllegalArgumentException 529 * if {@code horizon} is less than zero. 530 * @see #findWithinHorizon(Pattern, int) 531 */ 532 public String findWithinHorizon(String pattern, int horizon) { 533 return findWithinHorizon(Pattern.compile(pattern), horizon); 534 } 535 536 /** 537 * Returns whether this {@code Scanner} has one or more tokens remaining to parse. 538 * This method will block if the data is still being read. 539 * 540 * @return {@code true} if this {@code Scanner} has one or more tokens remaining, 541 * otherwise {@code false}. 542 * @throws IllegalStateException 543 * if the {@code Scanner} has been closed. 544 */ 545 public boolean hasNext() { 546 return hasNext(ANY_PATTERN); 547 } 548 549 /** 550 * Returns whether this {@code Scanner} has one or more tokens remaining to parse 551 * and the next token matches the given pattern. This method will block if the data is 552 * still being read. 553 * 554 * @param pattern 555 * the pattern to check for. 556 * @return {@code true} if this {@code Scanner} has more tokens and the next token 557 * matches the pattern, {@code false} otherwise. 558 * @throws IllegalStateException 559 * if the {@code Scanner} has been closed. 560 */ 561 public boolean hasNext(Pattern pattern) { 562 checkClosed(); 563 checkNull(pattern); 564 matchSuccessful = false; 565 saveCurrentStatus(); 566 // if the next token exists, set the match region, otherwise return 567 // false 568 if (!setTokenRegion()) { 569 recoverPreviousStatus(); 570 return false; 571 } 572 matcher.usePattern(pattern); 573 boolean hasNext = false; 574 // check whether next token matches the specified pattern 575 if (matcher.matches()) { 576 cachehasNextIndex = findStartIndex; 577 matchSuccessful = true; 578 hasNext = true; 579 } 580 recoverPreviousStatus(); 581 return hasNext; 582 } 583 584 /** 585 * Returns {@code true} if this {@code Scanner} has one or more tokens remaining to parse 586 * and the next token matches a pattern compiled from the given string. This method will 587 * block if the data is still being read. This call is equivalent to 588 * {@code hasNext(Pattern.compile(pattern))}. 589 * 590 * @param pattern 591 * the string specifying the pattern to scan for 592 * @return {@code true} if the specified pattern matches this {@code Scanner}'s 593 * next token, {@code false} otherwise. 594 * @throws IllegalStateException 595 * if the {@code Scanner} has been closed. 596 */ 597 public boolean hasNext(String pattern) { 598 return hasNext(Pattern.compile(pattern)); 599 } 600 601 /** 602 * Returns whether the next token can be translated into a valid 603 * {@code BigDecimal}. 604 * 605 * @return {@code true} if the next token can be translated into a valid 606 * {@code BigDecimal}, otherwise {@code false.} 607 * @throws IllegalStateException 608 * if the {@code Scanner} has been closed. 609 */ 610 public boolean hasNextBigDecimal() { 611 Pattern floatPattern = getFloatPattern(); 612 boolean isBigDecimalValue = false; 613 if (hasNext(floatPattern)) { 614 String floatString = matcher.group(); 615 floatString = removeLocaleInfoFromFloat(floatString); 616 try { 617 cacheHasNextValue = new BigDecimal(floatString); 618 isBigDecimalValue = true; 619 } catch (NumberFormatException e) { 620 matchSuccessful = false; 621 } 622 } 623 return isBigDecimalValue; 624 } 625 626 /** 627 * Returns whether the next token can be translated into a valid 628 * {@code BigInteger} in the default radix. 629 * 630 * @return {@code true} if the next token can be translated into a valid 631 * {@code BigInteger}, otherwise {@code false}. 632 * @throws IllegalStateException 633 * if the {@code Scanner} has been closed. 634 */ 635 public boolean hasNextBigInteger() { 636 return hasNextBigInteger(integerRadix); 637 } 638 639 /** 640 * Returns whether the next token can be translated into a valid 641 * {@code BigInteger} in the specified radix. 642 * 643 * @param radix 644 * the radix used to translate the token into a 645 * {@code BigInteger}. 646 * @return {@code true} if the next token can be translated into a valid 647 * {@code BigInteger}, otherwise {@code false}. 648 * @throws IllegalStateException 649 * if the {@code Scanner} has been closed. 650 */ 651 public boolean hasNextBigInteger(int radix) { 652 Pattern integerPattern = getIntegerPattern(radix); 653 boolean isBigIntegerValue = false; 654 if (hasNext(integerPattern)) { 655 String intString = matcher.group(); 656 intString = removeLocaleInfo(intString, DataType.INT); 657 try { 658 cacheHasNextValue = new BigInteger(intString, radix); 659 isBigIntegerValue = true; 660 } catch (NumberFormatException e) { 661 matchSuccessful = false; 662 } 663 } 664 return isBigIntegerValue; 665 } 666 667 /** 668 * Returns whether the next token can be translated into a valid 669 * {@code boolean} value. 670 * 671 * @return {@code true} if the next token can be translated into a valid 672 * {@code boolean} value, otherwise {@code false}. 673 * @throws IllegalStateException 674 * if the {@code Scanner} has been closed. 675 */ 676 public boolean hasNextBoolean() { 677 return hasNext(BOOLEAN_PATTERN); 678 } 679 680 /** 681 * Returns whether the next token can be translated into a valid 682 * {@code byte} value in the default radix. 683 * 684 * @return {@code true} if the next token can be translated into a valid 685 * {@code byte} value, otherwise {@code false}. 686 * @throws IllegalStateException 687 * if the {@code Scanner} has been closed. 688 */ 689 public boolean hasNextByte() { 690 return hasNextByte(integerRadix); 691 } 692 693 /** 694 * Returns whether the next token can be translated into a valid 695 * {@code byte} value in the specified radix. 696 * 697 * @param radix 698 * the radix used to translate the token into a {@code byte} 699 * value 700 * @return {@code true} if the next token can be translated into a valid 701 * {@code byte} value, otherwise {@code false}. 702 * @throws IllegalStateException 703 * if the {@code Scanner} has been closed. 704 */ 705 public boolean hasNextByte(int radix) { 706 Pattern integerPattern = getIntegerPattern(radix); 707 boolean isByteValue = false; 708 if (hasNext(integerPattern)) { 709 String intString = matcher.group(); 710 intString = removeLocaleInfo(intString, DataType.INT); 711 try { 712 cacheHasNextValue = Byte.valueOf(intString, radix); 713 isByteValue = true; 714 } catch (NumberFormatException e) { 715 matchSuccessful = false; 716 } 717 } 718 return isByteValue; 719 } 720 721 /** 722 * Returns whether the next token translated into a valid {@code double} 723 * value. 724 * 725 * @return {@code true} if the next token can be translated into a valid 726 * {@code double} value, otherwise {@code false}. 727 * @throws IllegalStateException 728 * if the {@code Scanner} has been closed. 729 */ 730 public boolean hasNextDouble() { 731 Pattern floatPattern = getFloatPattern(); 732 boolean isDoubleValue = false; 733 if (hasNext(floatPattern)) { 734 String floatString = matcher.group(); 735 floatString = removeLocaleInfoFromFloat(floatString); 736 try { 737 cacheHasNextValue = Double.valueOf(floatString); 738 isDoubleValue = true; 739 } catch (NumberFormatException e) { 740 matchSuccessful = false; 741 } 742 } 743 return isDoubleValue; 744 } 745 746 /** 747 * Returns whether the next token can be translated into a valid 748 * {@code float} value. 749 * 750 * @return {@code true} if the next token can be translated into a valid 751 * {@code float} value, otherwise {@code false}. 752 * @throws IllegalStateException 753 * if the {@code Scanner} has been closed. 754 */ 755 public boolean hasNextFloat() { 756 Pattern floatPattern = getFloatPattern(); 757 boolean isFloatValue = false; 758 if (hasNext(floatPattern)) { 759 String floatString = matcher.group(); 760 floatString = removeLocaleInfoFromFloat(floatString); 761 try { 762 cacheHasNextValue = Float.valueOf(floatString); 763 isFloatValue = true; 764 } catch (NumberFormatException e) { 765 matchSuccessful = false; 766 } 767 } 768 return isFloatValue; 769 } 770 771 /** 772 * Returns whether the next token can be translated into a valid {@code int} 773 * value in the default radix. 774 * 775 * @return {@code true} if the next token can be translated into a valid 776 * {@code int} value, otherwise {@code false}. 777 * @throws IllegalStateException 778 * if the {@code Scanner} has been closed, 779 */ 780 public boolean hasNextInt() { 781 return hasNextInt(integerRadix); 782 } 783 784 /** 785 * Returns whether the next token can be translated into a valid {@code int} 786 * value in the specified radix. 787 * 788 * @param radix 789 * the radix used to translate the token into an {@code int} 790 * value. 791 * @return {@code true} if the next token in this {@code Scanner}'s input can be 792 * translated into a valid {@code int} value, otherwise 793 * {@code false}. 794 * @throws IllegalStateException 795 * if the {@code Scanner} has been closed. 796 */ 797 public boolean hasNextInt(int radix) { 798 Pattern integerPattern = getIntegerPattern(radix); 799 boolean isIntValue = false; 800 if (hasNext(integerPattern)) { 801 String intString = matcher.group(); 802 intString = removeLocaleInfo(intString, DataType.INT); 803 try { 804 cacheHasNextValue = Integer.valueOf(intString, radix); 805 isIntValue = true; 806 } catch (NumberFormatException e) { 807 matchSuccessful = false; 808 } 809 } 810 return isIntValue; 811 } 812 813 /** 814 * Returns whether there is a line terminator in the input. 815 * This method may block. 816 * 817 * @return {@code true} if there is a line terminator in the input, 818 * otherwise, {@code false}. 819 * @throws IllegalStateException 820 * if the {@code Scanner} is closed. 821 */ 822 public boolean hasNextLine() { 823 checkClosed(); 824 matcher.usePattern(LINE_PATTERN); 825 matcher.region(findStartIndex, bufferLength); 826 827 boolean hasNextLine = false; 828 while (true) { 829 if (matcher.find()) { 830 if (inputExhausted || matcher.end() != bufferLength) { 831 matchSuccessful = true; 832 hasNextLine = true; 833 break; 834 } 835 } else { 836 if (inputExhausted) { 837 matchSuccessful = false; 838 break; 839 } 840 } 841 if (!inputExhausted) { 842 readMore(); 843 resetMatcher(); 844 } 845 } 846 return hasNextLine; 847 } 848 849 /** 850 * Returns whether the next token can be translated into a valid 851 * {@code long} value in the default radix. 852 * 853 * @return {@code true} if the next token can be translated into a valid 854 * {@code long} value, otherwise {@code false}. 855 * @throws IllegalStateException 856 * if the {@code Scanner} has been closed. 857 */ 858 public boolean hasNextLong() { 859 return hasNextLong(integerRadix); 860 } 861 862 /** 863 * Returns whether the next token can be translated into a valid 864 * {@code long} value in the specified radix. 865 * 866 * @param radix 867 * the radix used to translate the token into a {@code long} 868 * value. 869 * @return {@code true} if the next token can be translated into a valid 870 * {@code long} value, otherwise {@code false}. 871 * @throws IllegalStateException 872 * if the {@code Scanner} has been closed. 873 */ 874 public boolean hasNextLong(int radix) { 875 Pattern integerPattern = getIntegerPattern(radix); 876 boolean isLongValue = false; 877 if (hasNext(integerPattern)) { 878 String intString = matcher.group(); 879 intString = removeLocaleInfo(intString, DataType.INT); 880 try { 881 cacheHasNextValue = Long.valueOf(intString, radix); 882 isLongValue = true; 883 } catch (NumberFormatException e) { 884 matchSuccessful = false; 885 } 886 } 887 return isLongValue; 888 } 889 890 /** 891 * Returns whether the next token can be translated into a valid 892 * {@code short} value in the default radix. 893 * 894 * @return {@code true} if the next token can be translated into a valid 895 * {@code short} value, otherwise {@code false}. 896 * @throws IllegalStateException 897 * if the {@code Scanner} has been closed. 898 */ 899 public boolean hasNextShort() { 900 return hasNextShort(integerRadix); 901 } 902 903 /** 904 * Returns whether the next token can be translated into a valid 905 * {@code short} value in the specified radix. 906 * 907 * @param radix 908 * the radix used to translate the token into a {@code short} 909 * value. 910 * @return {@code true} if the next token can be translated into a valid 911 * {@code short} value, otherwise {@code false}. 912 * @throws IllegalStateException 913 * if the {@code Scanner} has been closed. 914 */ 915 public boolean hasNextShort(int radix) { 916 Pattern integerPattern = getIntegerPattern(radix); 917 boolean isShortValue = false; 918 if (hasNext(integerPattern)) { 919 String intString = matcher.group(); 920 intString = removeLocaleInfo(intString, DataType.INT); 921 try { 922 cacheHasNextValue = Short.valueOf(intString, radix); 923 isShortValue = true; 924 } catch (NumberFormatException e) { 925 matchSuccessful = false; 926 } 927 } 928 return isShortValue; 929 } 930 931 /** 932 * Returns the last {@code IOException} that was raised while reading from the underlying 933 * input. 934 * 935 * @return the last thrown {@code IOException}, or {@code null} if none was thrown. 936 */ 937 public IOException ioException() { 938 return lastIOException; 939 } 940 941 /** 942 * Return the {@code Locale} of this {@code Scanner}. 943 * 944 * @return the {@code Locale} of this {@code Scanner}. 945 */ 946 public Locale locale() { 947 return locale; 948 } 949 950 /** 951 * Returns the result of the last matching operation. 952 * <p> 953 * The next* and find* methods return the match result in the case of a 954 * successful match. 955 * 956 * @return the match result of the last successful match operation 957 * @throws IllegalStateException 958 * if the match result is not available, of if the last match 959 * was not successful. 960 */ 961 public MatchResult match() { 962 if (!matchSuccessful) { 963 throw new IllegalStateException(); 964 } 965 return matcher.toMatchResult(); 966 } 967 968 /** 969 * Returns the next token. The token will be both prefixed and postfixed by 970 * the delimiter that is currently being used (or a string that matches the 971 * delimiter pattern). This method will block if input is being read. 972 * 973 * @return the next complete token. 974 * @throws IllegalStateException 975 * if this {@code Scanner} has been closed. 976 * @throws NoSuchElementException 977 * if input has been exhausted. 978 */ 979 public String next() { 980 return next(ANY_PATTERN); 981 } 982 983 /** 984 * Returns the next token if it matches the specified pattern. The token 985 * will be both prefixed and postfixed by the delimiter that is currently 986 * being used (or a string that matches the delimiter pattern). This method will block 987 * if input is being read. 988 * 989 * @param pattern 990 * the specified pattern to scan. 991 * @return the next token. 992 * @throws IllegalStateException 993 * if this {@code Scanner} has been closed. 994 * @throws NoSuchElementException 995 * if input has been exhausted. 996 * @throws InputMismatchException 997 * if the next token does not match the pattern given. 998 */ 999 public String next(Pattern pattern) { 1000 checkClosed(); 1001 checkNull(pattern); 1002 matchSuccessful = false; 1003 saveCurrentStatus(); 1004 if (!setTokenRegion()) { 1005 recoverPreviousStatus(); 1006 // if setting match region fails 1007 throw new NoSuchElementException(); 1008 } 1009 matcher.usePattern(pattern); 1010 if (!matcher.matches()) { 1011 recoverPreviousStatus(); 1012 throw new InputMismatchException(); 1013 1014 } 1015 matchSuccessful = true; 1016 return matcher.group(); 1017 } 1018 1019 /** 1020 * Returns the next token if it matches the specified pattern. The token 1021 * will be both prefixed and postfixed by the delimiter that is currently 1022 * being used (or a string that matches the delimiter pattern). This method will block 1023 * if input is being read. Calling this method is equivalent to 1024 * {@code next(Pattern.compile(pattern))}. 1025 * 1026 * @param pattern 1027 * the string specifying the pattern to scan for. 1028 * @return the next token. 1029 * @throws IllegalStateException 1030 * if this {@code Scanner} has been closed. 1031 * @throws NoSuchElementException 1032 * if input has been exhausted. 1033 * @throws InputMismatchException 1034 * if the next token does not match the pattern given. 1035 */ 1036 public String next(String pattern) { 1037 return next(Pattern.compile(pattern)); 1038 } 1039 1040 /** 1041 * Returns the next token as a {@code BigDecimal}. This method will block if input is 1042 * being read. If the next token can be translated into a {@code BigDecimal} 1043 * the following is done: All {@code Locale}-specific prefixes, group separators, 1044 * and {@code Locale}-specific suffixes are removed. Then non-ASCII digits are 1045 * mapped into ASCII digits via {@link Character#digit(char, int)}, and a 1046 * negative sign (-) is added if the {@code Locale}-specific negative prefix or 1047 * suffix was present. Finally the resulting string is passed to 1048 * {@code BigDecimal(String) }. 1049 * 1050 * @return the next token as a {@code BigDecimal}. 1051 * @throws IllegalStateException 1052 * if this {@code Scanner} has been closed. 1053 * @throws NoSuchElementException 1054 * if input has been exhausted. 1055 * @throws InputMismatchException 1056 * if the next token can not be translated into a valid 1057 * {@code BigDecimal}. 1058 */ 1059 public BigDecimal nextBigDecimal() { 1060 checkClosed(); 1061 Object obj = cacheHasNextValue; 1062 cacheHasNextValue = null; 1063 if (obj instanceof BigDecimal) { 1064 findStartIndex = cachehasNextIndex; 1065 return (BigDecimal) obj; 1066 } 1067 Pattern floatPattern = getFloatPattern(); 1068 String floatString = next(floatPattern); 1069 floatString = removeLocaleInfoFromFloat(floatString); 1070 BigDecimal bigDecimalValue; 1071 try { 1072 bigDecimalValue = new BigDecimal(floatString); 1073 } catch (NumberFormatException e) { 1074 matchSuccessful = false; 1075 recoverPreviousStatus(); 1076 throw new InputMismatchException(); 1077 } 1078 return bigDecimalValue; 1079 } 1080 1081 /** 1082 * Returns the next token as a {@code BigInteger}. This method will block if input is 1083 * being read. Equivalent to {@code nextBigInteger(DEFAULT_RADIX)}. 1084 * 1085 * @return the next token as {@code BigInteger}. 1086 * @throws IllegalStateException 1087 * if this {@code Scanner} has been closed. 1088 * @throws NoSuchElementException 1089 * if input has been exhausted. 1090 * @throws InputMismatchException 1091 * if the next token can not be translated into a valid 1092 * {@code BigInteger}. 1093 */ 1094 public BigInteger nextBigInteger() { 1095 return nextBigInteger(integerRadix); 1096 } 1097 1098 /** 1099 * Returns the next token as a {@code BigInteger} with the specified radix. 1100 * This method will block if input is being read. If the next token can be translated 1101 * into a {@code BigInteger} the following is done: All {@code Locale}-specific 1102 * prefixes, group separators, and {@code Locale}-specific suffixes are removed. 1103 * Then non-ASCII digits are mapped into ASCII digits via 1104 * {@link Character#digit(char, int)}, and a negative sign (-) is added if the 1105 * {@code Locale}-specific negative prefix or suffix was present. Finally the 1106 * resulting String is passed to {@link BigInteger#BigInteger(String, int)}} 1107 * with the specified radix. 1108 * 1109 * @param radix 1110 * the radix used to translate the token into a 1111 * {@code BigInteger}. 1112 * @return the next token as a {@code BigInteger} 1113 * @throws IllegalStateException 1114 * if this {@code Scanner} has been closed. 1115 * @throws NoSuchElementException 1116 * if input has been exhausted. 1117 * @throws InputMismatchException 1118 * if the next token can not be translated into a valid 1119 * {@code BigInteger}. 1120 */ 1121 public BigInteger nextBigInteger(int radix) { 1122 checkClosed(); 1123 Object obj = cacheHasNextValue; 1124 cacheHasNextValue = null; 1125 if (obj instanceof BigInteger) { 1126 findStartIndex = cachehasNextIndex; 1127 return (BigInteger) obj; 1128 } 1129 Pattern integerPattern = getIntegerPattern(radix); 1130 String intString = next(integerPattern); 1131 intString = removeLocaleInfo(intString, DataType.INT); 1132 BigInteger bigIntegerValue; 1133 try { 1134 bigIntegerValue = new BigInteger(intString, radix); 1135 } catch (NumberFormatException e) { 1136 matchSuccessful = false; 1137 recoverPreviousStatus(); 1138 throw new InputMismatchException(); 1139 } 1140 return bigIntegerValue; 1141 } 1142 1143 /** 1144 * Returns the next token as a {@code boolean}. This method will block if input is 1145 * being read. 1146 * 1147 * @return the next token as a {@code boolean}. 1148 * @throws IllegalStateException 1149 * if this {@code Scanner} has been closed. 1150 * @throws NoSuchElementException 1151 * if input has been exhausted. 1152 * @throws InputMismatchException 1153 * if the next token can not be translated into a valid 1154 * {@code boolean} value. 1155 */ 1156 public boolean nextBoolean() { 1157 return Boolean.parseBoolean(next(BOOLEAN_PATTERN)); 1158 } 1159 1160 /** 1161 * Returns the next token as a {@code byte}. This method will block if input is being 1162 * read. Equivalent to {@code nextByte(DEFAULT_RADIX)}. 1163 * 1164 * @return the next token as a {@code byte}. 1165 * @throws IllegalStateException 1166 * if this {@code Scanner} has been closed. 1167 * @throws NoSuchElementException 1168 * if input has been exhausted. 1169 * @throws InputMismatchException 1170 * if the next token can not be translated into a valid 1171 * {@code byte} value. 1172 */ 1173 public byte nextByte() { 1174 return nextByte(integerRadix); 1175 } 1176 1177 /** 1178 * Returns the next token as a {@code byte} with the specified radix. Will 1179 * block if input is being read. If the next token can be translated into a 1180 * {@code byte} the following is done: All {@code Locale}-specific prefixes, group 1181 * separators, and {@code Locale}-specific suffixes are removed. Then non-ASCII 1182 * digits are mapped into ASCII digits via 1183 * {@link Character#digit(char, int)}, and a negative sign (-) is added if the 1184 * {@code Locale}-specific negative prefix or suffix was present. Finally the 1185 * resulting String is passed to {@link Byte#parseByte(String, int)}} with 1186 * the specified radix. 1187 * 1188 * @param radix 1189 * the radix used to translate the token into {@code byte} value. 1190 * @return the next token as a {@code byte}. 1191 * @throws IllegalStateException 1192 * if this {@code Scanner} has been closed. 1193 * @throws NoSuchElementException 1194 * if input has been exhausted. 1195 * @throws InputMismatchException 1196 * if the next token can not be translated into a valid 1197 * {@code byte} value. 1198 */ 1199 @SuppressWarnings("boxing") 1200 public byte nextByte(int radix) { 1201 checkClosed(); 1202 Object obj = cacheHasNextValue; 1203 cacheHasNextValue = null; 1204 if (obj instanceof Byte) { 1205 findStartIndex = cachehasNextIndex; 1206 return (Byte) obj; 1207 } 1208 Pattern integerPattern = getIntegerPattern(radix); 1209 String intString = next(integerPattern); 1210 intString = removeLocaleInfo(intString, DataType.INT); 1211 byte byteValue = 0; 1212 try { 1213 byteValue = Byte.parseByte(intString, radix); 1214 } catch (NumberFormatException e) { 1215 matchSuccessful = false; 1216 recoverPreviousStatus(); 1217 throw new InputMismatchException(); 1218 } 1219 return byteValue; 1220 } 1221 1222 /** 1223 * Returns the next token as a {@code double}. This method will block if input is being 1224 * read. If the next token can be translated into a {@code double} the 1225 * following is done: All {@code Locale}-specific prefixes, group separators, and 1226 * {@code Locale}-specific suffixes are removed. Then non-ASCII digits are mapped 1227 * into ASCII digits via {@link Character#digit(char, int)}, and a negative 1228 * sign (-) is added if the {@code Locale}-specific negative prefix or suffix was 1229 * present. Finally the resulting String is passed to 1230 * {@link Double#parseDouble(String)}}. If the token matches the localized 1231 * NaN or infinity strings, it is also passed to 1232 * {@link Double#parseDouble(String)}}. 1233 * 1234 * @return the next token as a {@code double}. 1235 * @throws IllegalStateException 1236 * if this {@code Scanner} has been closed. 1237 * @throws NoSuchElementException 1238 * if input has been exhausted. 1239 * @throws InputMismatchException 1240 * if the next token can not be translated into a valid 1241 * {@code double} value. 1242 */ 1243 @SuppressWarnings("boxing") 1244 public double nextDouble() { 1245 checkClosed(); 1246 Object obj = cacheHasNextValue; 1247 cacheHasNextValue = null; 1248 if (obj instanceof Double) { 1249 findStartIndex = cachehasNextIndex; 1250 return (Double) obj; 1251 } 1252 Pattern floatPattern = getFloatPattern(); 1253 String floatString = next(floatPattern); 1254 floatString = removeLocaleInfoFromFloat(floatString); 1255 double doubleValue = 0; 1256 try { 1257 doubleValue = Double.parseDouble(floatString); 1258 } catch (NumberFormatException e) { 1259 matchSuccessful = false; 1260 recoverPreviousStatus(); 1261 throw new InputMismatchException(); 1262 } 1263 return doubleValue; 1264 } 1265 1266 /** 1267 * Returns the next token as a {@code float}. This method will block if input is being 1268 * read. If the next token can be translated into a {@code float} the 1269 * following is done: All {@code Locale}-specific prefixes, group separators, and 1270 * {@code Locale}-specific suffixes are removed. Then non-ASCII digits are mapped 1271 * into ASCII digits via {@link Character#digit(char, int)}, and a negative 1272 * sign (-) is added if the {@code Locale}-specific negative prefix or suffix was 1273 * present. Finally the resulting String is passed to 1274 * {@link Float#parseFloat(String)}}.If the token matches the localized NaN 1275 * or infinity strings, it is also passed to 1276 * {@link Float#parseFloat(String)}}. 1277 * 1278 * @return the next token as a {@code float}. 1279 * @throws IllegalStateException 1280 * if this {@code Scanner} has been closed. 1281 * @throws NoSuchElementException 1282 * if input has been exhausted. 1283 * @throws InputMismatchException 1284 * if the next token can not be translated into a valid 1285 * {@code float} value. 1286 */ 1287 @SuppressWarnings("boxing") 1288 public float nextFloat() { 1289 checkClosed(); 1290 Object obj = cacheHasNextValue; 1291 cacheHasNextValue = null; 1292 if (obj instanceof Float) { 1293 findStartIndex = cachehasNextIndex; 1294 return (Float) obj; 1295 } 1296 Pattern floatPattern = getFloatPattern(); 1297 String floatString = next(floatPattern); 1298 floatString = removeLocaleInfoFromFloat(floatString); 1299 float floatValue = 0; 1300 try { 1301 floatValue = Float.parseFloat(floatString); 1302 } catch (NumberFormatException e) { 1303 matchSuccessful = false; 1304 recoverPreviousStatus(); 1305 throw new InputMismatchException(); 1306 } 1307 return floatValue; 1308 } 1309 1310 /** 1311 * Returns the next token as an {@code int}. This method will block if input is being 1312 * read. Equivalent to {@code nextInt(DEFAULT_RADIX)}. 1313 * 1314 * @return the next token as an {@code int} 1315 * @throws IllegalStateException 1316 * if this {@code Scanner} has been closed. 1317 * @throws NoSuchElementException 1318 * if input has been exhausted. 1319 * @throws InputMismatchException 1320 * if the next token can not be translated into a valid 1321 * {@code int} value. 1322 */ 1323 public int nextInt() { 1324 return nextInt(integerRadix); 1325 } 1326 1327 /** 1328 * Returns the next token as an {@code int} with the specified radix. This method will 1329 * block if input is being read. If the next token can be translated into an 1330 * {@code int} the following is done: All {@code Locale}-specific prefixes, group 1331 * separators, and {@code Locale}-specific suffixes are removed. Then non-ASCII 1332 * digits are mapped into ASCII digits via 1333 * {@link Character#digit(char, int)}, and a negative sign (-) is added if the 1334 * {@code Locale}-specific negative prefix or suffix was present. Finally the 1335 * resulting String is passed to {@link Integer#parseInt(String, int)} with 1336 * the specified radix. 1337 * 1338 * @param radix 1339 * the radix used to translate the token into an {@code int} 1340 * value. 1341 * @return the next token as an {@code int}. 1342 * @throws IllegalStateException 1343 * if this {@code Scanner} has been closed. 1344 * @throws NoSuchElementException 1345 * if input has been exhausted. 1346 * @throws InputMismatchException 1347 * if the next token can not be translated into a valid 1348 * {@code int} value. 1349 */ 1350 @SuppressWarnings("boxing") 1351 public int nextInt(int radix) { 1352 checkClosed(); 1353 Object obj = cacheHasNextValue; 1354 cacheHasNextValue = null; 1355 if (obj instanceof Integer) { 1356 findStartIndex = cachehasNextIndex; 1357 return (Integer) obj; 1358 } 1359 Pattern integerPattern = getIntegerPattern(radix); 1360 String intString = next(integerPattern); 1361 intString = removeLocaleInfo(intString, DataType.INT); 1362 int intValue = 0; 1363 try { 1364 intValue = Integer.parseInt(intString, radix); 1365 } catch (NumberFormatException e) { 1366 matchSuccessful = false; 1367 recoverPreviousStatus(); 1368 throw new InputMismatchException(); 1369 } 1370 return intValue; 1371 } 1372 1373 /** 1374 * Returns the skipped input and advances the {@code Scanner} to the beginning of 1375 * the next line. The returned result will exclude any line terminator. When 1376 * searching, if no line terminator is found, then a large amount of input 1377 * will be cached. If no line at all can be found, a {@code NoSuchElementException} 1378 * will be thrown. 1379 * 1380 * @return the skipped line. 1381 * @throws IllegalStateException 1382 * if the {@code Scanner} is closed. 1383 * @throws NoSuchElementException 1384 * if no line can be found, e.g. when input is an empty string. 1385 */ 1386 public String nextLine() { 1387 checkClosed(); 1388 1389 matcher.usePattern(LINE_PATTERN); 1390 matcher.region(findStartIndex, bufferLength); 1391 String result = null; 1392 while (true) { 1393 if (matcher.find()) { 1394 if (inputExhausted || matcher.end() != bufferLength 1395 || bufferLength < buffer.capacity()) { 1396 matchSuccessful = true; 1397 findStartIndex = matcher.end(); 1398 result = matcher.group(); 1399 break; 1400 } 1401 } else { 1402 if (inputExhausted) { 1403 matchSuccessful = false; 1404 throw new NoSuchElementException(); 1405 } 1406 } 1407 if (!inputExhausted) { 1408 readMore(); 1409 resetMatcher(); 1410 } 1411 } 1412 // Find text without line terminator here. 1413 if (result != null) { 1414 Matcher terminatorMatcher = LINE_TERMINATOR.matcher(result); 1415 if (terminatorMatcher.find()) { 1416 result = result.substring(0, terminatorMatcher.start()); 1417 } 1418 } 1419 return result; 1420 } 1421 1422 /** 1423 * Returns the next token as a {@code long}. This method will block if input is being 1424 * read. Equivalent to {@code nextLong(DEFAULT_RADIX)}. 1425 * 1426 * @return the next token as a {@code long}. 1427 * @throws IllegalStateException 1428 * if this {@code Scanner} has been closed. 1429 * @throws NoSuchElementException 1430 * if input has been exhausted. 1431 * @throws InputMismatchException 1432 * if the next token can not be translated into a valid 1433 * {@code long} value. 1434 */ 1435 public long nextLong() { 1436 return nextLong(integerRadix); 1437 } 1438 1439 /** 1440 * Returns the next token as a {@code long} with the specified radix. This method will 1441 * block if input is being read. If the next token can be translated into a 1442 * {@code long} the following is done: All {@code Locale}-specific prefixes, group 1443 * separators, and {@code Locale}-specific suffixes are removed. Then non-ASCII 1444 * digits are mapped into ASCII digits via 1445 * {@link Character#digit(char, int)}, and a negative sign (-) is added if the 1446 * {@code Locale}-specific negative prefix or suffix was present. Finally the 1447 * resulting String is passed to {@link Long#parseLong(String, int)}} with 1448 * the specified radix. 1449 * 1450 * @param radix 1451 * the radix used to translate the token into a {@code long} 1452 * value. 1453 * @return the next token as a {@code long}. 1454 * @throws IllegalStateException 1455 * if this {@code Scanner} has been closed. 1456 * @throws NoSuchElementException 1457 * if input has been exhausted. 1458 * @throws InputMismatchException 1459 * if the next token can not be translated into a valid 1460 * {@code long} value. 1461 */ 1462 @SuppressWarnings("boxing") 1463 public long nextLong(int radix) { 1464 checkClosed(); 1465 Object obj = cacheHasNextValue; 1466 cacheHasNextValue = null; 1467 if (obj instanceof Long) { 1468 findStartIndex = cachehasNextIndex; 1469 return (Long) obj; 1470 } 1471 Pattern integerPattern = getIntegerPattern(radix); 1472 String intString = next(integerPattern); 1473 intString = removeLocaleInfo(intString, DataType.INT); 1474 long longValue = 0; 1475 try { 1476 longValue = Long.parseLong(intString, radix); 1477 } catch (NumberFormatException e) { 1478 matchSuccessful = false; 1479 recoverPreviousStatus(); 1480 throw new InputMismatchException(); 1481 } 1482 return longValue; 1483 } 1484 1485 /** 1486 * Returns the next token as a {@code short}. This method will block if input is being 1487 * read. Equivalent to {@code nextShort(DEFAULT_RADIX)}. 1488 * 1489 * @return the next token as a {@code short}. 1490 * @throws IllegalStateException 1491 * if this {@code Scanner} has been closed. 1492 * @throws NoSuchElementException 1493 * if input has been exhausted. 1494 * @throws InputMismatchException 1495 * if the next token can not be translated into a valid 1496 * {@code short} value. 1497 */ 1498 public short nextShort() { 1499 return nextShort(integerRadix); 1500 } 1501 1502 /** 1503 * Returns the next token as a {@code short} with the specified radix. This method will 1504 * block if input is being read. If the next token can be translated into a 1505 * {@code short} the following is done: All {@code Locale}-specific prefixes, group 1506 * separators, and {@code Locale}-specific suffixes are removed. Then non-ASCII 1507 * digits are mapped into ASCII digits via 1508 * {@link Character#digit(char, int)}, and a negative sign (-) is added if the 1509 * {@code Locale}-specific negative prefix or suffix was present. Finally the 1510 * resulting String is passed to {@link Short#parseShort(String, int)}} 1511 * with the specified radix. 1512 * 1513 * @param radix 1514 * the radix used to translate the token into {@code short} 1515 * value. 1516 * @return the next token as a {@code short}. 1517 * @throws IllegalStateException 1518 * if this {@code Scanner} has been closed. 1519 * @throws NoSuchElementException 1520 * if input has been exhausted. 1521 * @throws InputMismatchException 1522 * if the next token can not be translated into a valid 1523 * {@code short} value. 1524 */ 1525 @SuppressWarnings("boxing") 1526 public short nextShort(int radix) { 1527 checkClosed(); 1528 Object obj = cacheHasNextValue; 1529 cacheHasNextValue = null; 1530 if (obj instanceof Short) { 1531 findStartIndex = cachehasNextIndex; 1532 return (Short) obj; 1533 } 1534 Pattern integerPattern = getIntegerPattern(radix); 1535 String intString = next(integerPattern); 1536 intString = removeLocaleInfo(intString, DataType.INT); 1537 short shortValue = 0; 1538 try { 1539 shortValue = Short.parseShort(intString, radix); 1540 } catch (NumberFormatException e) { 1541 matchSuccessful = false; 1542 recoverPreviousStatus(); 1543 throw new InputMismatchException(); 1544 } 1545 return shortValue; 1546 } 1547 1548 /** 1549 * Return the radix of this {@code Scanner}. 1550 * 1551 * @return the radix of this {@code Scanner} 1552 */ 1553 public int radix() { 1554 return integerRadix; 1555 } 1556 1557 /** 1558 * Tries to use specified pattern to match input starting from the current position. 1559 * The delimiter will be ignored. If a match is found, the matched input will be 1560 * skipped. If an anchored match of the specified pattern succeeds, the corresponding input 1561 * will also be skipped. Otherwise, a {@code NoSuchElementException} will be thrown. 1562 * Patterns that can match a lot of input may cause the {@code Scanner} to read 1563 * in a large amount of input. 1564 * 1565 * @param pattern 1566 * used to skip over input. 1567 * @return the {@code Scanner} itself. 1568 * @throws IllegalStateException 1569 * if the {@code Scanner} is closed. 1570 * @throws NoSuchElementException 1571 * if the specified pattern match fails. 1572 */ 1573 public Scanner skip(Pattern pattern) { 1574 checkClosed(); 1575 checkNull(pattern); 1576 matcher.usePattern(pattern); 1577 matcher.region(findStartIndex, bufferLength); 1578 while (true) { 1579 if (matcher.lookingAt()) { 1580 boolean matchInBuffer = matcher.end() < bufferLength 1581 || (matcher.end() == bufferLength && inputExhausted); 1582 if (matchInBuffer) { 1583 matchSuccessful = true; 1584 findStartIndex = matcher.end(); 1585 break; 1586 } 1587 } else { 1588 if (inputExhausted) { 1589 matchSuccessful = false; 1590 throw new NoSuchElementException(); 1591 } 1592 } 1593 if (!inputExhausted) { 1594 readMore(); 1595 resetMatcher(); 1596 } 1597 } 1598 return this; 1599 } 1600 1601 /** 1602 * Tries to use the specified string to construct a pattern and then uses 1603 * the constructed pattern to match input starting from the current position. The 1604 * delimiter will be ignored. This call is the same as invoke 1605 * {@code skip(Pattern.compile(pattern))}. 1606 * 1607 * @param pattern 1608 * the string used to construct a pattern which in turn is used to 1609 * match input. 1610 * @return the {@code Scanner} itself. 1611 * @throws IllegalStateException 1612 * if the {@code Scanner} is closed. 1613 */ 1614 public Scanner skip(String pattern) { 1615 return skip(Pattern.compile(pattern)); 1616 } 1617 1618 /** 1619 * Returns a string representation of this {@code Scanner}. The information 1620 * returned may be helpful for debugging. The format of the string is unspecified. 1621 * 1622 * @return a string representation of this {@code Scanner}. 1623 */ 1624 @Override 1625 public String toString() { 1626 return getClass().getName() + 1627 "[delimiter=" + delimiter + 1628 ",findStartIndex=" + findStartIndex + 1629 ",matchSuccessful=" + matchSuccessful + 1630 ",closed=" + closed + 1631 "]"; 1632 } 1633 1634 /** 1635 * Sets the delimiting pattern of this {@code Scanner}. 1636 * 1637 * @param pattern 1638 * the delimiting pattern to use. 1639 * @return this {@code Scanner}. 1640 */ 1641 public Scanner useDelimiter(Pattern pattern) { 1642 delimiter = pattern; 1643 return this; 1644 } 1645 1646 /** 1647 * Sets the delimiting pattern of this {@code Scanner} with a pattern compiled from 1648 * the supplied string value. 1649 * 1650 * @param pattern 1651 * a string from which a {@code Pattern} can be compiled. 1652 * @return this {@code Scanner}. 1653 */ 1654 public Scanner useDelimiter(String pattern) { 1655 return useDelimiter(Pattern.compile(pattern)); 1656 } 1657 1658 /** 1659 * Sets the {@code Locale} of this {@code Scanner} to a specified {@code Locale}. 1660 * 1661 * @param l 1662 * the specified {@code Locale} to use. 1663 * @return this {@code Scanner}. 1664 */ 1665 public Scanner useLocale(Locale l) { 1666 if (l == null) { 1667 throw new NullPointerException(); 1668 } 1669 this.locale = l; 1670 return this; 1671 } 1672 1673 /** 1674 * Sets the radix of this {@code Scanner} to the specified radix. 1675 * 1676 * @param radix 1677 * the specified radix to use. 1678 * @return this {@code Scanner}. 1679 */ 1680 public Scanner useRadix(int radix) { 1681 checkRadix(radix); 1682 this.integerRadix = radix; 1683 return this; 1684 } 1685 1686 private void checkRadix(int radix) { 1687 if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) { 1688 throw new IllegalArgumentException("Invalid radix: " + radix); 1689 } 1690 } 1691 1692 /** 1693 * Remove is not a supported operation on {@code Scanner}. 1694 * 1695 * @throws UnsupportedOperationException 1696 * if this method is invoked. 1697 */ 1698 public void remove() { 1699 throw new UnsupportedOperationException(); 1700 } 1701 1702 /* 1703 * Initialize some components. 1704 */ 1705 private void initialization() { 1706 buffer = CharBuffer.allocate(DEFAULT_TRUNK_SIZE); 1707 buffer.limit(0); 1708 matcher = delimiter.matcher(buffer); 1709 } 1710 1711 /* 1712 * Check the {@code Scanner}'s state, if it is closed, IllegalStateException will be 1713 * thrown. 1714 */ 1715 private void checkClosed() { 1716 if (closed) { 1717 throw new IllegalStateException(); 1718 } 1719 } 1720 1721 /* 1722 * Check the inputed pattern. If it is null, then a NullPointerException 1723 * will be thrown out. 1724 */ 1725 private void checkNull(Pattern pattern) { 1726 if (pattern == null) { 1727 throw new NullPointerException(); 1728 } 1729 } 1730 1731 /* 1732 * Change the matcher's string after reading input 1733 */ 1734 private void resetMatcher() { 1735 if (matcher == null) { 1736 matcher = delimiter.matcher(buffer); 1737 } else { 1738 matcher.reset(buffer); 1739 } 1740 matcher.region(findStartIndex, bufferLength); 1741 } 1742 1743 /* 1744 * Save the matcher's last find position 1745 */ 1746 private void saveCurrentStatus() { 1747 preStartIndex = findStartIndex; 1748 } 1749 1750 /* 1751 * Change the matcher's status to last find position 1752 */ 1753 private void recoverPreviousStatus() { 1754 findStartIndex = preStartIndex; 1755 } 1756 1757 /* 1758 * Get integer's pattern 1759 */ 1760 private Pattern getIntegerPattern(int radix) { 1761 checkRadix(radix); 1762 decimalFormat = (DecimalFormat) NumberFormat.getInstance(locale); 1763 1764 String allAvailableDigits = "0123456789abcdefghijklmnopqrstuvwxyz"; 1765 String ASCIIDigit = allAvailableDigits.substring(0, radix); 1766 String nonZeroASCIIDigit = allAvailableDigits.substring(1, radix); 1767 1768 StringBuilder digit = new StringBuilder("((?i)[").append(ASCIIDigit) 1769 .append("]|\\p{javaDigit})"); 1770 StringBuilder nonZeroDigit = new StringBuilder("((?i)[").append( 1771 nonZeroASCIIDigit).append("]|([\\p{javaDigit}&&[^0]]))"); 1772 StringBuilder numeral = getNumeral(digit, nonZeroDigit); 1773 1774 StringBuilder integer = new StringBuilder("(([-+]?(").append(numeral) 1775 .append(")))|(").append(addPositiveSign(numeral)).append(")|(") 1776 .append(addNegativeSign(numeral)).append(")"); 1777 1778 Pattern integerPattern = Pattern.compile(integer.toString()); 1779 return integerPattern; 1780 } 1781 1782 /* 1783 * Get pattern of float 1784 */ 1785 private Pattern getFloatPattern() { 1786 decimalFormat = (DecimalFormat) NumberFormat.getInstance(locale); 1787 1788 StringBuilder digit = new StringBuilder("([0-9]|(\\p{javaDigit}))"); 1789 StringBuilder nonZeroDigit = new StringBuilder("[\\p{javaDigit}&&[^0]]"); 1790 StringBuilder numeral = getNumeral(digit, nonZeroDigit); 1791 1792 String decimalSeparator = "\\" + decimalFormat.getDecimalFormatSymbols() 1793 .getDecimalSeparator(); 1794 StringBuilder decimalNumeral = new StringBuilder("(").append(numeral) 1795 .append("|").append(numeral) 1796 .append(decimalSeparator).append(digit).append("*+|").append( 1797 decimalSeparator).append(digit).append("++)"); 1798 StringBuilder exponent = new StringBuilder("([eE][+-]?").append(digit) 1799 .append("+)?"); 1800 1801 StringBuilder decimal = new StringBuilder("(([-+]?").append( 1802 decimalNumeral).append("(").append(exponent).append("?)") 1803 .append(")|(").append(addPositiveSign(decimalNumeral)).append( 1804 "(").append(exponent).append("?)").append(")|(") 1805 .append(addNegativeSign(decimalNumeral)).append("(").append( 1806 exponent).append("?)").append("))"); 1807 1808 StringBuilder hexFloat = new StringBuilder("([-+]?0[xX][0-9a-fA-F]*") 1809 .append("\\.").append( 1810 "[0-9a-fA-F]+([pP][-+]?[0-9]+)?)"); 1811 String localNaN = decimalFormat.getDecimalFormatSymbols().getNaN(); 1812 String localeInfinity = decimalFormat.getDecimalFormatSymbols() 1813 .getInfinity(); 1814 StringBuilder nonNumber = new StringBuilder("(NaN|\\Q").append(localNaN) 1815 .append("\\E|Infinity|\\Q").append(localeInfinity).append("\\E)"); 1816 StringBuilder singedNonNumber = new StringBuilder("((([-+]?(").append( 1817 nonNumber).append(")))|(").append(addPositiveSign(nonNumber)) 1818 .append(")|(").append(addNegativeSign(nonNumber)).append("))"); 1819 1820 StringBuilder floatString = new StringBuilder().append(decimal).append( 1821 "|").append(hexFloat).append("|").append(singedNonNumber); 1822 Pattern floatPattern = Pattern.compile(floatString.toString()); 1823 return floatPattern; 1824 } 1825 1826 private StringBuilder getNumeral(StringBuilder digit, 1827 StringBuilder nonZeroDigit) { 1828 String groupSeparator = "\\" 1829 + decimalFormat.getDecimalFormatSymbols() 1830 .getGroupingSeparator(); 1831 StringBuilder groupedNumeral = new StringBuilder("(").append( 1832 nonZeroDigit).append(digit).append("?").append(digit).append( 1833 "?(").append(groupSeparator).append(digit).append(digit) 1834 .append(digit).append(")+)"); 1835 StringBuilder numeral = new StringBuilder("((").append(digit).append( 1836 "++)|").append(groupedNumeral).append(")"); 1837 return numeral; 1838 } 1839 1840 /* 1841 * Add the locale specific positive prefixes and suffixes to the pattern 1842 */ 1843 private StringBuilder addPositiveSign(StringBuilder unSignNumeral) { 1844 String positivePrefix = ""; 1845 String positiveSuffix = ""; 1846 if (!decimalFormat.getPositivePrefix().isEmpty()) { 1847 positivePrefix = "\\Q" + decimalFormat.getPositivePrefix() + "\\E"; 1848 } 1849 if (!decimalFormat.getPositiveSuffix().isEmpty()) { 1850 positiveSuffix = "\\Q" + decimalFormat.getPositiveSuffix() + "\\E"; 1851 } 1852 StringBuilder signedNumeral = new StringBuilder() 1853 .append(positivePrefix).append(unSignNumeral).append( 1854 positiveSuffix); 1855 return signedNumeral; 1856 } 1857 1858 /* 1859 * Add the locale specific negative prefixes and suffixes to the pattern 1860 */ 1861 private StringBuilder addNegativeSign(StringBuilder unSignNumeral) { 1862 String negativePrefix = ""; 1863 String negativeSuffix = ""; 1864 if (!decimalFormat.getNegativePrefix().isEmpty()) { 1865 negativePrefix = "\\Q" + decimalFormat.getNegativePrefix() + "\\E"; 1866 } 1867 if (!decimalFormat.getNegativeSuffix().isEmpty()) { 1868 negativeSuffix = "\\Q" + decimalFormat.getNegativeSuffix() + "\\E"; 1869 } 1870 StringBuilder signedNumeral = new StringBuilder() 1871 .append(negativePrefix).append(unSignNumeral).append( 1872 negativeSuffix); 1873 return signedNumeral; 1874 } 1875 1876 /* 1877 * Remove locale related information from float String 1878 */ 1879 private String removeLocaleInfoFromFloat(String floatString) { 1880 // If the token is HexFloat 1881 if (-1 != floatString.indexOf('x') || -1 != floatString.indexOf('X')) { 1882 return floatString; 1883 } 1884 1885 int exponentIndex; 1886 String decimalNumeralString; 1887 String exponentString; 1888 // If the token is scientific notation 1889 if (-1 != (exponentIndex = floatString.indexOf('e')) 1890 || -1 != (exponentIndex = floatString.indexOf('E'))) { 1891 decimalNumeralString = floatString.substring(0, exponentIndex); 1892 exponentString = floatString.substring(exponentIndex + 1, 1893 floatString.length()); 1894 decimalNumeralString = removeLocaleInfo(decimalNumeralString, 1895 DataType.FLOAT); 1896 return decimalNumeralString + "e" + exponentString; 1897 } 1898 return removeLocaleInfo(floatString, DataType.FLOAT); 1899 } 1900 1901 /* 1902 * Remove the locale specific prefixes, group separators, and locale 1903 * specific suffixes from input string 1904 */ 1905 private String removeLocaleInfo(String token, DataType type) { 1906 StringBuilder tokenBuilder = new StringBuilder(token); 1907 boolean negative = removeLocaleSign(tokenBuilder); 1908 // Remove group separator 1909 String groupSeparator = String.valueOf(decimalFormat 1910 .getDecimalFormatSymbols().getGroupingSeparator()); 1911 int separatorIndex = -1; 1912 while (-1 != (separatorIndex = tokenBuilder.indexOf(groupSeparator))) { 1913 tokenBuilder.delete(separatorIndex, separatorIndex + 1); 1914 } 1915 // Remove decimal separator 1916 String decimalSeparator = String.valueOf(decimalFormat 1917 .getDecimalFormatSymbols().getDecimalSeparator()); 1918 separatorIndex = tokenBuilder.indexOf(decimalSeparator); 1919 StringBuilder result = new StringBuilder(""); 1920 if (DataType.INT == type) { 1921 for (int i = 0; i < tokenBuilder.length(); i++) { 1922 if (-1 != Character.digit(tokenBuilder.charAt(i), 1923 Character.MAX_RADIX)) { 1924 result.append(tokenBuilder.charAt(i)); 1925 } 1926 } 1927 } 1928 if (DataType.FLOAT == type) { 1929 if (tokenBuilder.toString().equals( 1930 decimalFormat.getDecimalFormatSymbols().getNaN())) { 1931 result.append("NaN"); 1932 } else if (tokenBuilder.toString().equals( 1933 decimalFormat.getDecimalFormatSymbols().getInfinity())) { 1934 result.append("Infinity"); 1935 } else { 1936 for (int i = 0; i < tokenBuilder.length(); i++) { 1937 if (-1 != Character.digit(tokenBuilder.charAt(i), 10)) { 1938 result.append(Character.digit(tokenBuilder.charAt(i), 1939 10)); 1940 } 1941 } 1942 } 1943 } 1944 // Token is NaN or Infinity 1945 if (result.length() == 0) { 1946 result = tokenBuilder; 1947 } 1948 if (-1 != separatorIndex) { 1949 result.insert(separatorIndex, "."); 1950 } 1951 // If input is negative 1952 if (negative) { 1953 result.insert(0, '-'); 1954 } 1955 return result.toString(); 1956 } 1957 1958 /* 1959 * Remove positive and negative sign from the parameter stringBuilder, and 1960 * return whether the input string is negative 1961 */ 1962 private boolean removeLocaleSign(StringBuilder tokenBuilder) { 1963 String positivePrefix = decimalFormat.getPositivePrefix(); 1964 String positiveSuffix = decimalFormat.getPositiveSuffix(); 1965 String negativePrefix = decimalFormat.getNegativePrefix(); 1966 String negativeSuffix = decimalFormat.getNegativeSuffix(); 1967 1968 if (tokenBuilder.indexOf("+") == 0) { 1969 tokenBuilder.delete(0, 1); 1970 } 1971 if (!positivePrefix.isEmpty() && tokenBuilder.indexOf(positivePrefix) == 0) { 1972 tokenBuilder.delete(0, positivePrefix.length()); 1973 } 1974 if (!positiveSuffix.isEmpty() 1975 && -1 != tokenBuilder.indexOf(positiveSuffix)) { 1976 tokenBuilder.delete( 1977 tokenBuilder.length() - positiveSuffix.length(), 1978 tokenBuilder.length()); 1979 } 1980 boolean negative = false; 1981 if (tokenBuilder.indexOf("-") == 0) { 1982 tokenBuilder.delete(0, 1); 1983 negative = true; 1984 } 1985 if (!negativePrefix.isEmpty() && tokenBuilder.indexOf(negativePrefix) == 0) { 1986 tokenBuilder.delete(0, negativePrefix.length()); 1987 negative = true; 1988 } 1989 if (!negativeSuffix.isEmpty() 1990 && -1 != tokenBuilder.indexOf(negativeSuffix)) { 1991 tokenBuilder.delete( 1992 tokenBuilder.length() - negativeSuffix.length(), 1993 tokenBuilder.length()); 1994 negative = true; 1995 } 1996 return negative; 1997 } 1998 1999 /* 2000 * Find the prefixed delimiter and posefixed delimiter in the input resource 2001 * and set the start index and end index of Matcher region. If postfixed 2002 * delimiter does not exist, the end index is set to be end of input. 2003 */ 2004 private boolean setTokenRegion() { 2005 // The position where token begins 2006 int tokenStartIndex = 0; 2007 // The position where token ends 2008 int tokenEndIndex = 0; 2009 // Use delimiter pattern 2010 matcher.usePattern(delimiter); 2011 matcher.region(findStartIndex, bufferLength); 2012 2013 tokenStartIndex = findPreDelimiter(); 2014 if (setHeadTokenRegion(tokenStartIndex)) { 2015 return true; 2016 } 2017 tokenEndIndex = findPostDelimiter(); 2018 // If the second delimiter is not found 2019 if (-1 == tokenEndIndex) { 2020 // Just first Delimiter Exists 2021 if (findStartIndex == bufferLength) { 2022 return false; 2023 } 2024 tokenEndIndex = bufferLength; 2025 findStartIndex = bufferLength; 2026 } 2027 2028 matcher.region(tokenStartIndex, tokenEndIndex); 2029 return true; 2030 } 2031 2032 /* 2033 * Find prefix delimiter 2034 */ 2035 private int findPreDelimiter() { 2036 int tokenStartIndex; 2037 boolean findComplete = false; 2038 while (!findComplete) { 2039 if (matcher.find()) { 2040 findComplete = true; 2041 // If just delimiter remains 2042 if (matcher.start() == findStartIndex 2043 && matcher.end() == bufferLength) { 2044 // If more input resource exists 2045 if (!inputExhausted) { 2046 readMore(); 2047 resetMatcher(); 2048 findComplete = false; 2049 } 2050 } 2051 } else { 2052 if (!inputExhausted) { 2053 readMore(); 2054 resetMatcher(); 2055 } else { 2056 return -1; 2057 } 2058 } 2059 } 2060 tokenStartIndex = matcher.end(); 2061 findStartIndex = matcher.end(); 2062 return tokenStartIndex; 2063 } 2064 2065 /* 2066 * Handle some special cases 2067 */ 2068 private boolean setHeadTokenRegion(int findIndex) { 2069 int tokenStartIndex; 2070 int tokenEndIndex; 2071 boolean setSuccess = false; 2072 // If no delimiter exists, but something exites in this scanner 2073 if (-1 == findIndex && preStartIndex != bufferLength) { 2074 tokenStartIndex = preStartIndex; 2075 tokenEndIndex = bufferLength; 2076 findStartIndex = bufferLength; 2077 matcher.region(tokenStartIndex, tokenEndIndex); 2078 setSuccess = true; 2079 } 2080 // If the first delimiter of scanner is not at the find start position 2081 if (-1 != findIndex && preStartIndex != matcher.start()) { 2082 tokenStartIndex = preStartIndex; 2083 tokenEndIndex = matcher.start(); 2084 findStartIndex = matcher.start(); 2085 // set match region and return 2086 matcher.region(tokenStartIndex, tokenEndIndex); 2087 setSuccess = true; 2088 } 2089 return setSuccess; 2090 } 2091 2092 /* 2093 * Find postfix delimiter 2094 */ 2095 private int findPostDelimiter() { 2096 int tokenEndIndex = 0; 2097 boolean findComplete = false; 2098 while (!findComplete) { 2099 if (matcher.find()) { 2100 findComplete = true; 2101 if (matcher.start() == findStartIndex 2102 && matcher.start() == matcher.end()) { 2103 findComplete = false; 2104 } 2105 } else { 2106 if (!inputExhausted) { 2107 readMore(); 2108 resetMatcher(); 2109 } else { 2110 return -1; 2111 } 2112 } 2113 } 2114 tokenEndIndex = matcher.start(); 2115 findStartIndex = matcher.start(); 2116 return tokenEndIndex; 2117 } 2118 2119 /* 2120 * Read more data from underlying Readable. If nothing is available or I/O 2121 * operation fails, global boolean variable inputExhausted will be set to 2122 * true, otherwise set to false. 2123 */ 2124 private void readMore() { 2125 int oldPosition = buffer.position(); 2126 int oldBufferLength = bufferLength; 2127 // Increase capacity if empty space is not enough 2128 if (bufferLength >= buffer.capacity()) { 2129 expandBuffer(); 2130 } 2131 2132 // Read input resource 2133 int readCount = 0; 2134 try { 2135 buffer.limit(buffer.capacity()); 2136 buffer.position(oldBufferLength); 2137 while ((readCount = input.read(buffer)) == 0) { 2138 // nothing to do here 2139 } 2140 } catch (IOException e) { 2141 // Consider the scenario: readable puts 4 chars into 2142 // buffer and then an IOException is thrown out. In this case, 2143 // buffer is 2144 // actually grown, but readable.read() will never return. 2145 bufferLength = buffer.position(); 2146 /* 2147 * Uses -1 to record IOException occurring, and no more input can be 2148 * read. 2149 */ 2150 readCount = -1; 2151 lastIOException = e; 2152 } 2153 2154 buffer.flip(); 2155 buffer.position(oldPosition); 2156 if (-1 == readCount) { 2157 inputExhausted = true; 2158 } else { 2159 bufferLength = readCount + bufferLength; 2160 } 2161 } 2162 2163 // Expand the size of internal buffer. 2164 private void expandBuffer() { 2165 int oldPosition = buffer.position(); 2166 int oldCapacity = buffer.capacity(); 2167 int oldLimit = buffer.limit(); 2168 int newCapacity = oldCapacity * DIPLOID; 2169 char[] newBuffer = new char[newCapacity]; 2170 System.arraycopy(buffer.array(), 0, newBuffer, 0, oldLimit); 2171 buffer = CharBuffer.wrap(newBuffer, 0, newCapacity); 2172 buffer.position(oldPosition); 2173 buffer.limit(oldLimit); 2174 } 2175 2176 /** 2177 * Resets this scanner's delimiter, locale, and radix. 2178 * 2179 * @return this scanner 2180 * @since 1.6 2181 */ 2182 public Scanner reset() { 2183 delimiter = DEFAULT_DELIMITER; 2184 locale = Locale.getDefault(); 2185 integerRadix = 10; 2186 return this; 2187 } 2188 } 2189