mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8072722: add stream support to Scanner
Reviewed-by: psandoz, chegar, sherman
This commit is contained in:
parent
a2f0fe3c94
commit
343e882e7f
3 changed files with 676 additions and 224 deletions
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -25,16 +25,18 @@
|
||||||
|
|
||||||
package java.util;
|
package java.util;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.util.regex.*;
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.math.*;
|
import java.math.*;
|
||||||
import java.nio.*;
|
import java.nio.*;
|
||||||
import java.nio.channels.*;
|
import java.nio.channels.*;
|
||||||
import java.nio.charset.*;
|
import java.nio.charset.*;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.text.*;
|
import java.text.*;
|
||||||
import java.util.Locale;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.regex.*;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
import sun.misc.LRUCache;
|
import sun.misc.LRUCache;
|
||||||
|
|
||||||
|
@ -96,22 +98,25 @@ import sun.misc.LRUCache;
|
||||||
* }</pre></blockquote>
|
* }</pre></blockquote>
|
||||||
*
|
*
|
||||||
* <p>The <a name="default-delimiter">default whitespace delimiter</a> used
|
* <p>The <a name="default-delimiter">default whitespace delimiter</a> used
|
||||||
* by a scanner is as recognized by {@link java.lang.Character}.{@link
|
* by a scanner is as recognized by {@link Character#isWhitespace(char)
|
||||||
* java.lang.Character#isWhitespace(char) isWhitespace}. The {@link #reset}
|
* Character.isWhitespace()}. The {@link #reset reset()}
|
||||||
* method will reset the value of the scanner's delimiter to the default
|
* method will reset the value of the scanner's delimiter to the default
|
||||||
* whitespace delimiter regardless of whether it was previously changed.
|
* whitespace delimiter regardless of whether it was previously changed.
|
||||||
*
|
*
|
||||||
* <p>A scanning operation may block waiting for input.
|
* <p>A scanning operation may block waiting for input.
|
||||||
*
|
*
|
||||||
* <p>The {@link #next} and {@link #hasNext} methods and their
|
* <p>The {@link #next} and {@link #hasNext} methods and their
|
||||||
* primitive-type companion methods (such as {@link #nextInt} and
|
* companion methods (such as {@link #nextInt} and
|
||||||
* {@link #hasNextInt}) first skip any input that matches the delimiter
|
* {@link #hasNextInt}) first skip any input that matches the delimiter
|
||||||
* pattern, and then attempt to return the next token. Both {@code hasNext}
|
* pattern, and then attempt to return the next token. Both {@code hasNext()}
|
||||||
* and {@code next} methods may block waiting for further input. Whether a
|
* and {@code next()} methods may block waiting for further input. Whether a
|
||||||
* {@code hasNext} method blocks has no connection to whether or not its
|
* {@code hasNext()} method blocks has no connection to whether or not its
|
||||||
* associated {@code next} method will block.
|
* associated {@code next()} method will block. The {@link #tokens} method
|
||||||
|
* may also block waiting for input.
|
||||||
*
|
*
|
||||||
* <p> The {@link #findInLine}, {@link #findWithinHorizon}, and {@link #skip}
|
* <p>The {@link #findInLine findInLine()},
|
||||||
|
* {@link #findWithinHorizon findWithinHorizon()},
|
||||||
|
* {@link #skip skip()}, and {@link #findAll findAll()}
|
||||||
* methods operate independently of the delimiter pattern. These methods will
|
* methods operate independently of the delimiter pattern. These methods will
|
||||||
* attempt to match the specified pattern with no regard to delimiters in the
|
* attempt to match the specified pattern with no regard to delimiters in the
|
||||||
* input and thus can be used in special circumstances where delimiters are
|
* input and thus can be used in special circumstances where delimiters are
|
||||||
|
@ -129,7 +134,7 @@ import sun.misc.LRUCache;
|
||||||
*
|
*
|
||||||
* <p> A scanner can read text from any object which implements the {@link
|
* <p> A scanner can read text from any object which implements the {@link
|
||||||
* java.lang.Readable} interface. If an invocation of the underlying
|
* java.lang.Readable} interface. If an invocation of the underlying
|
||||||
* readable's {@link java.lang.Readable#read} method throws an {@link
|
* readable's {@link java.lang.Readable#read read()} method throws an {@link
|
||||||
* java.io.IOException} then the scanner assumes that the end of the input
|
* java.io.IOException} then the scanner assumes that the end of the input
|
||||||
* has been reached. The most recent {@code IOException} thrown by the
|
* has been reached. The most recent {@code IOException} thrown by the
|
||||||
* underlying readable can be retrieved via the {@link #ioException} method.
|
* underlying readable can be retrieved via the {@link #ioException} method.
|
||||||
|
@ -156,7 +161,7 @@ import sun.misc.LRUCache;
|
||||||
* <a name="initial-locale">initial locale </a>is the value returned by the {@link
|
* <a name="initial-locale">initial locale </a>is the value returned by the {@link
|
||||||
* java.util.Locale#getDefault(Locale.Category)
|
* java.util.Locale#getDefault(Locale.Category)
|
||||||
* Locale.getDefault(Locale.Category.FORMAT)} method; it may be changed via the {@link
|
* Locale.getDefault(Locale.Category.FORMAT)} method; it may be changed via the {@link
|
||||||
* #useLocale} method. The {@link #reset} method will reset the value of the
|
* #useLocale useLocale()} method. The {@link #reset} method will reset the value of the
|
||||||
* scanner's locale to the initial locale regardless of whether it was
|
* scanner's locale to the initial locale regardless of whether it was
|
||||||
* previously changed.
|
* previously changed.
|
||||||
*
|
*
|
||||||
|
@ -374,6 +379,11 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
// A holder of the last IOException encountered
|
// A holder of the last IOException encountered
|
||||||
private IOException lastException;
|
private IOException lastException;
|
||||||
|
|
||||||
|
// Number of times this scanner's state has been modified.
|
||||||
|
// Generally incremented on most public APIs and checked
|
||||||
|
// within spliterator implementations.
|
||||||
|
int modCount;
|
||||||
|
|
||||||
// A pattern for java whitespace
|
// A pattern for java whitespace
|
||||||
private static Pattern WHITESPACE_PATTERN = Pattern.compile(
|
private static Pattern WHITESPACE_PATTERN = Pattern.compile(
|
||||||
"\\p{javaWhitespace}+");
|
"\\p{javaWhitespace}+");
|
||||||
|
@ -995,8 +1005,9 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finds the specified pattern in the buffer up to horizon.
|
// Finds the specified pattern in the buffer up to horizon.
|
||||||
// Returns a match for the specified input pattern.
|
// Returns true if the specified input pattern was matched,
|
||||||
private String findPatternInBuffer(Pattern pattern, int horizon) {
|
// and leaves the matcher field with the current match state.
|
||||||
|
private boolean findPatternInBuffer(Pattern pattern, int horizon) {
|
||||||
matchValid = false;
|
matchValid = false;
|
||||||
matcher.usePattern(pattern);
|
matcher.usePattern(pattern);
|
||||||
int bufferLimit = buf.limit();
|
int bufferLimit = buf.limit();
|
||||||
|
@ -1014,7 +1025,7 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
if (searchLimit != horizonLimit) {
|
if (searchLimit != horizonLimit) {
|
||||||
// Hit an artificial end; try to extend the match
|
// Hit an artificial end; try to extend the match
|
||||||
needInput = true;
|
needInput = true;
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
// The match could go away depending on what is next
|
// The match could go away depending on what is next
|
||||||
if ((searchLimit == horizonLimit) && matcher.requireEnd()) {
|
if ((searchLimit == horizonLimit) && matcher.requireEnd()) {
|
||||||
|
@ -1022,27 +1033,28 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
// that it is at the horizon and the end of input is
|
// that it is at the horizon and the end of input is
|
||||||
// required for the match.
|
// required for the match.
|
||||||
needInput = true;
|
needInput = true;
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Did not hit end, or hit real end, or hit horizon
|
// Did not hit end, or hit real end, or hit horizon
|
||||||
position = matcher.end();
|
position = matcher.end();
|
||||||
return matcher.group();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sourceClosed)
|
if (sourceClosed)
|
||||||
return null;
|
return false;
|
||||||
|
|
||||||
// If there is no specified horizon, or if we have not searched
|
// If there is no specified horizon, or if we have not searched
|
||||||
// to the specified horizon yet, get more input
|
// to the specified horizon yet, get more input
|
||||||
if ((horizon == 0) || (searchLimit != horizonLimit))
|
if ((horizon == 0) || (searchLimit != horizonLimit))
|
||||||
needInput = true;
|
needInput = true;
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a match for the specified input pattern anchored at
|
// Attempts to match a pattern anchored at the current position.
|
||||||
// the current position
|
// Returns true if the specified input pattern was matched,
|
||||||
private String matchPatternInBuffer(Pattern pattern) {
|
// and leaves the matcher field with the current match state.
|
||||||
|
private boolean matchPatternInBuffer(Pattern pattern) {
|
||||||
matchValid = false;
|
matchValid = false;
|
||||||
matcher.usePattern(pattern);
|
matcher.usePattern(pattern);
|
||||||
matcher.region(position, buf.limit());
|
matcher.region(position, buf.limit());
|
||||||
|
@ -1050,18 +1062,18 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
if (matcher.hitEnd() && (!sourceClosed)) {
|
if (matcher.hitEnd() && (!sourceClosed)) {
|
||||||
// Get more input and try again
|
// Get more input and try again
|
||||||
needInput = true;
|
needInput = true;
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
position = matcher.end();
|
position = matcher.end();
|
||||||
return matcher.group();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sourceClosed)
|
if (sourceClosed)
|
||||||
return null;
|
return false;
|
||||||
|
|
||||||
// Read more to find pattern
|
// Read more to find pattern
|
||||||
needInput = true;
|
needInput = true;
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Throws if the scanner is closed
|
// Throws if the scanner is closed
|
||||||
|
@ -1128,6 +1140,7 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
* @return this scanner
|
* @return this scanner
|
||||||
*/
|
*/
|
||||||
public Scanner useDelimiter(Pattern pattern) {
|
public Scanner useDelimiter(Pattern pattern) {
|
||||||
|
modCount++;
|
||||||
delimPattern = pattern;
|
delimPattern = pattern;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -1147,6 +1160,7 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
* @return this scanner
|
* @return this scanner
|
||||||
*/
|
*/
|
||||||
public Scanner useDelimiter(String pattern) {
|
public Scanner useDelimiter(String pattern) {
|
||||||
|
modCount++;
|
||||||
delimPattern = patternCache.forName(pattern);
|
delimPattern = patternCache.forName(pattern);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -1181,6 +1195,7 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
if (locale.equals(this.locale))
|
if (locale.equals(this.locale))
|
||||||
return this;
|
return this;
|
||||||
|
|
||||||
|
modCount++;
|
||||||
this.locale = locale;
|
this.locale = locale;
|
||||||
DecimalFormat df =
|
DecimalFormat df =
|
||||||
(DecimalFormat)NumberFormat.getNumberInstance(locale);
|
(DecimalFormat)NumberFormat.getNumberInstance(locale);
|
||||||
|
@ -1236,8 +1251,8 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
* number matching regular expressions; see
|
* number matching regular expressions; see
|
||||||
* <a href= "#localized-numbers">localized numbers</a> above.
|
* <a href= "#localized-numbers">localized numbers</a> above.
|
||||||
*
|
*
|
||||||
* <p>If the radix is less than {@code Character.MIN_RADIX}
|
* <p>If the radix is less than {@link Character#MIN_RADIX Character.MIN_RADIX}
|
||||||
* or greater than {@code Character.MAX_RADIX}, then an
|
* or greater than {@link Character#MAX_RADIX Character.MAX_RADIX}, then an
|
||||||
* {@code IllegalArgumentException} is thrown.
|
* {@code IllegalArgumentException} is thrown.
|
||||||
*
|
*
|
||||||
* <p>Invoking the {@link #reset} method will set the scanner's radix to
|
* <p>Invoking the {@link #reset} method will set the scanner's radix to
|
||||||
|
@ -1253,6 +1268,7 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
|
|
||||||
if (this.defaultRadix == radix)
|
if (this.defaultRadix == radix)
|
||||||
return this;
|
return this;
|
||||||
|
modCount++;
|
||||||
this.defaultRadix = radix;
|
this.defaultRadix = radix;
|
||||||
// Force rebuilding and recompilation of radix dependent patterns
|
// Force rebuilding and recompilation of radix dependent patterns
|
||||||
integerPattern = null;
|
integerPattern = null;
|
||||||
|
@ -1275,15 +1291,15 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
* if no match has been performed, or if the last match was
|
* if no match has been performed, or if the last match was
|
||||||
* not successful.
|
* not successful.
|
||||||
*
|
*
|
||||||
* <p>The various {@code next}methods of {@code Scanner}
|
* <p>The various {@code next} methods of {@code Scanner}
|
||||||
* make a match result available if they complete without throwing an
|
* make a match result available if they complete without throwing an
|
||||||
* exception. For instance, after an invocation of the {@link #nextInt}
|
* exception. For instance, after an invocation of the {@link #nextInt}
|
||||||
* method that returned an int, this method returns a
|
* method that returned an int, this method returns a
|
||||||
* {@code MatchResult} for the search of the
|
* {@code MatchResult} for the search of the
|
||||||
* <a href="#Integer-regex"><i>Integer</i></a> regular expression
|
* <a href="#Integer-regex"><i>Integer</i></a> regular expression
|
||||||
* defined above. Similarly the {@link #findInLine},
|
* defined above. Similarly the {@link #findInLine findInLine()},
|
||||||
* {@link #findWithinHorizon}, and {@link #skip} methods will make a
|
* {@link #findWithinHorizon findWithinHorizon()}, and {@link #skip skip()}
|
||||||
* match available if they succeed.
|
* methods will make a match available if they succeed.
|
||||||
*
|
*
|
||||||
* @return a match result for the last match operation
|
* @return a match result for the last match operation
|
||||||
* @throws IllegalStateException If no match result is available
|
* @throws IllegalStateException If no match result is available
|
||||||
|
@ -1333,6 +1349,7 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
ensureOpen();
|
ensureOpen();
|
||||||
saveState();
|
saveState();
|
||||||
|
modCount++;
|
||||||
while (!sourceClosed) {
|
while (!sourceClosed) {
|
||||||
if (hasTokenInBuffer())
|
if (hasTokenInBuffer())
|
||||||
return revertState(true);
|
return revertState(true);
|
||||||
|
@ -1357,6 +1374,7 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
public String next() {
|
public String next() {
|
||||||
ensureOpen();
|
ensureOpen();
|
||||||
clearCaches();
|
clearCaches();
|
||||||
|
modCount++;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
String token = getCompleteTokenInBuffer(null);
|
String token = getCompleteTokenInBuffer(null);
|
||||||
|
@ -1435,6 +1453,7 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
throw new NullPointerException();
|
throw new NullPointerException();
|
||||||
hasNextPattern = null;
|
hasNextPattern = null;
|
||||||
saveState();
|
saveState();
|
||||||
|
modCount++;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (getCompleteTokenInBuffer(pattern) != null) {
|
if (getCompleteTokenInBuffer(pattern) != null) {
|
||||||
|
@ -1466,6 +1485,7 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
if (pattern == null)
|
if (pattern == null)
|
||||||
throw new NullPointerException();
|
throw new NullPointerException();
|
||||||
|
|
||||||
|
modCount++;
|
||||||
// Did we already find this pattern?
|
// Did we already find this pattern?
|
||||||
if (hasNextPattern == pattern)
|
if (hasNextPattern == pattern)
|
||||||
return getCachedResult();
|
return getCachedResult();
|
||||||
|
@ -1497,6 +1517,7 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
public boolean hasNextLine() {
|
public boolean hasNextLine() {
|
||||||
saveState();
|
saveState();
|
||||||
|
|
||||||
|
modCount++;
|
||||||
String result = findWithinHorizon(linePattern(), 0);
|
String result = findWithinHorizon(linePattern(), 0);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
MatchResult mr = this.match();
|
MatchResult mr = this.match();
|
||||||
|
@ -1531,6 +1552,7 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
* @throws IllegalStateException if this scanner is closed
|
* @throws IllegalStateException if this scanner is closed
|
||||||
*/
|
*/
|
||||||
public String nextLine() {
|
public String nextLine() {
|
||||||
|
modCount++;
|
||||||
if (hasNextPattern == linePattern())
|
if (hasNextPattern == linePattern())
|
||||||
return getCachedResult();
|
return getCachedResult();
|
||||||
clearCaches();
|
clearCaches();
|
||||||
|
@ -1589,12 +1611,12 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
if (pattern == null)
|
if (pattern == null)
|
||||||
throw new NullPointerException();
|
throw new NullPointerException();
|
||||||
clearCaches();
|
clearCaches();
|
||||||
|
modCount++;
|
||||||
// Expand buffer to include the next newline or end of input
|
// Expand buffer to include the next newline or end of input
|
||||||
int endPosition = 0;
|
int endPosition = 0;
|
||||||
saveState();
|
saveState();
|
||||||
while (true) {
|
while (true) {
|
||||||
String token = findPatternInBuffer(separatorPattern(), 0);
|
if (findPatternInBuffer(separatorPattern(), 0)) {
|
||||||
if (token != null) {
|
|
||||||
endPosition = matcher.start();
|
endPosition = matcher.start();
|
||||||
break; // up to next newline
|
break; // up to next newline
|
||||||
}
|
}
|
||||||
|
@ -1623,7 +1645,7 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
* <p>An invocation of this method of the form
|
* <p>An invocation of this method of the form
|
||||||
* {@code findWithinHorizon(pattern)} behaves in exactly the same way as
|
* {@code findWithinHorizon(pattern)} behaves in exactly the same way as
|
||||||
* the invocation
|
* the invocation
|
||||||
* {@code findWithinHorizon(Pattern.compile(pattern, horizon))}.
|
* {@code findWithinHorizon(Pattern.compile(pattern), horizon)}.
|
||||||
*
|
*
|
||||||
* @param pattern a string specifying the pattern to search for
|
* @param pattern a string specifying the pattern to search for
|
||||||
* @param horizon the search horizon
|
* @param horizon the search horizon
|
||||||
|
@ -1673,13 +1695,13 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
if (horizon < 0)
|
if (horizon < 0)
|
||||||
throw new IllegalArgumentException("horizon < 0");
|
throw new IllegalArgumentException("horizon < 0");
|
||||||
clearCaches();
|
clearCaches();
|
||||||
|
modCount++;
|
||||||
|
|
||||||
// Search for the pattern
|
// Search for the pattern
|
||||||
while (true) {
|
while (true) {
|
||||||
String token = findPatternInBuffer(pattern, horizon);
|
if (findPatternInBuffer(pattern, horizon)) {
|
||||||
if (token != null) {
|
|
||||||
matchValid = true;
|
matchValid = true;
|
||||||
return token;
|
return matcher.group();
|
||||||
}
|
}
|
||||||
if (needInput)
|
if (needInput)
|
||||||
readInput();
|
readInput();
|
||||||
|
@ -1717,11 +1739,11 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
if (pattern == null)
|
if (pattern == null)
|
||||||
throw new NullPointerException();
|
throw new NullPointerException();
|
||||||
clearCaches();
|
clearCaches();
|
||||||
|
modCount++;
|
||||||
|
|
||||||
// Search for the pattern
|
// Search for the pattern
|
||||||
while (true) {
|
while (true) {
|
||||||
String token = matchPatternInBuffer(pattern);
|
if (matchPatternInBuffer(pattern)) {
|
||||||
if (token != null) {
|
|
||||||
matchValid = true;
|
matchValid = true;
|
||||||
position = matcher.end();
|
position = matcher.end();
|
||||||
return this;
|
return this;
|
||||||
|
@ -1932,7 +1954,7 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
*
|
*
|
||||||
* <p> An invocation of this method of the form
|
* <p> An invocation of this method of the form
|
||||||
* {@code nextShort()} behaves in exactly the same way as the
|
* {@code nextShort()} behaves in exactly the same way as the
|
||||||
* invocation {@code nextShort(radix)}, where {@code radix}
|
* invocation {@link #nextShort(int) nextShort(radix)}, where {@code radix}
|
||||||
* is the default radix of this scanner.
|
* is the default radix of this scanner.
|
||||||
*
|
*
|
||||||
* @return the {@code short} scanned from the input
|
* @return the {@code short} scanned from the input
|
||||||
|
@ -2590,8 +2612,10 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
* Resets this scanner.
|
* Resets this scanner.
|
||||||
*
|
*
|
||||||
* <p> Resetting a scanner discards all of its explicit state
|
* <p> Resetting a scanner discards all of its explicit state
|
||||||
* information which may have been changed by invocations of {@link
|
* information which may have been changed by invocations of
|
||||||
* #useDelimiter}, {@link #useLocale}, or {@link #useRadix}.
|
* {@link #useDelimiter useDelimiter()},
|
||||||
|
* {@link #useLocale useLocale()}, or
|
||||||
|
* {@link #useRadix useRadix()}.
|
||||||
*
|
*
|
||||||
* <p> An invocation of this method of the form
|
* <p> An invocation of this method of the form
|
||||||
* {@code scanner.reset()} behaves in exactly the same way as the
|
* {@code scanner.reset()} behaves in exactly the same way as the
|
||||||
|
@ -2612,6 +2636,206 @@ public final class Scanner implements Iterator<String>, Closeable {
|
||||||
useLocale(Locale.getDefault(Locale.Category.FORMAT));
|
useLocale(Locale.getDefault(Locale.Category.FORMAT));
|
||||||
useRadix(10);
|
useRadix(10);
|
||||||
clearCaches();
|
clearCaches();
|
||||||
|
modCount++;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a stream of delimiter-separated tokens from this scanner. The
|
||||||
|
* stream contains the same tokens that would be returned, starting from
|
||||||
|
* this scanner's current state, by calling the {@link #next} method
|
||||||
|
* repeatedly until the {@link #hasNext} method returns false.
|
||||||
|
*
|
||||||
|
* <p>The resulting stream is sequential and ordered. All stream elements are
|
||||||
|
* non-null.
|
||||||
|
*
|
||||||
|
* <p>Scanning starts upon initiation of the terminal stream operation, using the
|
||||||
|
* current state of this scanner. Subsequent calls to any methods on this scanner
|
||||||
|
* other than {@link #close} and {@link #ioException} may return undefined results
|
||||||
|
* or may cause undefined effects on the returned stream. The returned stream's source
|
||||||
|
* {@code Spliterator} is <em>fail-fast</em> and will, on a best-effort basis, throw a
|
||||||
|
* {@link java.util.ConcurrentModificationException} if any such calls are detected
|
||||||
|
* during stream pipeline execution.
|
||||||
|
*
|
||||||
|
* <p>After stream pipeline execution completes, this scanner is left in an indeterminate
|
||||||
|
* state and cannot be reused.
|
||||||
|
*
|
||||||
|
* <p>If this scanner contains a resource that must be released, this scanner
|
||||||
|
* should be closed, either by calling its {@link #close} method, or by
|
||||||
|
* closing the returned stream. Closing the stream will close the underlying scanner.
|
||||||
|
* {@code IllegalStateException} is thrown if the scanner has been closed when this
|
||||||
|
* method is called, or if this scanner is closed during stream pipeline execution.
|
||||||
|
*
|
||||||
|
* <p>This method might block waiting for more input.
|
||||||
|
*
|
||||||
|
* @apiNote
|
||||||
|
* For example, the following code will create a list of
|
||||||
|
* comma-delimited tokens from a string:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* List<String> result = new Scanner("abc,def,,ghi")
|
||||||
|
* .useDelimiter(",")
|
||||||
|
* .tokens()
|
||||||
|
* .collect(Collectors.toList());
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>The resulting list would contain {@code "abc"}, {@code "def"},
|
||||||
|
* the empty string, and {@code "ghi"}.
|
||||||
|
*
|
||||||
|
* @return a sequential stream of token strings
|
||||||
|
* @throws IllegalStateException if this scanner is closed
|
||||||
|
* @since 1.9
|
||||||
|
*/
|
||||||
|
public Stream<String> tokens() {
|
||||||
|
ensureOpen();
|
||||||
|
Stream<String> stream = StreamSupport.stream(new TokenSpliterator(), false);
|
||||||
|
return stream.onClose(this::close);
|
||||||
|
}
|
||||||
|
|
||||||
|
class TokenSpliterator extends Spliterators.AbstractSpliterator<String> {
|
||||||
|
int expectedCount = -1;
|
||||||
|
|
||||||
|
TokenSpliterator() {
|
||||||
|
super(Long.MAX_VALUE,
|
||||||
|
Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.ORDERED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean tryAdvance(Consumer<? super String> cons) {
|
||||||
|
if (expectedCount >= 0 && expectedCount != modCount) {
|
||||||
|
throw new ConcurrentModificationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasNext()) {
|
||||||
|
String token = next();
|
||||||
|
expectedCount = modCount;
|
||||||
|
cons.accept(token);
|
||||||
|
if (expectedCount != modCount) {
|
||||||
|
throw new ConcurrentModificationException();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
expectedCount = modCount;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a stream of match results from this scanner. The stream
|
||||||
|
* contains the same results in the same order that would be returned by
|
||||||
|
* calling {@code findWithinHorizon(pattern, 0)} and then {@link #match}
|
||||||
|
* successively as long as {@link #findWithinHorizon findWithinHorizon()}
|
||||||
|
* finds matches.
|
||||||
|
*
|
||||||
|
* <p>The resulting stream is sequential and ordered. All stream elements are
|
||||||
|
* non-null.
|
||||||
|
*
|
||||||
|
* <p>Scanning starts upon initiation of the terminal stream operation, using the
|
||||||
|
* current state of this scanner. Subsequent calls to any methods on this scanner
|
||||||
|
* other than {@link #close} and {@link #ioException} may return undefined results
|
||||||
|
* or may cause undefined effects on the returned stream. The returned stream's source
|
||||||
|
* {@code Spliterator} is <em>fail-fast</em> and will, on a best-effort basis, throw a
|
||||||
|
* {@link java.util.ConcurrentModificationException} if any such calls are detected
|
||||||
|
* during stream pipeline execution.
|
||||||
|
*
|
||||||
|
* <p>After stream pipeline execution completes, this scanner is left in an indeterminate
|
||||||
|
* state and cannot be reused.
|
||||||
|
*
|
||||||
|
* <p>If this scanner contains a resource that must be released, this scanner
|
||||||
|
* should be closed, either by calling its {@link #close} method, or by
|
||||||
|
* closing the returned stream. Closing the stream will close the underlying scanner.
|
||||||
|
* {@code IllegalStateException} is thrown if the scanner has been closed when this
|
||||||
|
* method is called, or if this scanner is closed during stream pipeline execution.
|
||||||
|
*
|
||||||
|
* <p>As with the {@link #findWithinHorizon findWithinHorizon()} methods, this method
|
||||||
|
* might block waiting for additional input, and it might buffer an unbounded amount of
|
||||||
|
* input searching for a match.
|
||||||
|
*
|
||||||
|
* @apiNote
|
||||||
|
* For example, the following code will read a file and return a list
|
||||||
|
* of all sequences of characters consisting of seven or more Latin capital
|
||||||
|
* letters:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* try (Scanner sc = new Scanner(Paths.get("input.txt"))) {
|
||||||
|
* Pattern pat = Pattern.compile("[A-Z]{7,}");
|
||||||
|
* List<String> capWords = sc.findAll(pat)
|
||||||
|
* .map(MatchResult::group)
|
||||||
|
* .collect(Collectors.toList());
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @param pattern the pattern to be matched
|
||||||
|
* @return a sequential stream of match results
|
||||||
|
* @throws NullPointerException if pattern is null
|
||||||
|
* @throws IllegalStateException if this scanner is closed
|
||||||
|
* @since 1.9
|
||||||
|
*/
|
||||||
|
public Stream<MatchResult> findAll(Pattern pattern) {
|
||||||
|
Objects.requireNonNull(pattern);
|
||||||
|
ensureOpen();
|
||||||
|
Stream<MatchResult> stream = StreamSupport.stream(new FindSpliterator(pattern), false);
|
||||||
|
return stream.onClose(this::close);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a stream of match results that match the provided pattern string.
|
||||||
|
* The effect is equivalent to the following code:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* scanner.findAll(Pattern.compile(patString))
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @param patString the pattern string
|
||||||
|
* @return a sequential stream of match results
|
||||||
|
* @throws NullPointerException if patString is null
|
||||||
|
* @throws IllegalStateException if this scanner is closed
|
||||||
|
* @throws PatternSyntaxException if the regular expression's syntax is invalid
|
||||||
|
* @since 1.9
|
||||||
|
* @see java.util.regex.Pattern
|
||||||
|
*/
|
||||||
|
public Stream<MatchResult> findAll(String patString) {
|
||||||
|
Objects.requireNonNull(patString);
|
||||||
|
ensureOpen();
|
||||||
|
return findAll(patternCache.forName(patString));
|
||||||
|
}
|
||||||
|
|
||||||
|
class FindSpliterator extends Spliterators.AbstractSpliterator<MatchResult> {
|
||||||
|
final Pattern pattern;
|
||||||
|
int expectedCount = -1;
|
||||||
|
|
||||||
|
FindSpliterator(Pattern pattern) {
|
||||||
|
super(Long.MAX_VALUE,
|
||||||
|
Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.ORDERED);
|
||||||
|
this.pattern = pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean tryAdvance(Consumer<? super MatchResult> cons) {
|
||||||
|
ensureOpen();
|
||||||
|
if (expectedCount >= 0) {
|
||||||
|
if (expectedCount != modCount) {
|
||||||
|
throw new ConcurrentModificationException();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
expectedCount = modCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// assert expectedCount == modCount
|
||||||
|
if (findPatternInBuffer(pattern, 0)) { // doesn't increment modCount
|
||||||
|
cons.accept(matcher.toMatchResult());
|
||||||
|
if (expectedCount != modCount) {
|
||||||
|
throw new ConcurrentModificationException();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (needInput)
|
||||||
|
readInput(); // doesn't increment modCount
|
||||||
|
else
|
||||||
|
return false; // reached end of input
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,25 +24,30 @@
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
* @bug 4313885 4926319 4927634 5032610 5032622 5049968 5059533 6223711 6277261 6269946 6288823
|
* @bug 4313885 4926319 4927634 5032610 5032622 5049968 5059533 6223711 6277261 6269946 6288823
|
||||||
|
* 8072722
|
||||||
* @summary Basic tests of java.util.Scanner methods
|
* @summary Basic tests of java.util.Scanner methods
|
||||||
* @key randomness
|
* @key randomness
|
||||||
* @run main/othervm ScanTest
|
* @run main/othervm ScanTest
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.text.*;
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.*;
|
|
||||||
import java.util.regex.*;
|
|
||||||
import java.math.*;
|
import java.math.*;
|
||||||
|
import java.nio.*;
|
||||||
|
import java.text.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.regex.*;
|
||||||
|
import java.util.stream.*;
|
||||||
|
|
||||||
public class ScanTest {
|
public class ScanTest {
|
||||||
|
|
||||||
private static boolean failure = false;
|
private static boolean failure = false;
|
||||||
private static int failCount = 0;
|
private static int failCount = 0;
|
||||||
private static int NUM_SOURCE_TYPES = 2;
|
private static int NUM_SOURCE_TYPES = 2;
|
||||||
|
private static File inputFile = new File(System.getProperty("test.src", "."), "input.txt");
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
|
||||||
Locale reservedLocale = Locale.getDefault();
|
Locale reservedLocale = Locale.getDefault();
|
||||||
String lang = reservedLocale.getLanguage();
|
String lang = reservedLocale.getLanguage();
|
||||||
try {
|
try {
|
||||||
|
@ -70,8 +75,10 @@ public class ScanTest {
|
||||||
cacheTest2();
|
cacheTest2();
|
||||||
nonASCIITest();
|
nonASCIITest();
|
||||||
resetTest();
|
resetTest();
|
||||||
|
streamCloseTest();
|
||||||
|
streamComodTest();
|
||||||
|
|
||||||
for (int j=0; j<NUM_SOURCE_TYPES; j++) {
|
for (int j = 0; j < NUM_SOURCE_TYPES; j++) {
|
||||||
hasNextTest(j);
|
hasNextTest(j);
|
||||||
nextTest(j);
|
nextTest(j);
|
||||||
hasNextPatternTest(j);
|
hasNextPatternTest(j);
|
||||||
|
@ -115,149 +122,147 @@ public class ScanTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void useCase1() throws Exception {
|
public static void useCase1() throws Exception {
|
||||||
File f = new File(System.getProperty("test.src", "."), "input.txt");
|
try (Scanner sc = new Scanner(inputFile)) {
|
||||||
Scanner sc = new Scanner(f);
|
sc.findWithinHorizon("usage case 1", 0);
|
||||||
sc.findWithinHorizon("usage case 1", 0);
|
String[] names = new String[4];
|
||||||
String[] names = new String[4];
|
for (int i=0; i<4; i++) {
|
||||||
for (int i=0; i<4; i++) {
|
while (sc.hasNextFloat())
|
||||||
while(sc.hasNextFloat())
|
sc.nextFloat();
|
||||||
sc.nextFloat();
|
names[i] = sc.next();
|
||||||
names[i] = sc.next();
|
sc.nextLine();
|
||||||
sc.nextLine();
|
}
|
||||||
|
if (!names[0].equals("Frank"))
|
||||||
|
failCount++;
|
||||||
|
if (!names[1].equals("Joe"))
|
||||||
|
failCount++;
|
||||||
|
if (!names[2].equals("Mary"))
|
||||||
|
failCount++;
|
||||||
|
if (!names[3].equals("Michelle"))
|
||||||
|
failCount++;
|
||||||
}
|
}
|
||||||
if (!names[0].equals("Frank"))
|
|
||||||
failCount++;
|
|
||||||
if (!names[1].equals("Joe"))
|
|
||||||
failCount++;
|
|
||||||
if (!names[2].equals("Mary"))
|
|
||||||
failCount++;
|
|
||||||
if (!names[3].equals("Michelle"))
|
|
||||||
failCount++;
|
|
||||||
sc.close();
|
|
||||||
report("Use case 1");
|
report("Use case 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void useCase2() throws Exception {
|
public static void useCase2() throws Exception {
|
||||||
File f = new File(System.getProperty("test.src", "."), "input.txt");
|
try (Scanner sc = new Scanner(inputFile).useDelimiter("-")) {
|
||||||
Scanner sc = new Scanner(f).useDelimiter("-");
|
String testDataTag = sc.findWithinHorizon("usage case 2\n", 0);
|
||||||
String testDataTag = sc.findWithinHorizon("usage case 2\n", 0);
|
if (!testDataTag.equals("usage case 2\n"))
|
||||||
if (!testDataTag.equals("usage case 2\n"))
|
failCount++;
|
||||||
failCount++;
|
if (!sc.next().equals("cat"))
|
||||||
if (!sc.next().equals("cat"))
|
failCount++;
|
||||||
failCount++;
|
if (sc.nextInt() != 9)
|
||||||
if (sc.nextInt() != 9)
|
failCount++;
|
||||||
failCount++;
|
if (!sc.next().equals("dog"))
|
||||||
if (!sc.next().equals("dog"))
|
failCount++;
|
||||||
failCount++;
|
if (sc.nextInt() != 6)
|
||||||
if (sc.nextInt() != 6)
|
failCount++;
|
||||||
failCount++;
|
if (!sc.next().equals("pig"))
|
||||||
if (!sc.next().equals("pig"))
|
failCount++;
|
||||||
failCount++;
|
if (sc.nextInt() != 2)
|
||||||
if (sc.nextInt() != 2)
|
failCount++;
|
||||||
failCount++;
|
if (!sc.next().equals(""))
|
||||||
if (!sc.next().equals(""))
|
failCount++;
|
||||||
failCount++;
|
if (sc.nextInt() != 5)
|
||||||
if (sc.nextInt() != 5)
|
failCount++;
|
||||||
failCount++;
|
}
|
||||||
sc.close();
|
|
||||||
report("Use case 2");
|
report("Use case 2");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void useCase3() throws Exception {
|
public static void useCase3() throws Exception {
|
||||||
File f = new File(System.getProperty("test.src", "."), "input.txt");
|
try (Scanner sc = new Scanner(inputFile)) {
|
||||||
Scanner sc = new Scanner(f);
|
String testDataTag = sc.findWithinHorizon("usage case 3\n", 0);
|
||||||
String testDataTag = sc.findWithinHorizon("usage case 3\n", 0);
|
if (!testDataTag.equals("usage case 3\n"))
|
||||||
if (!testDataTag.equals("usage case 3\n"))
|
failCount++;
|
||||||
failCount++;
|
Pattern tagPattern = Pattern.compile("@[a-z]+");
|
||||||
Pattern tagPattern = Pattern.compile("@[a-z]+");
|
Pattern endPattern = Pattern.compile("\\*\\/");
|
||||||
Pattern endPattern = Pattern.compile("\\*\\/");
|
String tag;
|
||||||
String tag;
|
String end = sc.findInLine(endPattern);
|
||||||
String end = sc.findInLine(endPattern);
|
|
||||||
|
|
||||||
while (end == null) {
|
while (end == null) {
|
||||||
if ((tag = sc.findInLine(tagPattern)) != null) {
|
if ((tag = sc.findInLine(tagPattern)) != null) {
|
||||||
String text = sc.nextLine();
|
String text = sc.nextLine();
|
||||||
text = text.substring(0, text.length() - 1);
|
text = text.substring(0, text.length() - 1);
|
||||||
//System.out.println(text);
|
//System.out.println(text);
|
||||||
} else {
|
} else {
|
||||||
sc.nextLine();
|
sc.nextLine();
|
||||||
|
}
|
||||||
|
end = sc.findInLine(endPattern);
|
||||||
}
|
}
|
||||||
end = sc.findInLine(endPattern);
|
|
||||||
}
|
}
|
||||||
report("Use case 3");
|
report("Use case 3");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void useCase4() throws Exception {
|
public static void useCase4() throws Exception {
|
||||||
File f = new File(System.getProperty("test.src", "."), "input.txt");
|
try (Scanner sc = new Scanner(inputFile)) {
|
||||||
Scanner sc = new Scanner(f);
|
String testDataTag = sc.findWithinHorizon("usage case 4\n", 0);
|
||||||
String testDataTag = sc.findWithinHorizon("usage case 4\n", 0);
|
if (!testDataTag.equals("usage case 4\n"))
|
||||||
if (!testDataTag.equals("usage case 4\n"))
|
|
||||||
failCount++;
|
|
||||||
|
|
||||||
// Read some text parts of four hrefs
|
|
||||||
String[] expected = { "Diffs", "Sdiffs", "Old", "New" };
|
|
||||||
for (int i=0; i<4; i++) {
|
|
||||||
sc.findWithinHorizon("<a href", 1000);
|
|
||||||
sc.useDelimiter("[<>\n]+");
|
|
||||||
sc.next();
|
|
||||||
String textOfRef = sc.next();
|
|
||||||
if (!textOfRef.equals(expected[i]))
|
|
||||||
failCount++;
|
failCount++;
|
||||||
}
|
|
||||||
// Read some html tags using < and > as delimiters
|
|
||||||
if (!sc.next().equals("/a"))
|
|
||||||
failCount++;
|
|
||||||
if (!sc.next().equals("b"))
|
|
||||||
failCount++;
|
|
||||||
|
|
||||||
// Scan some html tags using skip and next
|
// Read some text parts of four hrefs
|
||||||
Pattern nonTagStart = Pattern.compile("[^<]+");
|
String[] expected = { "Diffs", "Sdiffs", "Old", "New" };
|
||||||
Pattern tag = Pattern.compile("<[^>]+?>");
|
for (int i=0; i<4; i++) {
|
||||||
Pattern spotAfterTag = Pattern.compile("(?<=>)");
|
sc.findWithinHorizon("<a href", 1000);
|
||||||
String[] expected2 = { "</b>", "<p>", "<ul>", "<li>" };
|
sc.useDelimiter("[<>\n]+");
|
||||||
sc.useDelimiter(spotAfterTag);
|
sc.next();
|
||||||
int tagsFound = 0;
|
String textOfRef = sc.next();
|
||||||
while(tagsFound < 4) {
|
if (!textOfRef.equals(expected[i]))
|
||||||
if (!sc.hasNext(tag)) {
|
failCount++;
|
||||||
// skip text between tags
|
|
||||||
sc.skip(nonTagStart);
|
|
||||||
}
|
}
|
||||||
String tagContents = sc.next(tag);
|
// Read some html tags using < and > as delimiters
|
||||||
if (!tagContents.equals(expected2[tagsFound]))
|
if (!sc.next().equals("/a"))
|
||||||
failCount++;
|
failCount++;
|
||||||
tagsFound++;
|
if (!sc.next().equals("b"))
|
||||||
|
failCount++;
|
||||||
|
|
||||||
|
// Scan some html tags using skip and next
|
||||||
|
Pattern nonTagStart = Pattern.compile("[^<]+");
|
||||||
|
Pattern tag = Pattern.compile("<[^>]+?>");
|
||||||
|
Pattern spotAfterTag = Pattern.compile("(?<=>)");
|
||||||
|
String[] expected2 = { "</b>", "<p>", "<ul>", "<li>" };
|
||||||
|
sc.useDelimiter(spotAfterTag);
|
||||||
|
int tagsFound = 0;
|
||||||
|
while (tagsFound < 4) {
|
||||||
|
if (!sc.hasNext(tag)) {
|
||||||
|
// skip text between tags
|
||||||
|
sc.skip(nonTagStart);
|
||||||
|
}
|
||||||
|
String tagContents = sc.next(tag);
|
||||||
|
if (!tagContents.equals(expected2[tagsFound]))
|
||||||
|
failCount++;
|
||||||
|
tagsFound++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
report("Use case 4");
|
report("Use case 4");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void useCase5() throws Exception {
|
public static void useCase5() throws Exception {
|
||||||
File f = new File(System.getProperty("test.src", "."), "input.txt");
|
try (Scanner sc = new Scanner(inputFile)) {
|
||||||
Scanner sc = new Scanner(f);
|
String testDataTag = sc.findWithinHorizon("usage case 5\n", 0);
|
||||||
String testDataTag = sc.findWithinHorizon("usage case 5\n", 0);
|
if (!testDataTag.equals("usage case 5\n"))
|
||||||
if (!testDataTag.equals("usage case 5\n"))
|
|
||||||
failCount++;
|
|
||||||
|
|
||||||
sc.findWithinHorizon("Share Definitions", 0);
|
|
||||||
sc.nextLine();
|
|
||||||
sc.next("\\[([a-z]+)\\]");
|
|
||||||
String shareName = sc.match().group(1);
|
|
||||||
if (!shareName.equals("homes"))
|
|
||||||
failCount++;
|
|
||||||
|
|
||||||
String[] keys = { "comment", "browseable", "writable", "valid users" };
|
|
||||||
String[] vals = { "Home Directories", "no", "yes", "%S" };
|
|
||||||
for (int i=0; i<4; i++) {
|
|
||||||
sc.useDelimiter("=");
|
|
||||||
String key = sc.next().trim();
|
|
||||||
if (!key.equals(keys[i]))
|
|
||||||
failCount++;
|
|
||||||
sc.skip("[ =]+");
|
|
||||||
sc.useDelimiter("\n");
|
|
||||||
String value = sc.next();
|
|
||||||
if (!value.equals(vals[i]))
|
|
||||||
failCount++;
|
failCount++;
|
||||||
|
|
||||||
|
sc.findWithinHorizon("Share Definitions", 0);
|
||||||
sc.nextLine();
|
sc.nextLine();
|
||||||
|
sc.next("\\[([a-z]+)\\]");
|
||||||
|
String shareName = sc.match().group(1);
|
||||||
|
if (!shareName.equals("homes"))
|
||||||
|
failCount++;
|
||||||
|
|
||||||
|
String[] keys = { "comment", "browseable", "writable", "valid users" };
|
||||||
|
String[] vals = { "Home Directories", "no", "yes", "%S" };
|
||||||
|
for (int i=0; i<4; i++) {
|
||||||
|
sc.useDelimiter("=");
|
||||||
|
String key = sc.next().trim();
|
||||||
|
if (!key.equals(keys[i]))
|
||||||
|
failCount++;
|
||||||
|
sc.skip("[ =]+");
|
||||||
|
sc.useDelimiter("\n");
|
||||||
|
String value = sc.next();
|
||||||
|
if (!value.equals(vals[i]))
|
||||||
|
failCount++;
|
||||||
|
sc.nextLine();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
report("Use case 5");
|
report("Use case 5");
|
||||||
|
@ -445,12 +450,12 @@ public class ScanTest {
|
||||||
if (sc.hasNextLine()) failCount++;
|
if (sc.hasNextLine()) failCount++;
|
||||||
|
|
||||||
// Go through all the lines in a file
|
// Go through all the lines in a file
|
||||||
File f = new File(System.getProperty("test.src", "."), "input.txt");
|
try (Scanner sc2 = new Scanner(inputFile)) {
|
||||||
sc = new Scanner(f);
|
String lastLine = "blah";
|
||||||
String lastLine = "blah";
|
while (sc2.hasNextLine())
|
||||||
while(sc.hasNextLine())
|
lastLine = sc2.nextLine();
|
||||||
lastLine = sc.nextLine();
|
if (!lastLine.equals("# Data for usage case 6")) failCount++;
|
||||||
if (!lastLine.equals("# Data for usage case 6")) failCount++;
|
}
|
||||||
|
|
||||||
report("Has next line test");
|
report("Has next line test");
|
||||||
}
|
}
|
||||||
|
@ -629,48 +634,47 @@ public class ScanTest {
|
||||||
sc.delimiter();
|
sc.delimiter();
|
||||||
sc.useDelimiter("blah");
|
sc.useDelimiter("blah");
|
||||||
sc.useDelimiter(Pattern.compile("blah"));
|
sc.useDelimiter(Pattern.compile("blah"));
|
||||||
for (int i=0; i<NUM_METHODS; i++) {
|
|
||||||
|
for (Consumer<Scanner> method : methodList) {
|
||||||
try {
|
try {
|
||||||
methodCall(sc, i);
|
method.accept(sc);
|
||||||
failCount++;
|
failCount++;
|
||||||
} catch (IllegalStateException ise) {
|
} catch (IllegalStateException ise) {
|
||||||
// Correct
|
// Correct
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
report("Close test");
|
report("Close test");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int NUM_METHODS = 23;
|
static List<Consumer<Scanner>> methodList = Arrays.asList(
|
||||||
|
Scanner::hasNext,
|
||||||
private static void methodCall(Scanner sc, int i) {
|
Scanner::next,
|
||||||
switch(i) {
|
sc -> sc.hasNext(Pattern.compile("blah")),
|
||||||
case 0: sc.hasNext(); break;
|
sc -> sc.next(Pattern.compile("blah")),
|
||||||
case 1: sc.next(); break;
|
Scanner::hasNextBoolean,
|
||||||
case 2: sc.hasNext(Pattern.compile("blah")); break;
|
Scanner::nextBoolean,
|
||||||
case 3: sc.next(Pattern.compile("blah")); break;
|
Scanner::hasNextByte,
|
||||||
case 4: sc.hasNextBoolean(); break;
|
Scanner::nextByte,
|
||||||
case 5: sc.nextBoolean(); break;
|
Scanner::hasNextShort,
|
||||||
case 6: sc.hasNextByte(); break;
|
Scanner::nextShort,
|
||||||
case 7: sc.nextByte(); break;
|
Scanner::hasNextInt,
|
||||||
case 8: sc.hasNextShort(); break;
|
Scanner::nextInt,
|
||||||
case 9: sc.nextShort(); break;
|
Scanner::hasNextLong,
|
||||||
case 10: sc.hasNextInt(); break;
|
Scanner::nextLong,
|
||||||
case 11: sc.nextInt(); break;
|
Scanner::hasNextFloat,
|
||||||
case 12: sc.hasNextLong(); break;
|
Scanner::nextFloat,
|
||||||
case 13: sc.nextLong(); break;
|
Scanner::hasNextDouble,
|
||||||
case 14: sc.hasNextFloat(); break;
|
Scanner::nextDouble,
|
||||||
case 15: sc.nextFloat(); break;
|
Scanner::hasNextBigInteger,
|
||||||
case 16: sc.hasNextDouble(); break;
|
Scanner::nextBigInteger,
|
||||||
case 17: sc.nextDouble(); break;
|
Scanner::hasNextBigDecimal,
|
||||||
case 18: sc.hasNextBigInteger(); break;
|
Scanner::nextBigDecimal,
|
||||||
case 19: sc.nextBigInteger(); break;
|
Scanner::hasNextLine,
|
||||||
case 20: sc.hasNextBigDecimal(); break;
|
Scanner::tokens,
|
||||||
case 21: sc.nextBigDecimal(); break;
|
sc -> sc.findAll(Pattern.compile("blah")),
|
||||||
case 22: sc.hasNextLine(); break;
|
sc -> sc.findAll("blah")
|
||||||
default:
|
);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void removeTest() throws Exception {
|
public static void removeTest() throws Exception {
|
||||||
Scanner sc = new Scanner("testing");
|
Scanner sc = new Scanner("testing");
|
||||||
|
@ -864,19 +868,20 @@ public class ScanTest {
|
||||||
|
|
||||||
public static void fromFileTest() throws Exception {
|
public static void fromFileTest() throws Exception {
|
||||||
File f = new File(System.getProperty("test.src", "."), "input.txt");
|
File f = new File(System.getProperty("test.src", "."), "input.txt");
|
||||||
Scanner sc = new Scanner(f).useDelimiter("\n+");
|
try (Scanner sc = new Scanner(f)) {
|
||||||
String testDataTag = sc.findWithinHorizon("fromFileTest", 0);
|
sc.useDelimiter("\n+");
|
||||||
if (!testDataTag.equals("fromFileTest"))
|
String testDataTag = sc.findWithinHorizon("fromFileTest", 0);
|
||||||
failCount++;
|
if (!testDataTag.equals("fromFileTest"))
|
||||||
|
failCount++;
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
while (sc.hasNextLong()) {
|
while (sc.hasNextLong()) {
|
||||||
long blah = sc.nextLong();
|
long blah = sc.nextLong();
|
||||||
count++;
|
count++;
|
||||||
|
}
|
||||||
|
if (count != 7)
|
||||||
|
failCount++;
|
||||||
}
|
}
|
||||||
if (count != 7)
|
|
||||||
failCount++;
|
|
||||||
sc.close();
|
|
||||||
report("From file");
|
report("From file");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -884,7 +889,7 @@ public class ScanTest {
|
||||||
Scanner s = new Scanner("1 fish 2 fish red fish blue fish");
|
Scanner s = new Scanner("1 fish 2 fish red fish blue fish");
|
||||||
s.useDelimiter("\\s*fish\\s*");
|
s.useDelimiter("\\s*fish\\s*");
|
||||||
List <String> results = new ArrayList<String>();
|
List <String> results = new ArrayList<String>();
|
||||||
while(s.hasNext())
|
while (s.hasNext())
|
||||||
results.add(s.next());
|
results.add(s.next());
|
||||||
System.out.println(results);
|
System.out.println(results);
|
||||||
}
|
}
|
||||||
|
@ -1472,14 +1477,112 @@ public class ScanTest {
|
||||||
report("Reset test");
|
report("Reset test");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test that closing the stream also closes the underlying Scanner.
|
||||||
|
* The cases of attempting to open streams on a closed Scanner are
|
||||||
|
* covered by closeTest().
|
||||||
|
*/
|
||||||
|
public static void streamCloseTest() throws Exception {
|
||||||
|
Scanner sc;
|
||||||
|
|
||||||
|
Scanner sc1 = new Scanner("xyzzy");
|
||||||
|
sc1.tokens().close();
|
||||||
|
try {
|
||||||
|
sc1.hasNext();
|
||||||
|
failCount++;
|
||||||
|
} catch (IllegalStateException ise) {
|
||||||
|
// Correct result
|
||||||
|
}
|
||||||
|
|
||||||
|
Scanner sc2 = new Scanner("a b c d e f");
|
||||||
|
try {
|
||||||
|
sc2.tokens()
|
||||||
|
.peek(s -> sc2.close())
|
||||||
|
.count();
|
||||||
|
} catch (IllegalStateException ise) {
|
||||||
|
// Correct result
|
||||||
|
}
|
||||||
|
|
||||||
|
Scanner sc3 = new Scanner("xyzzy");
|
||||||
|
sc3.findAll("q").close();
|
||||||
|
try {
|
||||||
|
sc3.hasNext();
|
||||||
|
failCount++;
|
||||||
|
} catch (IllegalStateException ise) {
|
||||||
|
// Correct result
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Scanner sc4 = new Scanner(inputFile)) {
|
||||||
|
sc4.findAll("[0-9]+")
|
||||||
|
.peek(s -> sc4.close())
|
||||||
|
.count();
|
||||||
|
failCount++;
|
||||||
|
} catch (IllegalStateException ise) {
|
||||||
|
// Correct result
|
||||||
|
}
|
||||||
|
|
||||||
|
report("Streams Close test");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test ConcurrentModificationException
|
||||||
|
*/
|
||||||
|
public static void streamComodTest() {
|
||||||
|
try {
|
||||||
|
Scanner sc = new Scanner("a b c d e f");
|
||||||
|
sc.tokens()
|
||||||
|
.peek(s -> sc.hasNext())
|
||||||
|
.count();
|
||||||
|
failCount++;
|
||||||
|
} catch (ConcurrentModificationException cme) {
|
||||||
|
// Correct result
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Scanner sc = new Scanner("a b c d e f");
|
||||||
|
Iterator<String> it = sc.tokens().iterator();
|
||||||
|
it.next();
|
||||||
|
sc.next();
|
||||||
|
it.next();
|
||||||
|
failCount++;
|
||||||
|
} catch (ConcurrentModificationException cme) {
|
||||||
|
// Correct result
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String input = IntStream.range(0, 100)
|
||||||
|
.mapToObj(String::valueOf)
|
||||||
|
.collect(Collectors.joining(" "));
|
||||||
|
Scanner sc = new Scanner(input);
|
||||||
|
sc.findAll("[0-9]+")
|
||||||
|
.peek(s -> sc.hasNext())
|
||||||
|
.count();
|
||||||
|
failCount++;
|
||||||
|
} catch (ConcurrentModificationException cme) {
|
||||||
|
// Correct result
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String input = IntStream.range(0, 100)
|
||||||
|
.mapToObj(String::valueOf)
|
||||||
|
.collect(Collectors.joining(" "));
|
||||||
|
Scanner sc = new Scanner(input);
|
||||||
|
Iterator<MatchResult> it = sc.findAll("[0-9]+").iterator();
|
||||||
|
it.next();
|
||||||
|
sc.next();
|
||||||
|
it.next();
|
||||||
|
failCount++;
|
||||||
|
} catch (ConcurrentModificationException cme) {
|
||||||
|
// Correct result
|
||||||
|
}
|
||||||
|
|
||||||
|
report("Streams Comod test");
|
||||||
|
}
|
||||||
|
|
||||||
private static void report(String testName) {
|
private static void report(String testName) {
|
||||||
int spacesToAdd = 30 - testName.length();
|
System.err.printf("%-30s: %s%n", testName,
|
||||||
StringBuffer paddedNameBuffer = new StringBuffer(testName);
|
(failCount == 0) ? "Passed" : String.format("Failed(%d)", failCount));
|
||||||
for (int i=0; i<spacesToAdd; i++)
|
|
||||||
paddedNameBuffer.append(" ");
|
|
||||||
String paddedName = paddedNameBuffer.toString();
|
|
||||||
System.err.println(paddedName + ": " +
|
|
||||||
(failCount==0 ? "Passed":"Failed("+failCount+")"));
|
|
||||||
if (failCount > 0)
|
if (failCount > 0)
|
||||||
failure = true;
|
failure = true;
|
||||||
failCount = 0;
|
failCount = 0;
|
||||||
|
|
125
jdk/test/java/util/Scanner/ScannerStreamTest.java
Normal file
125
jdk/test/java/util/Scanner/ScannerStreamTest.java
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.testng.annotations.DataProvider;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Scanner;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.regex.MatchResult;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.LambdaTestHelpers;
|
||||||
|
import java.util.stream.OpTestCase;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.util.stream.TestData;
|
||||||
|
|
||||||
|
import static org.testng.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @bug 8072722
|
||||||
|
* @summary Tests of stream support in java.util.Scanner
|
||||||
|
* @library ../stream/bootlib
|
||||||
|
* @build java.util.stream.OpTestCase
|
||||||
|
* @run testng/othervm ScannerStreamTest
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public class ScannerStreamTest extends OpTestCase {
|
||||||
|
|
||||||
|
static File inputFile = new File(System.getProperty("test.src", "."), "input.txt");
|
||||||
|
|
||||||
|
@DataProvider(name = "Patterns")
|
||||||
|
public static Object[][] makeStreamTestData() {
|
||||||
|
// each inner array is [String description, String input, String delimiter]
|
||||||
|
// delimiter may be null
|
||||||
|
List<Object[]> data = new ArrayList<>();
|
||||||
|
|
||||||
|
data.add(new Object[] { "default delimiter", "abc def ghi", null });
|
||||||
|
data.add(new Object[] { "fixed delimiter", "abc,def,,ghi", "," });
|
||||||
|
data.add(new Object[] { "regexp delimiter", "###abc##def###ghi###j", "#+" });
|
||||||
|
|
||||||
|
return data.toArray(new Object[0][]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Scanner makeScanner(String input, String delimiter) {
|
||||||
|
Scanner sc = new Scanner(input);
|
||||||
|
if (delimiter != null) {
|
||||||
|
sc.useDelimiter(delimiter);
|
||||||
|
}
|
||||||
|
return sc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(dataProvider = "Patterns")
|
||||||
|
public void tokensTest(String description, String input, String delimiter) {
|
||||||
|
// derive expected result by using conventional loop
|
||||||
|
Scanner sc = makeScanner(input, delimiter);
|
||||||
|
List<String> expected = new ArrayList<>();
|
||||||
|
while (sc.hasNext()) {
|
||||||
|
expected.add(sc.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
Supplier<Stream<String>> ss = () -> makeScanner(input, delimiter).tokens();
|
||||||
|
withData(TestData.Factory.ofSupplier(description, ss))
|
||||||
|
.stream(LambdaTestHelpers.identity())
|
||||||
|
.expectedResult(expected)
|
||||||
|
.exercise();
|
||||||
|
}
|
||||||
|
|
||||||
|
Scanner makeFileScanner(File file) {
|
||||||
|
try {
|
||||||
|
return new Scanner(file, "UTF-8");
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
throw new UncheckedIOException(ioe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void findAllTest() {
|
||||||
|
// derive expected result by using conventional loop
|
||||||
|
Pattern pat = Pattern.compile("[A-Z]{7,}");
|
||||||
|
List<String> expected = new ArrayList<>();
|
||||||
|
|
||||||
|
try (Scanner sc = makeFileScanner(inputFile)) {
|
||||||
|
String match;
|
||||||
|
while ((match = sc.findWithinHorizon(pat, 0)) != null) {
|
||||||
|
expected.add(match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Supplier<Stream<String>> ss =
|
||||||
|
() -> makeFileScanner(inputFile).findAll(pat).map(MatchResult::group);
|
||||||
|
|
||||||
|
withData(TestData.Factory.ofSupplier("findAllTest", ss))
|
||||||
|
.stream(LambdaTestHelpers.identity())
|
||||||
|
.expectedResult(expected)
|
||||||
|
.exercise();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue