Skip to content

Commit

Permalink
Update Collections to support APIs added in Java 9
Browse files Browse the repository at this point in the history
It is based on GWT but avoids adding non-official new public APIs and implementation is optimized for J2CL.
Earlier review at https://gwt-review.googlesource.com/c/gwt/+/21501.

Closes #223

PiperOrigin-RevId: 604514266
  • Loading branch information
treblereel authored and copybara-github committed Feb 6, 2024
1 parent ac628dd commit ca50a8b
Show file tree
Hide file tree
Showing 11 changed files with 840 additions and 2 deletions.
40 changes: 40 additions & 0 deletions jre/java/java/util/Collections.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@
import static javaemul.internal.InternalPreconditions.checkArgument;
import static javaemul.internal.InternalPreconditions.checkElementIndex;
import static javaemul.internal.InternalPreconditions.checkNotNull;
import static javaemul.internal.InternalPreconditions.isApiChecked;

import java.io.Serializable;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import jsinterop.annotations.JsMethod;
import jsinterop.annotations.JsNonNull;

/**
Expand Down Expand Up @@ -1354,6 +1356,44 @@ public static <T> List<T> unmodifiableList(List<? extends T> list) {
list);
}

static <E> List<E> internalListOf(E[] elements) {
if (isApiChecked()) {
for (int i = 0; i < elements.length; i++) {
checkNotNull(elements[i]);
}
}
return new UnmodifiableRandomAccessList<E>(
elements.length == 0 ? emptyList() : Arrays.asList(elements));
}

static <E> Set<E> internalSetOf(E[] elements) {
if (elements.length == 0) {
return Collections.unmodifiableSet(emptySet());
}

Set<E> set = new HashSet<>();
for (int i = 0; i < elements.length; i++) {
boolean added = set.add(checkNotNull(elements[i]));
checkArgument(added, "Duplicate element");
}
return Collections.unmodifiableSet(set);
}

// Marked as JsMethod to take advantage of JS varargs.
@JsMethod
static <K, V> Map<K, V> internalMapOf(Object... elements) {
if (elements.length == 0) {
return Collections.unmodifiableMap(emptyMap());
}

Map<K, V> map = new HashMap<>();
for (int i = 0; i < elements.length; i = i + 2) {
V old = map.put((K) checkNotNull(elements[i]), (V) checkNotNull(elements[i + 1]));
checkArgument(old == null, "Duplicate element");
}
return Collections.unmodifiableMap(map);
}

public static <K, V> Map<K, V> unmodifiableMap(
final Map<? extends K, ? extends V> map) {
return new UnmodifiableMap<K, V>(map);
Expand Down
77 changes: 77 additions & 0 deletions jre/java/java/util/List.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import static javaemul.internal.InternalPreconditions.checkNotNull;

import java.util.function.UnaryOperator;
import javaemul.internal.ArrayHelper;
import jsinterop.annotations.JsIgnore;
import jsinterop.annotations.JsMethod;
import jsinterop.annotations.JsNonNull;
Expand All @@ -33,6 +34,82 @@
@JsType
public interface List<E> extends Collection<E> {

@JsIgnore
static <E> List<E> of() {
return jsOf();
}

@JsIgnore
static <E> List<E> of(E e1) {
return jsOf(e1);
}

@JsIgnore
static <E> List<E> of(E e1, E e2) {
return jsOf(e1, e2);
}

@JsIgnore
static <E> List<E> of(E e1, E e2, E e3) {
return jsOf(e1, e2, e3);
}

@JsIgnore
static <E> List<E> of(E e1, E e2, E e3, E e4) {
return jsOf(e1, e2, e3, e4);
}

@JsIgnore
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5) {
return jsOf(e1, e2, e3, e4, e5);
}

@JsIgnore
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6) {
return jsOf(e1, e2, e3, e4, e5, e6);
}

@JsIgnore
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) {
return jsOf(e1, e2, e3, e4, e5, e6, e7);
}

@JsIgnore
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) {
return jsOf(e1, e2, e3, e4, e5, e6, e7, e8);
}

@JsIgnore
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) {
return jsOf(e1, e2, e3, e4, e5, e6, e7, e8, e9);
}

@JsIgnore
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) {
return jsOf(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
}

@JsIgnore // List.of API is exposed to JS via other means.
static <E> List<E> of(E... elements) {
// Note that this is not JsMethod.
// If it was, then Java caller under JavaScript transpilation would do a JavaScript spread. We
// cannot just trust that cloning since under Wasm, it won't have such cloning. As a result
// under JS, we would end up having an extra clone happening.
// To workaround that, we avoid marking this as a JS method and expose another method to JS to
// act as the List.of implementation.

// Since one might pass an array here, we need to do a defensive copy here.
return Collections.internalListOf((E[]) ArrayHelper.unsafeClone(elements, 0, elements.length));
}

/** List.of API that is friendly to use from JavaScript. */
@JsMethod(name = "of")
private static <E> List<E> jsOf(E... elements) {
// Forward directly as we don't need defensive copy for JavaScript calls.
// Note that this method is also used internal "of(E e)" etc, to take advantage of JS varargs.
return Collections.internalListOf(elements);
}

@JsMethod(name = "addAtIndex")
void add(int index, E element);

Expand Down
139 changes: 138 additions & 1 deletion jre/java/java/util/Map.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
*/
package java.util;

