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 a47fb8c98b3..49dffb4ceb9 100644 --- a/user/super/com/google/gwt/emul/java/io/PrintStream.java +++ b/user/super/com/google/gwt/emul/java/io/PrintStream.java @@ -19,7 +19,7 @@ * See the official Java * API doc for details. */ -public class PrintStream extends FilterOutputStream { +public class PrintStream extends FilterOutputStream implements Appendable { /** Indicates whether or not this PrintStream has incurred an error. */ private boolean ioError = false; @@ -195,4 +195,22 @@ protected void clearError() { private void newline() { print('\n'); } + + @Override + public Appendable append(CharSequence csq) { + print(csq); + return this; + } + + @Override + public Appendable append(CharSequence csq, int start, int end) { + print(csq.subSequence(start, end)); + return this; + } + + @Override + public Appendable append(char c) { + print(c); + return this; + } } 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 63216ad3a92..db1b0c65935 100644 --- a/user/super/com/google/gwt/emul/java/io/Reader.java +++ b/user/super/com/google/gwt/emul/java/io/Reader.java @@ -18,7 +18,7 @@ /** * Reads a stream of characters. */ -public abstract class Reader { +public abstract class Reader implements Closeable { /** * The maximum buffer size to incrementally read in {@link #skip}. */ @@ -27,6 +27,7 @@ public abstract class Reader { /** * Closes the reader, and releases any associated resources. */ + @Override public abstract void close() throws IOException; /** diff --git a/user/super/com/google/gwt/emul/java/lang/Boolean.java b/user/super/com/google/gwt/emul/java/lang/Boolean.java index 4aafa9c1c66..188e08b72d2 100644 --- a/user/super/com/google/gwt/emul/java/lang/Boolean.java +++ b/user/super/com/google/gwt/emul/java/lang/Boolean.java @@ -18,13 +18,14 @@ import static javaemul.internal.InternalPreconditions.checkNotNull; import java.io.Serializable; +import java.lang.constant.Constable; import javaemul.internal.JsUtils; import jsinterop.annotations.JsMethod; /** * Wraps native boolean as an object. */ -public final class Boolean implements Comparable, Serializable { +public final class Boolean implements Comparable, Serializable, Constable { public static final Boolean FALSE = false; public static final Boolean TRUE = true; diff --git a/user/super/com/google/gwt/emul/java/lang/Byte.java b/user/super/com/google/gwt/emul/java/lang/Byte.java index d5f0f1f93d7..d40fbd3ab77 100644 --- a/user/super/com/google/gwt/emul/java/lang/Byte.java +++ b/user/super/com/google/gwt/emul/java/lang/Byte.java @@ -15,10 +15,12 @@ */ package java.lang; +import java.lang.constant.Constable; + /** * Wraps native byte as an object. */ -public final class Byte extends Number implements Comparable { +public final class Byte extends Number implements Comparable, Constable { public static final byte MIN_VALUE = (byte) 0x80; public static final byte MAX_VALUE = (byte) 0x7F; diff --git a/user/super/com/google/gwt/emul/java/lang/Class.java b/user/super/com/google/gwt/emul/java/lang/Class.java index c59eed0b6cc..8bda1112fb6 100644 --- a/user/super/com/google/gwt/emul/java/lang/Class.java +++ b/user/super/com/google/gwt/emul/java/lang/Class.java @@ -17,6 +17,7 @@ import com.google.gwt.core.client.JavaScriptObject; +import java.lang.constant.Constable; import java.lang.reflect.Type; import javaemul.internal.annotations.DoNotInline; @@ -29,7 +30,7 @@ * * @param the type of the object */ -public final class Class implements Type { +public final class Class implements Type, Constable { private static final int PRIMITIVE = 0x00000001; private static final int INTERFACE = 0x00000002; diff --git a/user/super/com/google/gwt/emul/java/lang/Enum.java b/user/super/com/google/gwt/emul/java/lang/Enum.java index b044a57b6cb..861eeed3a0e 100644 --- a/user/super/com/google/gwt/emul/java/lang/Enum.java +++ b/user/super/com/google/gwt/emul/java/lang/Enum.java @@ -21,6 +21,7 @@ import com.google.gwt.core.client.JavaScriptObject; import java.io.Serializable; +import java.lang.constant.Constable; import jsinterop.annotations.JsIgnore; import jsinterop.annotations.JsNonNull; import jsinterop.annotations.JsType; @@ -31,7 +32,7 @@ * @param */ @JsType -public abstract class Enum> implements Comparable, Serializable { +public abstract class Enum> implements Comparable, Serializable, Constable { @JsIgnore public static > T valueOf(Class enumType, String name) { diff --git a/user/super/com/google/gwt/emul/java/lang/Float.java b/user/super/com/google/gwt/emul/java/lang/Float.java index 4e5ec5ec02b..39d5f71b2c7 100644 --- a/user/super/com/google/gwt/emul/java/lang/Float.java +++ b/user/super/com/google/gwt/emul/java/lang/Float.java @@ -15,12 +15,14 @@ */ package java.lang; +import java.lang.constant.Constable; +import java.lang.constant.ConstantDesc; import javaemul.internal.JsUtils; /** * Wraps a primitive float as an object. */ -public final class Float extends Number implements Comparable { +public final class Float extends Number implements Comparable, Constable, ConstantDesc { public static final float MAX_VALUE = 3.4028235e+38f; public static final float MIN_VALUE = 1.4e-45f; public static final int MAX_EXPONENT = 127; diff --git a/user/super/com/google/gwt/emul/java/lang/Integer.java b/user/super/com/google/gwt/emul/java/lang/Integer.java index d349bdab092..60f179fbcc5 100644 --- a/user/super/com/google/gwt/emul/java/lang/Integer.java +++ b/user/super/com/google/gwt/emul/java/lang/Integer.java @@ -15,13 +15,15 @@ */ package java.lang; +import java.lang.constant.Constable; +import java.lang.constant.ConstantDesc; import javaemul.internal.JsUtils; import javaemul.internal.annotations.HasNoSideEffects; /** * Wraps a primitive int as an object. */ -public final class Integer extends Number implements Comparable { +public final class Integer extends Number implements Comparable, Constable, ConstantDesc { public static final int MAX_VALUE = 0x7fffffff; public static final int MIN_VALUE = 0x80000000; diff --git a/user/super/com/google/gwt/emul/java/lang/Long.java b/user/super/com/google/gwt/emul/java/lang/Long.java index 33622aa6b4f..886984b52fc 100644 --- a/user/super/com/google/gwt/emul/java/lang/Long.java +++ b/user/super/com/google/gwt/emul/java/lang/Long.java @@ -15,11 +15,13 @@ */ package java.lang; +import java.lang.constant.Constable; +import java.lang.constant.ConstantDesc; import javaemul.internal.LongUtils; import javaemul.internal.annotations.HasNoSideEffects; /** Wraps a primitive long as an object. */ -public final class Long extends Number implements Comparable { +public final class Long extends Number implements Comparable, Constable, ConstantDesc { /** Use nested class to avoid clinit on outer. */ static class BoxedValues { diff --git a/user/super/com/google/gwt/emul/java/lang/String.java b/user/super/com/google/gwt/emul/java/lang/String.java index 67b1cff1107..126cc180677 100644 --- a/user/super/com/google/gwt/emul/java/lang/String.java +++ b/user/super/com/google/gwt/emul/java/lang/String.java @@ -24,6 +24,8 @@ import java.io.Serializable; import java.io.UnsupportedEncodingException; +import java.lang.constant.Constable; +import java.lang.constant.ConstantDesc; import java.nio.charset.Charset; import java.nio.charset.UnsupportedCharsetException; import java.util.Comparator; @@ -55,7 +57,7 @@ // Needed to have constructors not fail compilation internally at Google @SuppressWarnings({ "ReturnValueIgnored", "unusable-by-js" }) public final class String implements Comparable, CharSequence, - Serializable { + Serializable, Constable, ConstantDesc { /* TODO(jat): consider whether we want to support the following methods; * *
    diff --git a/user/super/com/google/gwt/emul/java/lang/StringBuffer.java b/user/super/com/google/gwt/emul/java/lang/StringBuffer.java index 91f154a436e..4fb2d838871 100644 --- a/user/super/com/google/gwt/emul/java/lang/StringBuffer.java +++ b/user/super/com/google/gwt/emul/java/lang/StringBuffer.java @@ -23,7 +23,8 @@ * This class is an exact clone of {@link StringBuilder} except for the name. * Any change made to one should be mirrored in the other. */ -public final class StringBuffer extends AbstractStringBuilder { +public final class StringBuffer extends AbstractStringBuilder + implements Comparable { public StringBuffer() { super(""); @@ -187,4 +188,9 @@ public StringBuffer reverse() { reverse0(); return this; } + + @Override + public int compareTo(StringBuffer other) { + return toString().compareTo(other.toString()); + } } diff --git a/user/super/com/google/gwt/emul/java/lang/StringBuilder.java b/user/super/com/google/gwt/emul/java/lang/StringBuilder.java index da705efb0a6..5c8d37ae8ea 100644 --- a/user/super/com/google/gwt/emul/java/lang/StringBuilder.java +++ b/user/super/com/google/gwt/emul/java/lang/StringBuilder.java @@ -23,7 +23,8 @@ * This class is an exact clone of {@link StringBuffer} except for the name. * Any change made to one should be mirrored in the other. */ -public final class StringBuilder extends AbstractStringBuilder { +public final class StringBuilder extends AbstractStringBuilder + implements Comparable { public StringBuilder() { super(""); @@ -187,4 +188,9 @@ public StringBuilder reverse() { reverse0(); return this; } + + @Override + public int compareTo(StringBuilder other) { + return toString().compareTo(other.toString()); + } } diff --git a/user/super/com/google/gwt/emul/java/lang/constant/Constable.java b/user/super/com/google/gwt/emul/java/lang/constant/Constable.java new file mode 100644 index 00000000000..49a8d2975d3 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/lang/constant/Constable.java @@ -0,0 +1,19 @@ +/* + * Copyright 2025 GWT Project Authors + * + * Licensed 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 java.lang.constant; + +public interface Constable { +} diff --git a/user/super/com/google/gwt/emul/java/lang/constant/ConstantDesc.java b/user/super/com/google/gwt/emul/java/lang/constant/ConstantDesc.java new file mode 100644 index 00000000000..f51d69dbcda --- /dev/null +++ b/user/super/com/google/gwt/emul/java/lang/constant/ConstantDesc.java @@ -0,0 +1,19 @@ +/* + * Copyright 2026 GWT Project Authors + * + * Licensed 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 java.lang.constant; + +public interface ConstantDesc { +} diff --git a/user/super/com/google/gwt/emul/java/util/BitSet.java b/user/super/com/google/gwt/emul/java/util/BitSet.java index 1fa15d55677..0455a86f4de 100644 --- a/user/super/com/google/gwt/emul/java/util/BitSet.java +++ b/user/super/com/google/gwt/emul/java/util/BitSet.java @@ -34,7 +34,7 @@ * public static BitSet valueOf(ByteBuffer) * public static BitSet valueOf(LongBuffer) */ -public class BitSet { +public class BitSet implements Cloneable { private static final int WORD_MASK = 0x7fffffff; private static final int BITS_PER_WORD = 31; diff --git a/user/super/com/google/gwt/emul/java/util/EnumMap.java b/user/super/com/google/gwt/emul/java/util/EnumMap.java index 00ef1b98e95..949c3398043 100644 --- a/user/super/com/google/gwt/emul/java/util/EnumMap.java +++ b/user/super/com/google/gwt/emul/java/util/EnumMap.java @@ -28,7 +28,7 @@ * @param key type * @param value type */ -public class EnumMap, V> extends AbstractMap { +public class EnumMap, V> extends AbstractMap implements Cloneable { private final class EntrySet extends AbstractSet> { diff --git a/user/super/com/google/gwt/emul/java/util/EnumSet.java b/user/super/com/google/gwt/emul/java/util/EnumSet.java index 9d3c1472268..aa48fce4845 100644 --- a/user/super/com/google/gwt/emul/java/util/EnumSet.java +++ b/user/super/com/google/gwt/emul/java/util/EnumSet.java @@ -31,7 +31,7 @@ * * @param enumeration type */ -public abstract class EnumSet> extends AbstractSet { +public abstract class EnumSet> extends AbstractSet implements Cloneable { /** * Implemented via sparse array since the set size is finite. Iteration takes diff --git a/user/super/com/google/gwt/emul/java/util/Random.java b/user/super/com/google/gwt/emul/java/util/Random.java index 4a7da061541..acb44db3c37 100644 --- a/user/super/com/google/gwt/emul/java/util/Random.java +++ b/user/super/com/google/gwt/emul/java/util/Random.java @@ -37,6 +37,7 @@ import static javaemul.internal.InternalPreconditions.checkCriticalArgument; import static javaemul.internal.InternalPreconditions.checkNotNull; +import java.util.random.RandomGenerator; import javaemul.internal.JsUtils; /** @@ -46,7 +47,7 @@ * * This emulated version of Random is not serializable. */ -public class Random { +public class Random implements RandomGenerator { private static final double multiplierHi = 0x5de; private static final double multiplierLo = 0xece66d; @@ -132,23 +133,12 @@ public Random(long seed) { setSeed(seed); } - /** - * Returns the next pseudo-random, uniformly distributed {@code boolean} value - * generated by this generator. - * - * @return a pseudo-random, uniformly distributed boolean value. - */ + @Override public boolean nextBoolean() { return nextInternal(1) != 0; } - /** - * Modifies the {@code byte} array by a random sequence of {@code byte}s - * generated by this random number generator. - * - * @param buf non-null array to contain the new random {@code byte}s. - * @see #next - */ + @Override public void nextBytes(byte[] buf) { checkNotNull(buf); @@ -165,25 +155,13 @@ public void nextBytes(byte[] buf) { } } - /** - * Generates a normally distributed random {@code double} number between 0.0 - * inclusively and 1.0 exclusively. - * - * @return a random {@code double} in the range [0.0 - 1.0) - * @see #nextFloat - */ + @Override public double nextDouble() { return nextInternal(26) * twoToTheMinus26 + nextInternal(27) * twoToTheMinus53; } - /** - * Generates a normally distributed random {@code float} number between 0.0 - * inclusively and 1.0 exclusively. - * - * @return float a random {@code float} number between [0.0 and 1.0) - * @see #nextDouble - */ + @Override public float nextFloat() { return (float) (nextInternal(24) * twoToTheMinus24); } @@ -222,28 +200,12 @@ public synchronized double nextGaussian() { return v1 * norm; } - /** - * Generates a uniformly distributed 32-bit {@code int} value from the random - * number sequence. - * - * @return a uniformly distributed {@code int} value. - * @see java.lang.Integer#MAX_VALUE - * @see java.lang.Integer#MIN_VALUE - * @see #next - * @see #nextLong - */ + @Override public int nextInt() { return (int) nextInternal(32); } - /** - * Returns a new pseudo-random {@code int} value which is uniformly - * distributed between 0 (inclusively) and the value of {@code n} - * (exclusively). - * - * @param n the exclusive upper border of the range [0 - n). - * @return a random {@code int}. - */ + @Override public int nextInt(int n) { checkCriticalArgument(n > 0); @@ -258,17 +220,7 @@ public int nextInt(int n) { return (int) val; } - /** - * Generates a uniformly distributed 64-bit integer value from the random - * number sequence. - * - * @return 64-bit random integer. - * @see java.lang.Integer#MAX_VALUE - * @see java.lang.Integer#MIN_VALUE - * @see #next - * @see #nextInt() - * @see #nextInt(int) - */ + @Override public long nextLong() { return ((long) nextInternal(32) << 32) + (long) nextInternal(32); } diff --git a/user/super/com/google/gwt/emul/java/util/random/RandomGenerator.java b/user/super/com/google/gwt/emul/java/util/random/RandomGenerator.java new file mode 100644 index 00000000000..b096eb46555 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/util/random/RandomGenerator.java @@ -0,0 +1,100 @@ +/* + * Copyright 2026 GWT Project Authors + * + * Licensed 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 java.util.random; + +public interface RandomGenerator { + + /** + * Returns the next pseudo-random, uniformly distributed {@code boolean} value + * generated by this generator. + * + * @return a pseudo-random, uniformly distributed boolean value. + */ + boolean nextBoolean(); + + /** + * Generates a uniformly distributed 32-bit {@code int} value from the random + * number sequence. + * + * @return a uniformly distributed {@code int} value. + * @see java.lang.Integer#MAX_VALUE + * @see java.lang.Integer#MIN_VALUE + * @see #nextLong + */ + int nextInt(); + + /** + * Returns a new pseudo-random {@code int} value which is uniformly + * distributed between 0 (inclusively) and the value of {@code n} + * (exclusively). + * + * @param n the exclusive upper border of the range [0 - n). + * @return a random {@code int}. + */ + int nextInt(int n); + + /** + * Generates a uniformly distributed 64-bit integer value from the random + * number sequence. + * + * @return 64-bit random integer. + * @see java.lang.Integer#MAX_VALUE + * @see java.lang.Integer#MIN_VALUE + * @see #nextInt() + * @see #nextInt(int) + */ + long nextLong(); + + /** + * Generates a normally distributed random {@code float} number between 0.0 + * inclusively and 1.0 exclusively. + * + * @return float a random {@code float} number between [0.0 and 1.0) + * @see #nextDouble + */ + float nextFloat(); + + /** + * Generates a normally distributed random {@code double} number between 0.0 + * inclusively and 1.0 exclusively. + * + * @return a random {@code double} in the range [0.0 - 1.0) + * @see #nextFloat + */ + double nextDouble(); + + /** + * Pseudo-randomly generates (approximately) a normally distributed {@code + * double} value with mean 0.0 and a standard deviation value of {@code 1.0} + * using the polar method of G. E. P. Box, M. E. Muller, and G. + * Marsaglia, as described by Donald E. Knuth in The Art of Computer + * Programming, Volume 2: Seminumerical Algorithms, section 3.4.1, + * subsection C, algorithm P. + * + * @return a random {@code double} + * @see #nextDouble + */ + double nextGaussian(); + + /** + * Modifies the {@code byte} array by a random sequence of {@code byte}s + * generated by this random number generator. + * + * @param buf non-null array to contain the new random {@code byte}s. + */ + void nextBytes(byte[] buf); +} \ No newline at end of file diff --git a/user/test/com/google/gwt/emultest/java/io/PrintStreamTest.java b/user/test/com/google/gwt/emultest/java/io/PrintStreamTest.java index bdfeb4921cb..3b10d8a4f13 100644 --- a/user/test/com/google/gwt/emultest/java/io/PrintStreamTest.java +++ b/user/test/com/google/gwt/emultest/java/io/PrintStreamTest.java @@ -38,7 +38,7 @@ public class PrintStreamTest extends GWTTestCase { private static class MockPrintStream extends PrintStream { - public MockPrintStream(OutputStream os) { + MockPrintStream(OutputStream os) { super(os); } @@ -281,4 +281,11 @@ public void testClearError() { assertFalse(ps.checkError()); ps.close(); } + + public void testAppend() { + ps.append('h'); + ps.append("owd"); + ps.append("xyz", 1, 2); + assertEquals("howdy", new String(baos.toByteArray(), UTF_8)); + } } diff --git a/user/test/com/google/gwt/emultest/java/io/StringReaderTest.java b/user/test/com/google/gwt/emultest/java/io/StringReaderTest.java index c9d79de9338..191f83d8d5c 100644 --- a/user/test/com/google/gwt/emultest/java/io/StringReaderTest.java +++ b/user/test/com/google/gwt/emultest/java/io/StringReaderTest.java @@ -102,6 +102,14 @@ public void testSkip_longString() throws Exception { assertEquals(-1, reader.read()); } + public void testClose() throws Exception { + char[] out = new char[3]; + try (StringReader reader = new StringReader("foo")) { + assertEquals(3, reader.read(out)); + } + assertEquals("foo", new String(out)); + } + private static String repeat(char c, int count) { char[] arr = new char[count]; Arrays.fill(arr, c); diff --git a/user/test/com/google/gwt/emultest/java/lang/StringBufferTest.java b/user/test/com/google/gwt/emultest/java/lang/StringBufferTest.java index 72ce24508c8..a7b78692999 100644 --- a/user/test/com/google/gwt/emultest/java/lang/StringBufferTest.java +++ b/user/test/com/google/gwt/emultest/java/lang/StringBufferTest.java @@ -18,6 +18,8 @@ import com.google.gwt.junit.client.GWTTestCase; import java.util.Locale; +import java.util.TreeSet; +import java.util.stream.Collectors; /** * This class tests classes StringBuffer and StringBuilder. @@ -552,4 +554,30 @@ public void testSubstring() { assertEquals("bc", "abcdef".substring(1, 3)); assertEquals("bcdef", "abcdef".substring(1)); } + + public void testCompareToBuffer() { + assertTrue(new StringBuffer("b").compareTo(new StringBuffer("a")) > 0); + assertTrue(new StringBuffer("b").compareTo(new StringBuffer("c")) < 0); + assertEquals(0, new StringBuffer("b").compareTo(new StringBuffer("b"))); + + TreeSet sorted = new TreeSet<>(); + sorted.add(new StringBuffer("b")); + sorted.add(new StringBuffer("c")); + sorted.add(new StringBuffer("d")); + sorted.add(new StringBuffer("a")); + assertEquals("abcd", sorted.stream().map(Object::toString).collect(Collectors.joining())); + } + + public void testCompareToBuilder() { + assertTrue(new StringBuilder("b").compareTo(new StringBuilder("a")) > 0); + assertTrue(new StringBuilder("b").compareTo(new StringBuilder("c")) < 0); + assertEquals(0, new StringBuilder("b").compareTo(new StringBuilder("b"))); + + TreeSet sorted = new TreeSet<>(); + sorted.add(new StringBuilder("b")); + sorted.add(new StringBuilder("c")); + sorted.add(new StringBuilder("d")); + sorted.add(new StringBuilder("a")); + assertEquals("abcd", sorted.stream().map(Object::toString).collect(Collectors.joining())); + } }