From 5c8b80aa32e8f3b589ce3c76a20fd3282b73dffa Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Sun, 26 Oct 2014 17:26:21 +0300 Subject: [PATCH] Add BufferedInputStream and StringTokenizer --- .../java/io/TBufferedInputStream.java | 255 +++++++++++++ .../classlib/java/io/TFilterInputStream.java | 70 ++++ .../classlib/java/util/TStringTokenizer.java | 132 +++++++ .../java/io/BufferedInputStreamTest.java | 338 ++++++++++++++++++ .../java/util/StringTokenizerTest.java | 194 ++++++++++ 5 files changed, 989 insertions(+) create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/io/TBufferedInputStream.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/io/TFilterInputStream.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/util/TStringTokenizer.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/io/BufferedInputStreamTest.java create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/util/StringTokenizerTest.java diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/io/TBufferedInputStream.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/io/TBufferedInputStream.java new file mode 100644 index 000000000..eac3fa937 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/io/TBufferedInputStream.java @@ -0,0 +1,255 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.io; + +import org.teavm.classlib.java.lang.TString; + +public class TBufferedInputStream extends TFilterInputStream { + protected volatile byte[] buf; + protected int count; + protected int marklimit; + protected int markpos = -1; + protected int pos; + + public TBufferedInputStream(TInputStream in) { + super(in); + buf = new byte[8192]; + } + + public TBufferedInputStream(TInputStream in, int size) { + super(in); + if (size <= 0) { + throw new IllegalArgumentException("Size " + size + " is negative"); + } + buf = new byte[size]; + } + + @Override + public synchronized int available() throws TIOException { + TInputStream localIn = in; + if (buf == null || localIn == null) { + throw new TIOException(TString.wrap("Stream is closed")); + } + return count - pos + localIn.available(); + } + + @Override + public void close() throws TIOException { + buf = null; + TInputStream localIn = in; + in = null; + if (localIn != null) { + localIn.close(); + } + } + + private int fillbuf(TInputStream localIn, byte[] localBuf) throws TIOException { + if (markpos == -1 || (pos - markpos >= marklimit)) { + /* Mark position not set or exceeded readlimit */ + int result = localIn.read(localBuf); + if (result > 0) { + markpos = -1; + pos = 0; + count = result == -1 ? 0 : result; + } + return result; + } + if (markpos == 0 && marklimit > localBuf.length) { + /* Increase buffer size to accommodate the readlimit */ + int newLength = localBuf.length * 2; + if (newLength > marklimit) { + newLength = marklimit; + } + byte[] newbuf = new byte[newLength]; + System.arraycopy(localBuf, 0, newbuf, 0, localBuf.length); + // Reassign buf, which will invalidate any local references + // FIXME: what if buf was null? + localBuf = buf = newbuf; + } else if (markpos > 0) { + System.arraycopy(localBuf, markpos, localBuf, 0, localBuf.length - markpos); + } + /* Set the new position and mark position */ + pos -= markpos; + count = markpos = 0; + int bytesread = localIn.read(localBuf, pos, localBuf.length - pos); + count = bytesread <= 0 ? pos : pos + bytesread; + return bytesread; + } + + @Override + public synchronized void mark(int readlimit) { + marklimit = readlimit; + markpos = pos; + } + + @Override + public boolean markSupported() { + return true; + } + + @Override + public synchronized int read() throws TIOException { + // Use local refs since buf and in may be invalidated by an + // unsynchronized close() + byte[] localBuf = buf; + TInputStream localIn = in; + if (localBuf == null || localIn == null) { + throw new TIOException(TString.wrap("Stream is closed")); + } + + /* Are there buffered bytes available? */ + if (pos >= count && fillbuf(localIn, localBuf) == -1) { + return -1; /* no, fill buffer */ + } + // localBuf may have been invalidated by fillbuf + if (localBuf != buf) { + localBuf = buf; + if (localBuf == null) { + throw new TIOException(TString.wrap("Stream is closed")); + } + } + + /* Did filling the buffer fail with -1 (EOF)? */ + if (count - pos > 0) { + return localBuf[pos++] & 0xFF; + } + return -1; + } + + @Override + public synchronized int read(byte[] buffer, int offset, int length) throws TIOException { + byte[] localBuf = buf; + if (localBuf == null) { + throw new TIOException(TString.wrap("Stream is closed")); + } + // avoid int overflow + if (offset > buffer.length - length || offset < 0 || length < 0) { + throw new IndexOutOfBoundsException(); + } + if (length == 0) { + return 0; + } + TInputStream localIn = in; + if (localIn == null) { + throw new TIOException(TString.wrap("Stream is closed")); + } + + int required; + if (pos < count) { + /* There are bytes available in the buffer. */ + int copylength = count - pos >= length ? length : count - pos; + System.arraycopy(localBuf, pos, buffer, offset, copylength); + pos += copylength; + if (copylength == length || localIn.available() == 0) { + return copylength; + } + offset += copylength; + required = length - copylength; + } else { + required = length; + } + + while (true) { + int read; + /* + * If we're not marked and the required size is greater than the + * buffer, simply read the bytes directly bypassing the buffer. + */ + if (markpos == -1 && required >= localBuf.length) { + read = localIn.read(buffer, offset, required); + if (read == -1) { + return required == length ? -1 : length - required; + } + } else { + if (fillbuf(localIn, localBuf) == -1) { + return required == length ? -1 : length - required; + } + // localBuf may have been invalidated by fillbuf + if (localBuf != buf) { + localBuf = buf; + if (localBuf == null) { + throw new TIOException(TString.wrap("Stream is closed")); + } + } + + read = count - pos >= required ? required : count - pos; + System.arraycopy(localBuf, pos, buffer, offset, read); + pos += read; + } + required -= read; + if (required == 0) { + return length; + } + if (localIn.available() == 0) { + return length - required; + } + offset += read; + } + } + + @Override + public synchronized void reset() throws TIOException { + if (buf == null) { + throw new TIOException(TString.wrap("Stream is closed")); + } + if (-1 == markpos) { + throw new TIOException(TString.wrap("Mark has been invalidated.")); + } + pos = markpos; + } + + @Override + public synchronized long skip(long amount) throws TIOException { + // Use local refs since buf and in may be invalidated by an + // unsynchronized close() + byte[] localBuf = buf; + TInputStream localIn = in; + if (localBuf == null) { + throw new TIOException(TString.wrap("Stream is closed")); + } + if (amount < 1) { + return 0; + } + if (localIn == null) { + throw new TIOException(TString.wrap("Stream is closed")); + } + + if (count - pos >= amount) { + pos += amount; + return amount; + } + long read = count - pos; + pos = count; + + if (markpos != -1) { + if (amount <= marklimit) { + if (fillbuf(localIn, localBuf) == -1) { + return read; + } + if (count - pos >= amount - read) { + pos += amount - read; + return amount; + } + // Couldn't get all the bytes, skip what we read + read += (count - pos); + pos = count; + return read; + } + } + return read + localIn.skip(amount - read); + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/io/TFilterInputStream.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/io/TFilterInputStream.java new file mode 100644 index 000000000..a391efde5 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/io/TFilterInputStream.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.io; + +public class TFilterInputStream extends TInputStream { + protected volatile TInputStream in; + + protected TFilterInputStream(TInputStream in) { + this.in = in; + } + + @Override + public int available() throws TIOException { + return in.available(); + } + + @Override + public void close() throws TIOException { + in.close(); + } + + @Override + public synchronized void mark(int readlimit) { + in.mark(readlimit); + } + + @Override + public boolean markSupported() { + return in.markSupported(); + } + + @Override + public int read() throws TIOException { + return in.read(); + } + + @Override + public int read(byte[] buffer) throws TIOException { + return read(buffer, 0, buffer.length); + } + + @Override + public int read(byte[] buffer, int offset, int count) throws TIOException { + return in.read(buffer, offset, count); + } + + @Override + public synchronized void reset() throws TIOException { + in.reset(); + } + + @Override + public long skip(long count) throws TIOException { + return in.skip(count); + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TStringTokenizer.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TStringTokenizer.java new file mode 100644 index 000000000..dc1e35f50 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TStringTokenizer.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.teavm.classlib.java.util; + +public class TStringTokenizer implements TEnumeration { + private String string; + private String delimiters; + private boolean returnDelimiters; + private int position; + + public TStringTokenizer(String string) { + this(string, " \t\n\r\f", false); + } + + public TStringTokenizer(String string, String delimiters) { + this(string, delimiters, false); + } + + public TStringTokenizer(String string, String delimiters, boolean returnDelimiters) { + if (string != null) { + this.string = string; + this.delimiters = delimiters; + this.returnDelimiters = returnDelimiters; + this.position = 0; + } else + throw new NullPointerException(); + } + + public int countTokens() { + int count = 0; + boolean inToken = false; + for (int i = position, length = string.length(); i < length; i++) { + if (delimiters.indexOf(string.charAt(i), 0) >= 0) { + if (returnDelimiters) + count++; + if (inToken) { + count++; + inToken = false; + } + } else { + inToken = true; + } + } + if (inToken) + count++; + return count; + } + + @Override + public boolean hasMoreElements() { + return hasMoreTokens(); + } + + public boolean hasMoreTokens() { + if (delimiters == null) { + throw new NullPointerException(); + } + int length = string.length(); + if (position < length) { + if (returnDelimiters) + return true; // there is at least one character and even if + // it is a delimiter it is a token + + // otherwise find a character which is not a delimiter + for (int i = position; i < length; i++) + if (delimiters.indexOf(string.charAt(i), 0) == -1) + return true; + } + return false; + } + + @Override + public Object nextElement() { + return nextToken(); + } + + public String nextToken() { + if (delimiters == null) { + throw new NullPointerException(); + } + int i = position; + int length = string.length(); + + if (i < length) { + if (returnDelimiters) { + if (delimiters.indexOf(string.charAt(position), 0) >= 0) { + return String.valueOf(string.charAt(position++)); + } + for (position++; position < length; position++) { + if (delimiters.indexOf(string.charAt(position), 0) >= 0) { + return string.substring(i, position); + } + } + return string.substring(i); + } + + while (i < length && delimiters.indexOf(string.charAt(i), 0) >= 0) { + i++; + } + position = i; + if (i < length) { + for (position++; position < length; position++) { + if (delimiters.indexOf(string.charAt(position), 0) >= 0) { + return string.substring(i, position); + } + } + return string.substring(i); + } + } + throw new TNoSuchElementException(); + } + + public String nextToken(String delims) { + this.delimiters = delims; + return nextToken(); + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/io/BufferedInputStreamTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/io/BufferedInputStreamTest.java new file mode 100644 index 000000000..d6cd76043 --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/io/BufferedInputStreamTest.java @@ -0,0 +1,338 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.io; + +import static org.junit.Assert.*; +import java.io.*; +import org.junit.Test; + +@SuppressWarnings("resource") +public class BufferedInputStreamTest { + byte[] ibuf = new byte[4096]; + + @Test + public void test_ConstructorLjava_io_InputStream() { + try { + BufferedInputStream str = new BufferedInputStream(null); + str.read(); + fail("Expected an IOException"); + } catch (IOException e) { + // Expected + } + } + + static class MockBufferedInputStream extends BufferedInputStream { + static byte[] buf; + + MockBufferedInputStream(InputStream is) { + super(is); + buf = super.buf; + } + + MockBufferedInputStream(InputStream is, int size) { + super(is, size); + buf = super.buf; + } + } + + @Test + public void test_available() throws IOException { + ByteArrayInputStream isFile = new ByteArrayInputStream(new byte[] { 2, 3, 5, 7, 11 }); + BufferedInputStream is = new BufferedInputStream(isFile); + assertTrue("Returned incorrect number of available bytes", is.available() == 5); + + // Test that a closed stream throws an IOE for available() + BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream(new byte[] { 'h', 'e', 'l', 'l', + 'o', ' ', 't', 'i', 'm' })); + int available = bis.available(); + bis.close(); + assertTrue(available != 0); + + try { + bis.available(); + fail("Expected test to throw IOE."); + } catch (IOException ex) { + // expected + } + } + + @Test + public void test_close() throws IOException { + new BufferedInputStream(new ByteArrayInputStream(new byte[100])).close(); + + // regression for HARMONY-667 + BufferedInputStream buf = new BufferedInputStream(null, 5); + buf.close(); + } + + @Test + public void test_markI() throws IOException { + ByteArrayInputStream isFile = new ByteArrayInputStream(new byte[10000]); + BufferedInputStream is = new BufferedInputStream(isFile, 5); + byte[] buf1 = new byte[100]; + byte[] buf2 = new byte[100]; + is.skip(3000); + is.mark(1000); + is.read(buf1, 0, buf1.length); + is.reset(); + is.read(buf2, 0, buf2.length); + is.reset(); + assertTrue("Failed to mark correct position", + new String(buf1, 0, buf1.length).equals(new String(buf2, 0, buf2.length))); + + byte[] bytes = new byte[256]; + for (int i = 0; i < 256; i++) { + bytes[i] = (byte)i; + } + InputStream in = new BufferedInputStream(new ByteArrayInputStream(bytes), 12); + in.skip(6); + in.mark(14); + in.read(new byte[14], 0, 14); + in.reset(); + assertTrue("Wrong bytes", in.read() == 6 && in.read() == 7); + + in = new BufferedInputStream(new ByteArrayInputStream(bytes), 12); + in.skip(6); + in.mark(8); + in.skip(7); + in.reset(); + assertTrue("Wrong bytes 2", in.read() == 6 && in.read() == 7); + + BufferedInputStream buf = new BufferedInputStream(new ByteArrayInputStream(new byte[] { 0, 1, 2, 3, 4 }), 2); + buf.mark(3); + bytes = new byte[3]; + int result = buf.read(bytes); + assertEquals(3, result); + assertEquals("Assert 0:", 0, bytes[0]); + assertEquals("Assert 1:", 1, bytes[1]); + assertEquals("Assert 2:", 2, bytes[2]); + assertEquals("Assert 3:", 3, buf.read()); + + buf = new BufferedInputStream(new ByteArrayInputStream(new byte[] { 0, 1, 2, 3, 4 }), 2); + buf.mark(3); + bytes = new byte[4]; + result = buf.read(bytes); + assertEquals(4, result); + assertEquals("Assert 4:", 0, bytes[0]); + assertEquals("Assert 5:", 1, bytes[1]); + assertEquals("Assert 6:", 2, bytes[2]); + assertEquals("Assert 7:", 3, bytes[3]); + assertEquals("Assert 8:", 4, buf.read()); + assertEquals("Assert 9:", -1, buf.read()); + + buf = new BufferedInputStream(new ByteArrayInputStream(new byte[] { 0, 1, 2, 3, 4 }), 2); + buf.mark(Integer.MAX_VALUE); + buf.read(); + buf.close(); + } + + @Test + public void test_read() throws IOException { + ByteArrayInputStream isFile = new ByteArrayInputStream(new byte[10000]); + BufferedInputStream is = new BufferedInputStream(isFile, 5); + InputStreamReader isr = new InputStreamReader(is); + int c = isr.read(); + assertTrue("read returned incorrect char", c == 0); + + byte[] bytes = new byte[256]; + for (int i = 0; i < 256; i++) { + bytes[i] = (byte)i; + } + InputStream in = new BufferedInputStream(new ByteArrayInputStream(bytes), 12); + assertEquals("Wrong initial byte", 0, in.read()); // Fill the + // buffer + byte[] buf = new byte[14]; + in.read(buf, 0, 14); // Read greater than the buffer + assertTrue("Wrong block read data", new String(buf, 0, 14).equals(new String(bytes, 1, 14))); + assertEquals("Wrong bytes", 15, in.read()); // Check next byte + } + + @Test + public void test_read$BII_Exception() throws IOException { + BufferedInputStream bis = new BufferedInputStream(null); + + try { + bis.read(new byte[0], -1, -1); + fail("should throw IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + bis.read(new byte[0], 1, -1); + fail("should throw IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + bis.read(new byte[0], 1, 1); + fail("should throw IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + bis.close(); + + try { + bis.read(null, -1, -1); + fail("should throw IOException"); + } catch (IOException e) { + // expected + } + } + + @Test + public void test_read$BII() throws IOException { + ByteArrayInputStream isFile = new ByteArrayInputStream(new byte[10000]); + BufferedInputStream is = new BufferedInputStream(isFile); + byte[] buf1 = new byte[100]; + is.skip(3000); + is.mark(1000); + is.read(buf1, 0, buf1.length); + + BufferedInputStream bufin = new BufferedInputStream(new InputStream() { + int size = 2, pos = 0; + + byte[] contents = new byte[size]; + + @Override + public int read() throws IOException { + if (pos >= size) { + throw new IOException("Read past end of data"); + } + return contents[pos++]; + } + + @Override + public int read(byte[] buf, int off, int len) throws IOException { + if (pos >= size) { + throw new IOException("Read past end of data"); + } + int toRead = len; + if (toRead > available()) { + toRead = available(); + } + System.arraycopy(contents, pos, buf, off, toRead); + pos += toRead; + return toRead; + } + + @Override + public int available() { + return size - pos; + } + }); + bufin.read(); + int result = bufin.read(new byte[2], 0, 2); + assertTrue("Incorrect result: " + result, result == 1); + } + + @Test + public void test_reset() throws IOException { + ByteArrayInputStream isFile = new ByteArrayInputStream(new byte[10000]); + BufferedInputStream is = new BufferedInputStream(isFile); + byte[] buf1 = new byte[10]; + byte[] buf2 = new byte[10]; + is.mark(2000); + is.read(buf1, 0, 10); + is.reset(); + is.read(buf2, 0, 10); + is.reset(); + assertTrue("Reset failed", new String(buf1, 0, buf1.length).equals(new String(buf2, 0, buf2.length))); + + BufferedInputStream bIn = new BufferedInputStream(new ByteArrayInputStream("1234567890".getBytes())); + bIn.mark(10); + for (int i = 0; i < 11; i++) { + bIn.read(); + } + bIn.reset(); + } + + @Test + public void test_reset_Exception() throws IOException { + BufferedInputStream bis = new BufferedInputStream(null); + + // throws IOException with message "Mark has been invalidated" + try { + bis.reset(); + fail("should throw IOException"); + } catch (IOException e) { + // expected + } + + // does not throw IOException + bis.mark(1); + bis.reset(); + + bis.close(); + + // throws IOException with message "stream is closed" + try { + bis.reset(); + fail("should throw IOException"); + } catch (IOException e) { + // expected + } + } + + @Test + public void test_reset_scenario1() throws IOException { + byte[] input = "12345678900".getBytes(); + BufferedInputStream buffis = new BufferedInputStream(new ByteArrayInputStream(input)); + buffis.read(); + buffis.mark(5); + buffis.skip(5); + buffis.reset(); + } + + @Test + public void test_reset_scenario2() throws IOException { + byte[] input = "12345678900".getBytes(); + BufferedInputStream buffis = new BufferedInputStream(new ByteArrayInputStream(input)); + buffis.mark(5); + buffis.skip(6); + buffis.reset(); + } + + @Test + public void test_skipJ() throws IOException { + ByteArrayInputStream isFile = new ByteArrayInputStream(new byte[10000]); + BufferedInputStream is = new BufferedInputStream(isFile); + + byte[] buf1 = new byte[10]; + is.mark(2000); + is.skip(1000); + is.read(buf1, 0, buf1.length); + is.reset(); + + // regression for HARMONY-667 + try { + BufferedInputStream buf = new BufferedInputStream(null, 5); + buf.skip(10); + fail("Should throw IOException"); + } catch (IOException e) { + // Expected + } + } + + @Test + public void test_skip_NullInputStream() throws IOException { + BufferedInputStream buf = new BufferedInputStream(null, 5); + assertEquals(0, buf.skip(0)); + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/StringTokenizerTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/util/StringTokenizerTest.java new file mode 100644 index 000000000..46ad0a6d4 --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/util/StringTokenizerTest.java @@ -0,0 +1,194 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.teavm.classlib.java.util; + +import static org.junit.Assert.*; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; +import org.junit.Test; + +public class StringTokenizerTest { + @Test + public void test_ConstructorLjava_lang_StringLjava_lang_String() { + StringTokenizer st = new StringTokenizer("This:is:a:test:String", ":"); + assertTrue("Created incorrect tokenizer", st.countTokens() == 5 && (st.nextElement().equals("This"))); + } + + @Test + public void test_ConstructorLjava_lang_StringLjava_lang_StringZ() { + StringTokenizer st = new StringTokenizer("This:is:a:test:String", ":", true); + st.nextElement(); + assertTrue("Created incorrect tokenizer", st.countTokens() == 8 && (st.nextElement().equals(":"))); + } + + @Test + public void test_countTokens() { + StringTokenizer st = new StringTokenizer("This is a test String"); + assertEquals("Incorrect token count returned", 5, st.countTokens()); + } + + @Test + public void test_hasMoreElements() { + StringTokenizer st = new StringTokenizer("This is a test String"); + st.nextElement(); + assertTrue("hasMoreElements returned incorrect value", st.hasMoreElements()); + st.nextElement(); + st.nextElement(); + st.nextElement(); + st.nextElement(); + assertTrue("hasMoreElements returned incorrect value", !st.hasMoreElements()); + } + + @Test + public void test_hasMoreTokens() { + StringTokenizer st = new StringTokenizer("This is a test String"); + for (int counter = 0; counter < 5; counter++) { + assertTrue("StringTokenizer incorrectly reports it has no more tokens", st.hasMoreTokens()); + st.nextToken(); + } + assertTrue("StringTokenizer incorrectly reports it has more tokens", !st.hasMoreTokens()); + } + + @Test + public void test_nextElement() { + StringTokenizer st = new StringTokenizer("This is a test String"); + assertEquals("nextElement returned incorrect value", "This", st.nextElement()); + assertEquals("nextElement returned incorrect value", "is", st.nextElement()); + assertEquals("nextElement returned incorrect value", "a", st.nextElement()); + assertEquals("nextElement returned incorrect value", "test", st.nextElement()); + assertEquals("nextElement returned incorrect value", "String", st.nextElement()); + try { + st.nextElement(); + fail("nextElement failed to throw a NoSuchElementException when it should have been out of elements"); + } catch (NoSuchElementException e) { + return; + } + } + + @Test + public void test_nextToken() { + StringTokenizer st = new StringTokenizer("This is a test String"); + assertEquals("nextToken returned incorrect value", "This", st.nextToken()); + assertEquals("nextToken returned incorrect value", "is", st.nextToken()); + assertEquals("nextToken returned incorrect value", "a", st.nextToken()); + assertEquals("nextToken returned incorrect value", "test", st.nextToken()); + assertEquals("nextToken returned incorrect value", "String", st.nextToken()); + try { + st.nextToken(); + fail("nextToken failed to throw a NoSuchElementException when it should have been out of elements"); + } catch (NoSuchElementException e) { + return; + } + } + + @Test + public void test_nextTokenLjava_lang_String() { + StringTokenizer st = new StringTokenizer("This is a test String"); + assertEquals("nextToken(String) returned incorrect value with normal token String", "This", st.nextToken(" ")); + assertEquals("nextToken(String) returned incorrect value with custom token String", " is a ", + st.nextToken("tr")); + assertEquals("calling nextToken() did not use the new default delimiter list", "es", st.nextToken()); + } + + @Test + public void test_hasMoreElements_NPE() { + StringTokenizer stringTokenizer = new StringTokenizer(new String(), (String)null, true); + try { + stringTokenizer.hasMoreElements(); + fail("should throw NullPointerException"); + } catch (NullPointerException e) { + // Expected + } + + stringTokenizer = new StringTokenizer(new String(), (String)null); + try { + stringTokenizer.hasMoreElements(); + fail("should throw NullPointerException"); + } catch (NullPointerException e) { + // Expected + } + } + + @Test + public void test_hasMoreTokens_NPE() { + StringTokenizer stringTokenizer = new StringTokenizer(new String(), (String)null, true); + try { + stringTokenizer.hasMoreTokens(); + fail("should throw NullPointerException"); + } catch (NullPointerException e) { + // Expected + } + + stringTokenizer = new StringTokenizer(new String(), (String)null); + try { + stringTokenizer.hasMoreTokens(); + fail("should throw NullPointerException"); + } catch (NullPointerException e) { + // Expected + } + } + + @Test + public void test_nextElement_NPE() { + StringTokenizer stringTokenizer = new StringTokenizer(new String(), (String)null, true); + try { + stringTokenizer.nextElement(); + fail("should throw NullPointerException"); + } catch (NullPointerException e) { + // Expected + } + + stringTokenizer = new StringTokenizer(new String(), (String)null); + try { + stringTokenizer.nextElement(); + fail("should throw NullPointerException"); + } catch (NullPointerException e) { + // Expected + } + } + + @Test + public void test_nextToken_NPE() { + StringTokenizer stringTokenizer = new StringTokenizer(new String(), (String)null, true); + try { + stringTokenizer.nextToken(); + fail("should throw NullPointerException"); + } catch (NullPointerException e) { + // Expected + } + + stringTokenizer = new StringTokenizer(new String(), (String)null); + try { + stringTokenizer.nextToken(); + fail("should throw NullPointerException"); + } catch (NullPointerException e) { + // Expected + } + } + + @Test + public void test_nextTokenLjava_lang_String_NPE() { + StringTokenizer stringTokenizer = new StringTokenizer(new String()); + try { + stringTokenizer.nextToken(null); + fail("should throw NullPointerException"); + } catch (NullPointerException e) { + // Expected + } + } +}