diff --git a/src/main/java/org/json/CharSequenceReader.java b/src/main/java/org/json/CharSequenceReader.java new file mode 100644 index 000000000..5abba0238 --- /dev/null +++ b/src/main/java/org/json/CharSequenceReader.java @@ -0,0 +1,101 @@ +package org.json; + +import java.io.Reader; + +/** + * a Reader to read from a {@link CharSequence} e.g. a {@link String}. + */ +public class CharSequenceReader extends Reader { + + private final CharSequence charSequence; + private int index; + private int markedIndex; + + /** + * creates a new CharSequenceReader to read from the given CharSequence + * @param charSequence the input to read. + */ + public CharSequenceReader(CharSequence charSequence) { + this.charSequence = charSequence; + } + + /** + * reads the next character from the input. + * @return returns the read character or -1 if the end of the input is reached. + */ + @Override + public int read() { + if (index < charSequence.length()) { + return charSequence.charAt(index++); + } + return -1; + } + + + /** + * reads a maximum number of characters from the input into the charBuffer starting at the offset + * and returns the number of actual read characters. + * + * @param charBuffer the target charBuffer to write the read characters into + * @param offset the offset index where to start writing into the charBuffer + * @param length the maximum number of characters to read + * @return the number of characters read into the charBuffer or -1 if the end of the input is reached. + */ + @Override + public int read(char[] charBuffer, int offset, int length) { + if (charBuffer == null) { + throw new NullPointerException("The target charBuffer cannot be null"); + } else if (offset < 0) { + throw new IndexOutOfBoundsException("Offset must not be negative"); + } else if (length < 0) { + throw new IndexOutOfBoundsException("Length must not be negative"); + } else if (offset + length > charBuffer.length) { + throw new IndexOutOfBoundsException("Offset + length must not be larger than the charBuffer length"); + } else if (index >= charSequence.length()) { + return -1; + } + + int charsToRead = Math.min(length, charSequence.length() - index); + for (int i = 0; i < charsToRead; i++) { + charBuffer[offset + i] = charSequence.charAt(index++); + } + + return charsToRead; + } + + /** + * returns true to indicate that mark is supported. + * @return true + */ + @Override + public boolean markSupported() { + return true; + } + + /** + * marks the reader on the current position to later jump back to it by using {@link #reset()}. + * @param readAheadLimit will be ignored. This reader has no readAheadLimit. + */ + @Override + public void mark(int readAheadLimit) { + markedIndex = this.index; + } + + /** + * resets the reader to the previous marked position used by {@link #mark(int)}. + */ + @Override + public void reset() { + index = markedIndex; + } + + + /** + * does nothing. + */ + @Override + public void close() { + + } + +} diff --git a/src/main/java/org/json/JSONTokener.java b/src/main/java/org/json/JSONTokener.java index b8808bb4f..19b8cafeb 100644 --- a/src/main/java/org/json/JSONTokener.java +++ b/src/main/java/org/json/JSONTokener.java @@ -67,7 +67,7 @@ public JSONTokener(InputStream inputStream) { * @param s A source string. */ public JSONTokener(String s) { - this(new StringReader(s)); + this(new CharSequenceReader(s)); } diff --git a/src/test/java/org/json/junit/CharSequenceReaderTest.java b/src/test/java/org/json/junit/CharSequenceReaderTest.java new file mode 100644 index 000000000..12fe0699f --- /dev/null +++ b/src/test/java/org/json/junit/CharSequenceReaderTest.java @@ -0,0 +1,88 @@ +package org.json.junit; + +import org.json.CharSequenceReader; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class CharSequenceReaderTest { + + @Test + public void testReadSingleCharacter() { + CharSequenceReader reader = new CharSequenceReader("test"); + assertEquals('t', reader.read()); + assertEquals('e', reader.read()); + assertEquals('s', reader.read()); + assertEquals('t', reader.read()); + assertEquals(-1, reader.read()); + assertEquals(-1, reader.read()); + } + + @Test + public void testReadToCharBuffer() { + CharSequenceReader reader = new CharSequenceReader("test"); + char[] buffer = new char[10]; + int read = reader.read(buffer, 2, 3); + assertEquals(3, read); + assertArrayEquals(new char[]{0, 0, 't', 'e', 's', 0, 0, 0, 0, 0}, buffer); + } + + @Test + public void testReadPastEnd() { + CharSequenceReader reader = new CharSequenceReader("test"); + char[] buffer = new char[5]; + assertEquals(4, reader.read(buffer, 0, 5)); + assertEquals(-1, reader.read(buffer, 0, 5)); + } + + @Test + public void testMarkAndReset() { + CharSequenceReader reader = new CharSequenceReader("test"); + assertEquals('t', reader.read()); + reader.mark(0); + assertEquals('e', reader.read()); + assertEquals('s', reader.read()); + reader.reset(); + assertEquals('e', reader.read()); + } + + @Test(expected = NullPointerException.class) + public void testReadToNullBuffer() { + CharSequenceReader reader = new CharSequenceReader("test"); + reader.read(null, 0, 1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testNegativeOffset() { + CharSequenceReader reader = new CharSequenceReader("test"); + char[] buffer = new char[10]; + reader.read(buffer, -1, 5); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testNegativeLength() { + CharSequenceReader reader = new CharSequenceReader("test"); + char[] buffer = new char[10]; + reader.read(buffer, 0, -5); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testOffsetPlusLengthExceedsBuffer() { + CharSequenceReader reader = new CharSequenceReader("test"); + char[] buffer = new char[5]; + reader.read(buffer, 3, 3); + } + + @Test + public void testCloseDoesNothing() { + CharSequenceReader reader = new CharSequenceReader("test"); + reader.close(); + assertEquals('t', reader.read()); + } + + @Test + public void testMarkSupported() { + CharSequenceReader reader = new CharSequenceReader("test"); + assertTrue(reader.markSupported()); + } +} \ No newline at end of file