From 5aad7937b7fd3715b173ab1b0f7d093c1735dfc9 Mon Sep 17 00:00:00 2001 From: Xueming Shen Date: Tue, 14 Jun 2016 16:56:27 -0700 Subject: [PATCH] 8139414: java.util.Scanner hasNext() returns true, next() throws NoSuchElementException 8072582: Scanner delimits incorrectly when delimiter spans a buffer boundary Reviewed-by: smarks --- .../share/classes/java/util/Scanner.java | 39 +++++++------- jdk/test/java/util/Scanner/ScanTest.java | 51 ++++++++++++++++++- 2 files changed, 71 insertions(+), 19 deletions(-) diff --git a/jdk/src/java.base/share/classes/java/util/Scanner.java b/jdk/src/java.base/share/classes/java/util/Scanner.java index a0e6dd89acc..32427e12801 100644 --- a/jdk/src/java.base/share/classes/java/util/Scanner.java +++ b/jdk/src/java.base/share/classes/java/util/Scanner.java @@ -793,7 +793,6 @@ public final class Scanner implements Iterator, Closeable { private void readInput() { if (buf.limit() == buf.capacity()) makeSpace(); - // Prepare to receive data int p = buf.position(); buf.position(buf.limit()); @@ -806,15 +805,12 @@ public final class Scanner implements Iterator, Closeable { lastException = ioe; n = -1; } - if (n == -1) { sourceClosed = true; needInput = false; } - if (n > 0) needInput = false; - // Restore current position and limit for reading buf.limit(buf.position()); buf.position(p); @@ -871,15 +867,20 @@ public final class Scanner implements Iterator, Closeable { matchValid = false; matcher.usePattern(delimPattern); matcher.region(position, buf.limit()); - // Skip delims first - if (matcher.lookingAt()) + if (matcher.lookingAt()) { + if (matcher.hitEnd() && !sourceClosed) { + // more input might change the match of delims, in which + // might change whether or not if there is token left in + // buffer (don't update the "position" in this case) + needInput = true; + return false; + } position = matcher.end(); - + } // If we are sitting at the end, no more tokens in buffer if (position == buf.limit()) return false; - return true; } @@ -900,7 +901,6 @@ public final class Scanner implements Iterator, Closeable { */ private String getCompleteTokenInBuffer(Pattern pattern) { matchValid = false; - // Skip delims first matcher.usePattern(delimPattern); if (!skipped) { // Enforcing only one skip of leading delims @@ -941,13 +941,16 @@ public final class Scanner implements Iterator, Closeable { foundNextDelim = matcher.find(); } if (foundNextDelim) { - // In the rare case that more input could cause the match - // to be lost and there is more input coming we must wait - // for more input. Note that hitting the end is okay as long - // as the match cannot go away. It is the beginning of the - // next delims we want to be sure about, we don't care if - // they potentially extend further. - if (matcher.requireEnd() && !sourceClosed) { + // In two rare cases that more input might cause the match to be + // lost or change. + // (1) if requireEnd() is true, more input might cause the match + // to be lost, we must wait for more input. + // (2) while hitting the end is okay IF the match does not + // go away AND the beginning of the next delims does not change + // (we don't care if they potentially extend further). But it's + // possible that more input could cause the beginning of the + // delims change, so have to wait for more input as well. + if ((matcher.requireEnd() || matcher.hitEnd()) && !sourceClosed) { needInput = true; return null; } @@ -1341,8 +1344,9 @@ public final class Scanner implements Iterator, Closeable { saveState(); modCount++; while (!sourceClosed) { - if (hasTokenInBuffer()) + if (hasTokenInBuffer()) { return revertState(true); + } readInput(); } boolean result = hasTokenInBuffer(); @@ -1365,7 +1369,6 @@ public final class Scanner implements Iterator, Closeable { ensureOpen(); clearCaches(); modCount++; - while (true) { String token = getCompleteTokenInBuffer(null); if (token != null) { diff --git a/jdk/test/java/util/Scanner/ScanTest.java b/jdk/test/java/util/Scanner/ScanTest.java index 45383f0674a..cc8bca9deaa 100644 --- a/jdk/test/java/util/Scanner/ScanTest.java +++ b/jdk/test/java/util/Scanner/ScanTest.java @@ -24,7 +24,7 @@ /** * @test * @bug 4313885 4926319 4927634 5032610 5032622 5049968 5059533 6223711 6277261 6269946 6288823 - * 8072722 + * 8072722 8072582 8139414 * @summary Basic tests of java.util.Scanner methods * @key randomness * @modules jdk.localedata @@ -70,6 +70,7 @@ public class ScanTest { ioExceptionTest(); matchTest(); delimiterTest(); + boundaryDelimTest(); useLocaleTest(); closeTest(); cacheTest(); @@ -504,6 +505,54 @@ public class ScanTest { report("Single delim test"); } + private static void append(StringBuilder sb, char c, int n) { + for (int i = 0; i < n; i++) { + sb.append(c); + } + } + + public static void boundaryDelimTest() throws Exception { + // 8072582 + StringBuilder sb = new StringBuilder(); + append(sb, 'a', 228); sb.append(","); + append(sb, 'b', 293); sb.append("#,#"); + append(sb, 'c', 308); sb.append(","); + append(sb, 'd', 188); sb.append("#,#"); + append(sb, 'e', 2); + try (Scanner scanner = new Scanner(sb.toString())) { + scanner.useDelimiter("(#,#)|(,)"); + while(scanner.hasNext()){ + String next = scanner.next(); + if(next.contains("#")){ + System.out.printf("[%s]%n", next); + failCount++; + } + } + } + + // 8139414 + int i = 1019; + sb = new StringBuilder(); + sb.append("--;"); + for (int j = 0; j < 1019; ++j) { + sb.append(j%10); + } + sb.append("-;-"); + String text = sb.toString(); + try (Scanner scanner = new Scanner(text)) { + scanner.useDelimiter("-;(-)?"); + while (scanner.hasNext()) { + scanner.next(); + } + } catch (NoSuchElementException e) { + System.out.println("Caught NoSuchElementException " + e); + e.printStackTrace(); + failCount++; + } + + report("delim at boundary test"); + } + /* * The hasNextPattern caches a match of a pattern called the regular cache * The hasNextType caches a match of that type called the type cache