diff --git a/build_tools/doctool/src/com/google/doctool/custom/JavaEmulSummaryDoclet.java b/build_tools/doctool/src/com/google/doctool/custom/JavaEmulSummaryDoclet.java index 46d55574db..baab41da9e 100644 --- a/build_tools/doctool/src/com/google/doctool/custom/JavaEmulSummaryDoclet.java +++ b/build_tools/doctool/src/com/google/doctool/custom/JavaEmulSummaryDoclet.java @@ -117,13 +117,13 @@ public void close() { printWriter.close(); } - public void formatLine(String template, Object... args) { + void formatLine(String template, Object... args) { maySkip = template.startsWith(" "); printWriter.format(template, args); printWriter.println(); } - public void skipLine() { + void skipLine() { if (maySkip) { printWriter.println(); } diff --git a/build_tools/doctool/src/com/google/doctool/custom/missing/gh10221.properties b/build_tools/doctool/src/com/google/doctool/custom/missing/gh10221.properties deleted file mode 100644 index cb5929bdb7..0000000000 --- a/build_tools/doctool/src/com/google/doctool/custom/missing/gh10221.properties +++ /dev/null @@ -1,27 +0,0 @@ -title=Miscellaneous Java 17 omissions -members=java.util.Objects#checkIndex(long, long)\ -java.util.Objects#checkFromIndexSize(long, long, long)\ -java.util.Objects#checkFromToIndex(long, long, long)\ -java.lang.Integer#parseInt(CharSequence, int, int, int)\ -java.util.Collections#emptyEnumeration()\ -java.util.Collections#indexOfSubList(List, List)\ -java.util.Collections#lastIndexOfSubList(List, List)\ -java.util.Collections#unmodifiableNavigableSet(NavigableSet)\ -java.util.Collections#unmodifiableNavigableMap(NavigableMap)\ -java.util.Collections#emptySortedSet()\ -java.util.Collections#emptyNavigableSet()\ -java.util.Collections#emptySortedMap()\ -java.util.Collections#emptyNavigableMap()\ -java.io.InputStream#readAllBytes()\ -java.io.InputStream#readNBytes(int)\ -java.io.InputStream#readNBytes(byte[], int, int)\ -java.io.InputStream#transferTo(OutputStream)\ -java.io.InputStream#nullInputStream()\ -java.io.InputStream#skipNBytes(long)\ -java.io.PrintStream#writeBytes(byte[])\ -java.io.Reader#nullReader()\ -java.io.Reader#transferTo(Writer)\ -java.security.MessageDigestSpi#clone()\ -java.lang.Double#toHexString(double)\ -java.lang.Float#toHexString(float)\ -java.lang.System#lineSeparator() diff --git a/user/super/com/google/gwt/emul/java/io/InputStream.java b/user/super/com/google/gwt/emul/java/io/InputStream.java index 5d25c9e388..149c2ccbc8 100644 --- a/user/super/com/google/gwt/emul/java/io/InputStream.java +++ b/user/super/com/google/gwt/emul/java/io/InputStream.java @@ -21,6 +21,8 @@ import static javaemul.internal.InternalPreconditions.checkNotNull; +import java.util.Arrays; + /** * A readable source of bytes. * @@ -54,206 +56,296 @@ */ public abstract class InputStream extends Object implements Closeable { - /** - * Size of the temporary buffer used when skipping bytes with {@link skip(long)}. - */ - private static final int MAX_SKIP_BUFFER_SIZE = 4096; + /** + * Size of the temporary buffer used when skipping bytes with {@link skip(long)}. + */ + private static final int MAX_SKIP_BUFFER_SIZE = 4096; - /** - * This constructor does nothing. It is provided for signature - * compatibility. - */ - public InputStream() { - /* empty */ - } + /** + * This constructor does nothing. It is provided for signature + * compatibility. + */ + public InputStream() { + /* empty */ + } - /** - * Returns an estimated number of bytes that can be read or skipped without blocking for more - * input. - * - *

Note that this method provides such a weak guarantee that it is not very useful in - * practice. - * - *

Firstly, the guarantee is "without blocking for more input" rather than "without - * blocking": a read may still block waiting for I/O to complete — the guarantee is - * merely that it won't have to wait indefinitely for data to be written. The result of this - * method should not be used as a license to do I/O on a thread that shouldn't be blocked. - * - *

Secondly, the result is a - * conservative estimate and may be significantly smaller than the actual number of bytes - * available. In particular, an implementation that always returns 0 would be correct. - * In general, callers should only use this method if they'd be satisfied with - * treating the result as a boolean yes or no answer to the question "is there definitely - * data ready?". - * - *

Thirdly, the fact that a given number of bytes is "available" does not guarantee that a - * read or skip will actually read or skip that many bytes: they may read or skip fewer. - * - *

It is particularly important to realize that you must not use this method to - * size a container and assume that you can read the entirety of the stream without needing - * to resize the container. Such callers should probably write everything they read to a - * {@link ByteArrayOutputStream} and convert that to a byte array. Alternatively, if you're - * reading from a file, {@link File#length} returns the current length of the file (though - * assuming the file's length can't change may be incorrect, reading a file is inherently - * racy). - * - *

The default implementation of this method in {@code InputStream} always returns 0. - * Subclasses should override this method if they are able to indicate the number of bytes - * available. - * - * @return the estimated number of bytes available - * @throws IOException if this stream is closed or an error occurs - */ - public int available() throws IOException { - return 0; - } + /** + * Returns an estimated number of bytes that can be read or skipped without blocking for more + * input. + * + *

Note that this method provides such a weak guarantee that it is not very useful in + * practice. + * + *

Firstly, the guarantee is "without blocking for more input" rather than "without + * blocking": a read may still block waiting for I/O to complete — the guarantee is + * merely that it won't have to wait indefinitely for data to be written. The result of this + * method should not be used as a license to do I/O on a thread that shouldn't be blocked. + * + *

Secondly, the result is a + * conservative estimate and may be significantly smaller than the actual number of bytes + * available. In particular, an implementation that always returns 0 would be correct. + * In general, callers should only use this method if they'd be satisfied with + * treating the result as a boolean yes or no answer to the question "is there definitely + * data ready?". + * + *

Thirdly, the fact that a given number of bytes is "available" does not guarantee that a + * read or skip will actually read or skip that many bytes: they may read or skip fewer. + * + *

It is particularly important to realize that you must not use this method to + * size a container and assume that you can read the entirety of the stream without needing + * to resize the container. Such callers should probably write everything they read to a + * {@link ByteArrayOutputStream} and convert that to a byte array. Alternatively, if you're + * reading from a file, {@link File#length} returns the current length of the file (though + * assuming the file's length can't change may be incorrect, reading a file is inherently + * racy). + * + *

The default implementation of this method in {@code InputStream} always returns 0. + * Subclasses should override this method if they are able to indicate the number of bytes + * available. + * + * @return the estimated number of bytes available + * @throws IOException if this stream is closed or an error occurs + */ + public int available() throws IOException { + return 0; + } - /** - * Closes this stream. Concrete implementations of this class should free - * any resources during close. This implementation does nothing. - * - * @throws IOException - * if an error occurs while closing this stream. - */ - @Override - public void close() throws IOException { - /* empty */ - } + /** + * Closes this stream. Concrete implementations of this class should free + * any resources during close. This implementation does nothing. + * + * @throws IOException + * if an error occurs while closing this stream. + */ + @Override + public void close() throws IOException { + /* empty */ + } + + /** + * Sets a mark position in this InputStream. The parameter {@code readlimit} + * indicates how many bytes can be read before the mark is invalidated. + * Sending {@code reset()} will reposition the stream back to the marked + * position provided {@code readLimit} has not been surpassed. + *

+ * This default implementation does nothing and concrete subclasses must + * provide their own implementation. + * + * @param readlimit + * the number of bytes that can be read from this stream before + * the mark is invalidated. + * @see #markSupported() + * @see #reset() + */ + public void mark(int readlimit) { + /* empty */ + } + + /** + * Indicates whether this stream supports the {@code mark()} and + * {@code reset()} methods. The default implementation returns {@code false}. + * + * @return always {@code false}. + * @see #mark(int) + * @see #reset() + */ + public boolean markSupported() { + return false; + } - /** - * Sets a mark position in this InputStream. The parameter {@code readlimit} - * indicates how many bytes can be read before the mark is invalidated. - * Sending {@code reset()} will reposition the stream back to the marked - * position provided {@code readLimit} has not been surpassed. - *

- * This default implementation does nothing and concrete subclasses must - * provide their own implementation. - * - * @param readlimit - * the number of bytes that can be read from this stream before - * the mark is invalidated. - * @see #markSupported() - * @see #reset() - */ - public void mark(int readlimit) { - /* empty */ + /** + * Reads a single byte from this stream and returns it as an integer in the + * range from 0 to 255. Returns -1 if the end of the stream has been + * reached. Blocks until one byte has been read, the end of the source + * stream is detected or an exception is thrown. + * + * @throws IOException + * if the stream is closed or another IOException occurs. + */ + public abstract int read() throws IOException; + + /** + * Equivalent to {@code read(buffer, 0, buffer.length)}. + */ + public int read(byte[] buffer) throws IOException { + // Note that GWT will throw a JavascriptException rather than a NullPointerException if we + // skip this check and the buffer array is null. This way we ensure that this implementation + // behaves in the same way as the classes that are emulated. + checkNotNull(buffer); + return read(buffer, 0, buffer.length); + } + + /** + * Reads up to {@code byteCount} bytes from this stream and stores them in + * the byte array {@code buffer} starting at {@code byteOffset}. + * Returns the number of bytes actually read or -1 if the end of the stream + * has been reached. + * + * @throws IndexOutOfBoundsException + * if {@code byteOffset < 0 || byteCount < 0 || byteOffset + byteCount > buffer.length}. + * @throws IOException + * if the stream is closed or another IOException occurs. + */ + public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException { + IOUtils.checkOffsetAndCount(buffer, byteOffset, byteCount); + for (int i = 0; i < byteCount; ++i) { + int c; + try { + if ((c = read()) == -1) { + return i == 0 ? -1 : i; + } + } catch (IOException e) { + if (i != 0) { + return i; + } + throw e; + } + buffer[byteOffset + i] = (byte) c; } + return byteCount; + } + + public byte[] readAllBytes() throws IOException { + return readNBytes(0, Integer.MAX_VALUE); + } + + public byte[] readNBytes(int len) throws IOException { + return readNBytes(0, len); + } - /** - * Indicates whether this stream supports the {@code mark()} and - * {@code reset()} methods. The default implementation returns {@code false}. - * - * @return always {@code false}. - * @see #mark(int) - * @see #reset() - */ - public boolean markSupported() { - return false; + public byte[] readNBytes(int off, int len) throws IOException { + if (len == 0) { + return new byte[0]; + } + skipNBytes(off); + int capacity = Math.min(len, Math.max(2048, available())); + byte[] buffer = new byte[capacity]; + int pos = 0; + while (pos <= len) { + int chunk = read(buffer, pos, capacity - pos); + if (chunk == 0) { + break; + } + pos += chunk; + if (pos == capacity) { + capacity = Math.min(len, 2 * capacity); + buffer = Arrays.copyOf(buffer, capacity); + } } - /** - * Reads a single byte from this stream and returns it as an integer in the - * range from 0 to 255. Returns -1 if the end of the stream has been - * reached. Blocks until one byte has been read, the end of the source - * stream is detected or an exception is thrown. - * - * @throws IOException - * if the stream is closed or another IOException occurs. - */ - public abstract int read() throws IOException; + return pos < len ? Arrays.copyOf(buffer, pos) : buffer; + } - /** - * Equivalent to {@code read(buffer, 0, buffer.length)}. - */ - public int read(byte[] buffer) throws IOException { - // Note that GWT will throw a JavascriptException rather than a NullPointerException if we - // skip this check and the buffer array is null. This way we ensure that this implementation - // behaves in the same way as the classes that are emulated. - checkNotNull(buffer); - return read(buffer, 0, buffer.length); + public int readNBytes(byte[] buffer, int off, int len) throws IOException { + if (len == 0) { + return 0; + } + skipNBytes(off); + int pos = 0; + while (pos <= len) { + int chunk = read(buffer, pos, len); + if (chunk == 0) { + break; + } + pos += chunk; } - /** - * Reads up to {@code byteCount} bytes from this stream and stores them in - * the byte array {@code buffer} starting at {@code byteOffset}. - * Returns the number of bytes actually read or -1 if the end of the stream - * has been reached. - * - * @throws IndexOutOfBoundsException - * if {@code byteOffset < 0 || byteCount < 0 || byteOffset + byteCount > buffer.length}. - * @throws IOException - * if the stream is closed or another IOException occurs. - */ - public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException { - IOUtils.checkOffsetAndCount(buffer, byteOffset, byteCount); - for (int i = 0; i < byteCount; ++i) { - int c; - try { - if ((c = read()) == -1) { - return i == 0 ? -1 : i; - } - } catch (IOException e) { - if (i != 0) { - return i; - } - throw e; - } - buffer[byteOffset + i] = (byte) c; + return pos; + } + + public void skipNBytes(long n) throws IOException { + long skipped = 0; + while (skipped < n) { + long chunk = skip(n); + if (chunk == 0) { + if (read() >= 0) { + skipped++; + } else { + throw new IOException("End of stream reached"); } - return byteCount; + } + skipped += chunk; } + } - /** - * Resets this stream to the last marked location. Throws an - * {@code IOException} if the number of bytes read since the mark has been - * set is greater than the limit provided to {@code mark}, or if no mark - * has been set. - *

- * This implementation always throws an {@code IOException} and concrete - * subclasses should provide the proper implementation. - * - * @throws IOException - * if this stream is closed or another IOException occurs. - */ - public void reset() throws IOException { - throw new IOException(); + /** + * Resets this stream to the last marked location. Throws an + * {@code IOException} if the number of bytes read since the mark has been + * set is greater than the limit provided to {@code mark}, or if no mark + * has been set. + *

+ * This implementation always throws an {@code IOException} and concrete + * subclasses should provide the proper implementation. + * + * @throws IOException + * if this stream is closed or another IOException occurs. + */ + public void reset() throws IOException { + throw new IOException(); + } + + /** + * Skips at most {@code byteCount} bytes in this stream. The number of actual + * bytes skipped may be anywhere between 0 and {@code byteCount}. If + * {@code byteCount} is negative, this method does nothing and returns 0, but + * some subclasses may throw. + * + *

Note the "at most" in the description of this method: this method may + * choose to skip fewer bytes than requested. Callers should always + * check the return value. + * + *

This default implementation reads bytes into a temporary buffer. Concrete + * subclasses should provide their own implementation. + * + * @return the number of bytes actually skipped. + * @throws IOException if this stream is closed or another IOException + * occurs. + */ + public long skip(long byteCount) throws IOException { + if (byteCount <= 0) { + return 0; + } + final int bSize = (int) Math.min(MAX_SKIP_BUFFER_SIZE, byteCount); + final byte[] b = new byte[bSize]; + long skipped = 0; + while (skipped < byteCount) { + final int toRead = (int) Math.min(byteCount - skipped, b.length); + final int readCount = read(b, 0, toRead); + if (readCount == -1) { + break; + } + skipped += readCount; + if (readCount < toRead) { + break; + } } + return skipped; + } - /** - * Skips at most {@code byteCount} bytes in this stream. The number of actual - * bytes skipped may be anywhere between 0 and {@code byteCount}. If - * {@code byteCount} is negative, this method does nothing and returns 0, but - * some subclasses may throw. - * - *

Note the "at most" in the description of this method: this method may - * choose to skip fewer bytes than requested. Callers should always - * check the return value. - * - *

This default implementation reads bytes into a temporary buffer. Concrete - * subclasses should provide their own implementation. - * - * @return the number of bytes actually skipped. - * @throws IOException if this stream is closed or another IOException - * occurs. - */ - public long skip(long byteCount) throws IOException { - if (byteCount <= 0) { - return 0; - } - final int bSize = (int) Math.min(MAX_SKIP_BUFFER_SIZE, byteCount); - final byte[] b = new byte[bSize]; - long skipped = 0; - while (skipped < byteCount) { - final int toRead = (int) Math.min(byteCount - skipped, b.length); - final int readCount = read(b, 0, toRead); - if (readCount == -1) { - break; - } - skipped += readCount; - if (readCount < toRead) { - break; - } - } - return skipped; + public void transferTo(OutputStream writer) throws IOException { + byte[] buffer = new byte[2048]; + int read; + while ((read = read(buffer)) > 0) { + writer.write(buffer, 0, read); } + } + + public static InputStream nullInputStream() { + return new InputStream() { + private boolean closed; + + @Override + public int read() throws IOException { + if (closed) { + throw new IOException("Already closed"); + } + return -1; + } + + @Override + public void close() { + closed = true; + } + }; + } } diff --git a/user/super/com/google/gwt/emul/java/io/PrintStream.java b/user/super/com/google/gwt/emul/java/io/PrintStream.java index 49dffb4ceb..2b4c1435d0 100644 --- a/user/super/com/google/gwt/emul/java/io/PrintStream.java +++ b/user/super/com/google/gwt/emul/java/io/PrintStream.java @@ -145,6 +145,10 @@ public void close() { } } + public void writeBytes(byte[] buffer) { + write(buffer, 0, buffer.length); + } + @Override public void write(byte[] buffer, int offset, int length) { // Force buffer null check first! diff --git a/user/super/com/google/gwt/emul/java/io/Reader.java b/user/super/com/google/gwt/emul/java/io/Reader.java index db1b0c6593..9d168e4b38 100644 --- a/user/super/com/google/gwt/emul/java/io/Reader.java +++ b/user/super/com/google/gwt/emul/java/io/Reader.java @@ -100,4 +100,31 @@ public long skip(long n) throws IOException { } return n - remaining; } + + public void transferTo(Writer writer) throws IOException { + char[] buffer = new char[1024]; + int read; + while ((read = read(buffer)) > 0) { + writer.write(buffer, 0, read); + } + } + + public static Reader nullReader() { + return new Reader() { + private boolean closed; + + @Override + public int read(char[] cbuf, int off, int len) throws IOException { + if (closed) { + throw new IOException("Already closed"); + } + return 0; + } + + @Override + public void close() throws IOException { + closed = true; + } + }; + } } diff --git a/user/super/com/google/gwt/emul/java/lang/Character.java b/user/super/com/google/gwt/emul/java/lang/Character.java index d675b1719e..588fe29d93 100644 --- a/user/super/com/google/gwt/emul/java/lang/Character.java +++ b/user/super/com/google/gwt/emul/java/lang/Character.java @@ -62,7 +62,7 @@ * - isUpperCase(char c) * */ -public final class Character implements Comparable, Serializable { +public final class Character implements Comparable, Constable, Serializable { /** * Helper class to share code between implementations, by making a char * array look like a CharSequence. diff --git a/user/super/com/google/gwt/emul/java/lang/Double.java b/user/super/com/google/gwt/emul/java/lang/Double.java index 828a40a4a6..5d53c22dca 100644 --- a/user/super/com/google/gwt/emul/java/lang/Double.java +++ b/user/super/com/google/gwt/emul/java/lang/Double.java @@ -23,7 +23,8 @@ /** * Wraps a primitive double as an object. */ -public final class Double extends Number implements Comparable { +public final class Double extends Number implements Comparable, + Constable, ConstantDesc { public static final double MAX_VALUE = 1.7976931348623157e+308; public static final double MIN_VALUE = 4.9e-324; public static final double MIN_NORMAL = 2.2250738585072014e-308; diff --git a/user/super/com/google/gwt/emul/java/lang/Short.java b/user/super/com/google/gwt/emul/java/lang/Short.java index 15e9b38e6d..9897b00f42 100644 --- a/user/super/com/google/gwt/emul/java/lang/Short.java +++ b/user/super/com/google/gwt/emul/java/lang/Short.java @@ -20,7 +20,7 @@ /** * Wraps a primitive short as an object. */ -public final class Short extends Number implements Comparable { +public final class Short extends Number implements Comparable, Constable { public static final short MIN_VALUE = (short) 0x8000; public static final short MAX_VALUE = (short) 0x7fff; diff --git a/user/super/com/google/gwt/emul/java/security/MessageDigest.java b/user/super/com/google/gwt/emul/java/security/MessageDigest.java index c706c2ab36..9bdf8bb819 100644 --- a/user/super/com/google/gwt/emul/java/security/MessageDigest.java +++ b/user/super/com/google/gwt/emul/java/security/MessageDigest.java @@ -17,6 +17,8 @@ import static javaemul.internal.Coercions.ensureInt; +import java.util.Arrays; + /** * Message Digest algorithm - tailMap(K fromKey) { } } + static class UnmodifiableNavigableMap extends UnmodifiableSortedMap + implements NavigableMap { + + private final NavigableMap navigableMap; + + public UnmodifiableNavigableMap(NavigableMap navigableMap) { + super(navigableMap); + this.navigableMap = navigableMap; + } + + @Override + public Entry lowerEntry(K key) { + return navigableMap.lowerEntry(key); + } + + @Override + public K lowerKey(K key) { + return navigableMap.lowerKey(key); + } + + @Override + public Entry floorEntry(K key) { + return navigableMap.floorEntry(key); + } + + @Override + public K floorKey(K key) { + return navigableMap.floorKey(key); + } + + @Override + public Entry ceilingEntry(K key) { + return navigableMap.ceilingEntry(key); + } + + @Override + public K ceilingKey(K key) { + return navigableMap.ceilingKey(key); + } + + @Override + public Entry higherEntry(K key) { + return navigableMap.higherEntry(key); + } + + @Override + public K higherKey(K key) { + return navigableMap.higherKey(key); + } + + @Override + public Entry firstEntry() { + return navigableMap.firstEntry(); + } + + @Override + public Entry lastEntry() { + return navigableMap.lastEntry(); + } + + @Override + public Entry pollFirstEntry() { + return navigableMap.pollFirstEntry(); + } + + @Override + public Entry pollLastEntry() { + return navigableMap.pollLastEntry(); + } + + @Override + public NavigableMap descendingMap() { + return new UnmodifiableNavigableMap<>(navigableMap.descendingMap()); + } + + @Override + public NavigableSet navigableKeySet() { + return new UnmodifiableNavigableSet(navigableMap.navigableKeySet()); + } + + @Override + public NavigableSet descendingKeySet() { + return null; + } + + @Override + public NavigableMap subMap(K fromKey, boolean fromInclusive, + K toKey, boolean toInclusive) { + return new UnmodifiableNavigableMap<>(navigableMap + .subMap(fromKey, fromInclusive, toKey, toInclusive)); + } + + @Override + public NavigableMap headMap(K toKey, boolean inclusive) { + return new UnmodifiableNavigableMap<>(navigableMap.headMap(toKey, inclusive)); + } + + @Override + public NavigableMap tailMap(K fromKey, boolean inclusive) { + return new UnmodifiableNavigableMap<>(navigableMap.tailMap(fromKey, inclusive)); + } + } + static class UnmodifiableSortedSet extends UnmodifiableSet implements SortedSet { private SortedSet sortedSet; @@ -854,6 +957,73 @@ public SortedSet tailSet(E fromElement) { } } + private static class UnmodifiableNavigableSet extends UnmodifiableSortedSet + implements NavigableSet { + private NavigableSet navigableSet; + + public UnmodifiableNavigableSet(NavigableSet sortedSet) { + super(sortedSet); + this.navigableSet = sortedSet; + } + + @Override + public T lower(T t) { + return navigableSet.lower(t); + } + + @Override + public T floor(T t) { + return navigableSet.floor(t); + } + + @Override + public T ceiling(T t) { + return navigableSet.ceiling(t); + } + + @Override + public T higher(T t) { + return navigableSet.higher(t); + } + + @Override + public T pollFirst() { + return navigableSet.pollFirst(); + } + + @Override + public T pollLast() { + return navigableSet.pollLast(); + } + + @Override + public NavigableSet descendingSet() { + return new UnmodifiableNavigableSet<>(navigableSet.descendingSet()); + } + + @Override + public Iterator descendingIterator() { + return navigableSet.descendingIterator(); + } + + @Override + public NavigableSet subSet(T fromElement, boolean fromInclusive, T toElement, + boolean toInclusive) { + return new UnmodifiableNavigableSet<>( + navigableSet.subSet(fromElement, fromInclusive, toElement, toInclusive)); + } + + @Override + public NavigableSet headSet(T toElement, boolean inclusive) { + return new UnmodifiableNavigableSet<>(navigableSet.headSet(toElement, inclusive)); + } + + @Override + public NavigableSet tailSet(T fromElement, boolean inclusive) { + return new UnmodifiableNavigableSet<>(navigableSet.tailSet(fromElement, inclusive)); + } + } + private static class UnmodifiableCollectionIterator implements Iterator { private final Iterator it; @@ -1124,6 +1294,20 @@ public T nextElement() { }; } + public static Enumeration emptyEnumeration() { + return new Enumeration() { + @Override + public boolean hasMoreElements() { + return false; + } + + @Override + public T nextElement() { + throw new NoSuchElementException(); + } + }; + } + public static void fill(List list, T obj) { for (ListIterator it = list.listIterator(); it.hasNext();) { it.next(); @@ -1373,6 +1557,57 @@ public static SortedSet unmodifiableSortedSet(SortedSet set) { return new UnmodifiableSortedSet(set); } + public static NavigableMap unmodifiableNavigableMap(NavigableMap map) { + return new UnmodifiableNavigableMap<>(map); + } + + public static NavigableSet unmodifiableNavigableSet(NavigableSet set) { + return new UnmodifiableNavigableSet<>(set); + } + + public static SortedSet emptySortedSet() { + return new UnmodifiableSortedSet(new TreeSet<>()); + } + + public static SortedMap emptySortedMap() { + return new UnmodifiableSortedMap<>(new TreeMap<>()); + } + + public static NavigableSet emptyNavigableSet() { + return new UnmodifiableNavigableSet(new TreeSet<>()); + } + + public static NavigableMap emptyNavigableMap() { + return new UnmodifiableNavigableMap<>(new TreeMap<>()); + } + + public static int indexOfSubList(List source, List target) { + for (int start = 0; start <= source.size() - target.size(); start++) { + if (sublistMatch(source, target, start)) { + return start; + } + } + return -1; + } + + private static boolean sublistMatch(List source, List target, int start) { + for (int idx = 0; idx < target.size(); idx++) { + if (!Objects.equals(target.get(idx), source.get(idx + start))) { + return false; + } + } + return true; + } + + public static int lastIndexOfSubList(List source, List target) { + for (int start = source.size() - target.size(); start >= 0; start--) { + if (sublistMatch(source, target, start)) { + return start; + } + } + return -1; + } + /** * Computes hash code without preserving elements order (e.g. HashSet). */