import static javaemul.internal.InternalPreconditions.checkArgument;
import static javaemul.internal.InternalPreconditions.checkNotNull;

import java.io.Serializable;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;

import jsinterop.annotations.JsIgnore;
import jsinterop.annotations.JsNonNull;
import jsinterop.annotations.JsType;
Expand All @@ -35,6 +35,143 @@
@JsType
public interface Map<K, V> {

@JsIgnore
static <K, V> Map<K, V> of() {
return Collections.internalMapOf();
}

@JsIgnore
static <K, V> Map<K, V> of(K key, V value) {
return Collections.internalMapOf(key, value);
}

@JsIgnore
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2) {
return Collections.internalMapOf(k1, v1, k2, v2);
}

@JsIgnore
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3) {
return Collections.internalMapOf(k1, v1, k2, v2, k3, v3);
}

@JsIgnore
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
return Collections.internalMapOf(k1, v1, k2, v2, k3, v3, k4, v4);
}

@JsIgnore
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
return Collections.internalMapOf(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5);
}

@JsIgnore
static <K, V> Map<K, V> of(
K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) {
return Collections.internalMapOf(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6);
}

@JsIgnore
static <K, V> Map<K, V> of(
K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7) {
return Collections.internalMapOf(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7);
}

@JsIgnore
static <K, V> Map<K, V> of(
K k1,
V v1,
K k2,
V v2,
K k3,
V v3,
K k4,
V v4,
K k5,
V v5,
K k6,
V v6,
K k7,
V v7,
K k8,
V v8) {
return Collections.internalMapOf(
k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8);
}

@JsIgnore
static <K, V> Map<K, V> of(
K k1,
V v1,
K k2,
V v2,
K k3,
V v3,
K k4,
V v4,
K k5,
V v5,
K k6,
V v6,
K k7,
V v7,
K k8,
V v8,
K k9,
V v9) {
return Collections.internalMapOf(
k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9);
}

@JsIgnore
static <K, V> Map<K, V> of(
K k1,
V v1,
K k2,
V v2,
K k3,
V v3,
K k4,
V v4,
K k5,
V v5,
K k6,
V v6,
K k7,
V v7,
K k8,
V v8,
K k9,
V v9,
K k10,
V v10) {
return Collections.internalMapOf(
k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10);
}

@JsIgnore
static <K, V> Entry<K, V> entry(K key, V value) {
// This isn't quite consistent with the javadoc, since this is serializable, while entry()
// need not be serializable.
return new AbstractMap.SimpleImmutableEntry<>(checkNotNull(key), checkNotNull(value));
}

@JsIgnore
static <K, V> Map<K, V> ofEntries(Entry<? extends K, ? extends V>... entries) {
if (entries.length == 0) {
return Collections.emptyMap();
}

Map<K, V> map = new HashMap<>();
for (Entry<? extends K, ? extends V> entry : entries) {
checkNotNull(entry);
V old = map.put(checkNotNull(entry.getKey()), checkNotNull(entry.getValue()));
checkArgument(old == null, "Duplicate element");
}

return Collections.unmodifiableMap(map);
}

/**
* Represents an individual map entry.
*/
Expand Down
70 changes: 70 additions & 0 deletions jre/java/java/util/Set.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package java.util;

import jsinterop.annotations.JsIgnore;
import jsinterop.annotations.JsMethod;
import jsinterop.annotations.JsType;

/**
Expand All @@ -31,6 +32,75 @@ public interface Set<E> extends Collection<E> {
@Override
Iterator<E> iterator();

@JsIgnore
static <E> Set<E> of() {
return jsOf();
}

@JsIgnore
static <E> Set<E> of(E e1) {
return jsOf(e1);
}

@JsIgnore
static <E> Set<E> of(E e1, E e2) {
return jsOf(e1, e2);
}

@JsIgnore
static <E> Set<E> of(E e1, E e2, E e3) {
return jsOf(e1, e2, e3);
}

@JsIgnore
static <E> Set<E> of(E e1, E e2, E e3, E e4) {
return jsOf(e1, e2, e3, e4);
}

@JsIgnore
static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5) {
return jsOf(e1, e2, e3, e4, e5);
}

@JsIgnore
static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6) {
return jsOf(e1, e2, e3, e4, e5, e6);
}

@JsIgnore
static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) {
return jsOf(e1, e2, e3, e4, e5, e6, e7);
}

@JsIgnore
static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) {
return jsOf(e1, e2, e3, e4, e5, e6, e7, e8);
}

@JsIgnore
static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) {
return jsOf(e1, e2, e3, e4, e5, e6, e7, e8, e9);
}

@JsIgnore
static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) {
return jsOf(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
}

@JsIgnore
static <E> Set<E> of(E... elements) {
// This is not marked as JS method for symmetry with List.of and avoid extra cloning at
// call sites when an array is passed. A different method provided as Set.of to JS below.
return Collections.internalSetOf(elements);
}

/** Set.of API that is friendly to use from JavaScript. */
@JsMethod(name = "of")
static <E> Set<E> jsOf(E... elements) {
// Note that this method is also used internal "of(E e)" etc, to take advantage of JS varargs.
return Collections.internalSetOf(elements);
}

@JsIgnore
@Override
default Spliterator<E> spliterator() {
Expand Down
Loading

0 comments on commit ca50a8b

Please sign in to comment.