8341566: Add Reader.of(CharSequence)

Reviewed-by: rriggs, jpai, liach, alanb
This commit is contained in:
Markus KARG 2024-10-24 14:34:58 +00:00 committed by Chen Liang
parent b0ac633b2d
commit 3c14c2babb
3 changed files with 349 additions and 47 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2024, 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
@ -140,6 +140,119 @@ public abstract class Reader implements Readable, Closeable {
};
}
/**
* Returns a {@code Reader} that reads characters from a
* {@code CharSequence}. The reader is initially open and reading starts at
* the first character in the sequence.
*
* <p> The returned reader supports the {@link #mark mark()} and
* {@link #reset reset()} operations.
*
* <p> The resulting reader is not safe for use by multiple
* concurrent threads. If the reader is to be used by more than one
* thread it should be controlled by appropriate synchronization.
*
* <p> If the sequence changes while the reader is open, e.g. the length
* changes, the behavior is undefined.
*
* @param cs {@code CharSequence} providing the character stream.
* @return a {@code Reader} which reads characters from {@code cs}
* @throws NullPointerException if {@code cs} is {@code null}
*
* @since 24
*/
public static Reader of(final CharSequence cs) {
Objects.requireNonNull(cs);
return new Reader() {
private boolean isClosed;
private int next = 0;
private int mark = 0;
/** Check to make sure that the stream has not been closed */
private void ensureOpen() throws IOException {
if (isClosed)
throw new IOException("Stream closed");
}
@Override
public int read() throws IOException {
ensureOpen();
if (next >= cs.length())
return -1;
return cs.charAt(next++);
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
ensureOpen();
Objects.checkFromIndexSize(off, len, cbuf.length);
if (len == 0) {
return 0;
}
int length = cs.length();
if (next >= length)
return -1;
int n = Math.min(length - next, len);
switch (cs) {
case String s -> s.getChars(next, next + n, cbuf, off);
case StringBuilder sb -> sb.getChars(next, next + n, cbuf, off);
case StringBuffer sb -> sb.getChars(next, next + n, cbuf, off);
case CharBuffer cb -> cb.get(next, cbuf, off, n);
default -> {
for (int i = 0; i < n; i++)
cbuf[off + i] = cs.charAt(next + i);
}
}
next += n;
return n;
}
@Override
public long skip(long n) throws IOException {
ensureOpen();
if (next >= cs.length())
return 0;
// Bound skip by beginning and end of the source
long r = Math.min(cs.length() - next, n);
r = Math.max(-next, r);
next += (int)r;
return r;
}
@Override
public boolean ready() throws IOException {
ensureOpen();
return true;
}
@Override
public boolean markSupported() {
return true;
}
@Override
public void mark(int readAheadLimit) throws IOException {
if (readAheadLimit < 0){
throw new IllegalArgumentException("Read-ahead limit < 0");
}
ensureOpen();
mark = next;
}
@Override
public void reset() throws IOException {
ensureOpen();
next = mark;
}
@Override
public void close() {
isClosed = true;
}
};
}
/**
* The object used to synchronize operations on this stream. For
* efficiency, a character-stream object may use an object other than

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2024, 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
@ -30,16 +30,17 @@ import java.util.Objects;
/**
* A character stream whose source is a string.
*
* @apiNote
* {@link Reader#of(CharSequence)} provides a method to read from any
* {@link CharSequence} that may be more efficient than {@code StringReader}.
*
* @author Mark Reinhold
* @since 1.1
*/
public class StringReader extends Reader {
private final int length;
private String str;
private int next = 0;
private int mark = 0;
private final Reader r;
/**
* Creates a new string reader.
@ -47,14 +48,7 @@ public class StringReader extends Reader {
* @param s String providing the character stream.
*/
public StringReader(String s) {
this.length = s.length();
this.str = s;
}
/** Check to make sure that the stream has not been closed */
private void ensureOpen() throws IOException {
if (str == null)
throw new IOException("Stream closed");
r = Reader.of(s);
}
/**
@ -67,10 +61,7 @@ public class StringReader extends Reader {
*/
public int read() throws IOException {
synchronized (lock) {
ensureOpen();
if (next >= length)
return -1;
return str.charAt(next++);
return r.read();
}
}
@ -94,17 +85,7 @@ public class StringReader extends Reader {
*/
public int read(char[] cbuf, int off, int len) throws IOException {
synchronized (lock) {
ensureOpen();
Objects.checkFromIndexSize(off, len, cbuf.length);
if (len == 0) {
return 0;
}
if (next >= length)
return -1;
int n = Math.min(length - next, len);
str.getChars(next, next + n, cbuf, off);
next += n;
return n;
return r.read(cbuf, off, len);
}
}
@ -130,14 +111,7 @@ public class StringReader extends Reader {
*/
public long skip(long n) throws IOException {
synchronized (lock) {
ensureOpen();
if (next >= length)
return 0;
// Bound skip by beginning and end of the source
long r = Math.min(length - next, n);
r = Math.max(-next, r);
next += (int)r;
return r;
return r.skip(n);
}
}
@ -150,8 +124,7 @@ public class StringReader extends Reader {
*/
public boolean ready() throws IOException {
synchronized (lock) {
ensureOpen();
return true;
return r.ready();
}
}
@ -176,12 +149,8 @@ public class StringReader extends Reader {
* @throws IOException If an I/O error occurs
*/
public void mark(int readAheadLimit) throws IOException {
if (readAheadLimit < 0){
throw new IllegalArgumentException("Read-ahead limit < 0");
}
synchronized (lock) {
ensureOpen();
mark = next;
r.mark(readAheadLimit);
}
}
@ -193,8 +162,7 @@ public class StringReader extends Reader {
*/
public void reset() throws IOException {
synchronized (lock) {
ensureOpen();
next = mark;
r.reset();
}
}
@ -207,7 +175,11 @@ public class StringReader extends Reader {
*/
public void close() {
synchronized (lock) {
str = null;
try {
r.close();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
}