From ecb9a47d1a19796fd26f89dfb984f54477ae96fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Hohwiller?= Date: Mon, 25 Dec 2023 15:17:52 +0100 Subject: [PATCH] small improvement (min,max) --- .../scanner/AbstractCharStreamScanner.java | 28 +++++++++++++++++-- .../mmm/scanner/CharSequenceScanner.java | 2 +- .../github/mmm/scanner/CharStreamScanner.java | 24 ++++++++++------ .../AbstractCharStreamScannerTest.java | 6 ++-- 4 files changed, 44 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/io/github/mmm/scanner/AbstractCharStreamScanner.java b/core/src/main/java/io/github/mmm/scanner/AbstractCharStreamScanner.java index d36ffaf..18cb815 100644 --- a/core/src/main/java/io/github/mmm/scanner/AbstractCharStreamScanner.java +++ b/core/src/main/java/io/github/mmm/scanner/AbstractCharStreamScanner.java @@ -1417,11 +1417,14 @@ public boolean skipOver(String substring, boolean ignoreCase, CharFilter stopFil } @Override - public String readWhile(CharFilter filter, int max) { + public String readWhile(CharFilter filter, int min, int max) { if (max < 0) { throw new IllegalArgumentException("Max must NOT be negative: " + max); } + if (max < min) { + throw new IllegalArgumentException("Min (" + min + ") must be less or requal to max (" + max + ")"); + } StringBuilder builder = null; if (this.offset >= this.limit) { fill(); @@ -1439,7 +1442,7 @@ public String readWhile(CharFilter filter, int max) { while (this.offset < end) { char c = this.buffer[this.offset]; if (!filter.accept(c)) { - return getAppended(builder, start, this.offset); + return requireMin(getAppended(builder, start, this.offset), min, filter); } handleChar(c); this.offset++; @@ -1448,11 +1451,30 @@ public String readWhile(CharFilter filter, int max) { remain -= len; builder = append(builder, start, this.offset); if ((remain == 0) || !fill()) { - return eot(builder, true); + return requireMin(eot(builder, true), min, filter); } } } + /** + * @param actual the actual number of characters. + * @param min the minimum number of characters required. + * @param filter the {@link CharFilter} that was used. + */ + protected void requireMin(int actual, int min, CharFilter filter) { + + if (actual < min) { + throw new IllegalStateException( + "Required at least " + min + " character(s) (" + filter.getDescription() + ") but found only " + actual); + } + } + + private String requireMin(String result, int min, CharFilter filter) { + + requireMin(result.length(), min, filter); + return result; + } + /** * @return the {@link String} with the characters that have already been parsed but are still available in the * underlying buffer. May be used for debugging or error messages. diff --git a/core/src/main/java/io/github/mmm/scanner/CharSequenceScanner.java b/core/src/main/java/io/github/mmm/scanner/CharSequenceScanner.java index f317bc5..2f30251 100644 --- a/core/src/main/java/io/github/mmm/scanner/CharSequenceScanner.java +++ b/core/src/main/java/io/github/mmm/scanner/CharSequenceScanner.java @@ -470,7 +470,7 @@ public void require(String expected, boolean ignoreCase) { } @Override - public String readWhile(CharFilter filter, int max) { + public String readWhile(CharFilter filter, int min, int max) { int currentPos = this.offset; int len = skipWhile(filter, max); diff --git a/core/src/main/java/io/github/mmm/scanner/CharStreamScanner.java b/core/src/main/java/io/github/mmm/scanner/CharStreamScanner.java index 516e932..95cd636 100644 --- a/core/src/main/java/io/github/mmm/scanner/CharStreamScanner.java +++ b/core/src/main/java/io/github/mmm/scanner/CharStreamScanner.java @@ -305,17 +305,20 @@ default String readUntil(CharFilter filter, boolean acceptEnd, String stop, bool /** * @param stopFilter the {@link CharFilter} that decides which characters to {@link CharFilter#accept(char) accept} as * stop characters. - * @param maxLength the (maximum) length of the characters to consume. + * @param min the minimum number of characters expected. + * @param max the (maximum) length of the characters to consume. * @return the {@link String} with all consumed characters excluding the stop character. If no {@code stop} character * was found until {@code maxLength} characters have been consumed, this method behaves like {@link #read(int) * read(maxLength)}. + * @throws IllegalStateException if less than the minimum number of characters have been + * {@link CharFilter#accept(char) rejected}. * @see #read(int) - * @see #readWhile(CharFilter, int) + * @see #readWhile(CharFilter, int, int) * @see #peekUntil(CharFilter, int) */ - default String readUntil(CharFilter stopFilter, int maxLength) { + default String readUntil(CharFilter stopFilter, int min, int max) { - return readWhile(stopFilter.negate(), maxLength); + return readWhile(stopFilter.negate(), min, max); } /** @@ -326,13 +329,13 @@ default String readUntil(CharFilter stopFilter, int maxLength) { * * @see #skipWhile(CharFilter) * - * @param filter is used to {@link CharFilter#accept(char) decide} which characters should be accepted. + * @param filter used to {@link CharFilter#accept(char) decide} which characters should be accepted. * @return a string with all characters {@link CharFilter#accept(char) accepted} by the given {@code filter}. Will be * the empty string if no character was accepted. */ default String readWhile(CharFilter filter) { - return readWhile(filter, Integer.MAX_VALUE); + return readWhile(filter, 0, Integer.MAX_VALUE); } /** @@ -345,13 +348,16 @@ default String readWhile(CharFilter filter) { * * @see #skipWhile(char) * - * @param filter is used to {@link CharFilter#accept(char) decide} which characters should be accepted. - * @param max is the maximum number of characters that should be read. + * @param filter used to {@link CharFilter#accept(char) decide} which characters should be accepted. + * @param min the minimum number of characters expected. + * @param max the maximum number of characters that should be read. * @return a string with all characters {@link CharFilter#accept(char) accepted} by the given {@code filter} limited * to the length of {@code max} and the {@link #hasNext() end} of this scanner. Will be the empty string if no * character was accepted. + * @throws IllegalStateException if less than the minimum number of characters have been + * {@link CharFilter#accept(char) accepted}. */ - String readWhile(CharFilter filter, int max); + String readWhile(CharFilter filter, int min, int max); /** * @return a {@link String} with the data until the end of the current line or the end of the data. Will be diff --git a/core/src/test/java/io/github/mmm/scanner/AbstractCharStreamScannerTest.java b/core/src/test/java/io/github/mmm/scanner/AbstractCharStreamScannerTest.java index 844e66c..da9ad36 100644 --- a/core/src/test/java/io/github/mmm/scanner/AbstractCharStreamScannerTest.java +++ b/core/src/test/java/io/github/mmm/scanner/AbstractCharStreamScannerTest.java @@ -712,12 +712,12 @@ public void testReadWhile() { // then assertThat(scanner.readWhile(textFilter)).isEqualTo("abc"); assertThat(scanner.readWhile(textFilter)).isEmpty(); - assertThat(scanner.readWhile(textFilter, 0)).isEmpty(); + assertThat(scanner.readWhile(textFilter, 0, 0)).isEmpty(); assertThat(scanner.readWhile(spaceFilter)).isEqualTo(" "); assertThat(scanner.readWhile(textFilter)).isEqualTo("def"); assertThat(scanner.readWhile(spaceFilter)).isEqualTo(" "); - assertThat(scanner.readWhile(textFilter, 2)).isEqualTo("gh"); - assertThat(scanner.readWhile(textFilter, 2)).isEqualTo("i"); + assertThat(scanner.readWhile(textFilter, 0, 2)).isEqualTo("gh"); + assertThat(scanner.readWhile(textFilter, 0, 2)).isEqualTo("i"); assertThat(scanner.hasNext()).isFalse(); assertThat(scanner.getPosition()).isEqualTo(12); assertThat(scanner.getColumn()).isEqualTo(13);