Skip to content

Commit

Permalink
Expose more Java 8 APIs to Android users.
Browse files Browse the repository at this point in the history
While there, remove a workaround for #1330: As discussed in cl/152418428, the problem was fixed for Java 8, and even Android has [a working implementation](https://cs.android.com/android/platform/superproject/+/android-5.0.0_r1.0.1:libcore/luni/src/main/java/java/io/FilterOutputStream.java;l=63,75;drc=eb8027492e81d5d3a0d1cd49494c59f9a03eeaa3) by 5.0.0, [aka Lollipop](https://source.android.com/docs/setup/reference/build-numbers), which is [what we target](https://guava.dev/#important-warnings).

And address a few more warnings.

RELNOTES=Exposed some additional Java 8 APIs to Android users. Plus: `io`: Changed `ByteSink` and `CharSink` to no longer call `flush()` in some cases before `close()`. This is a no-op for well-behaved streams, which internally flush their data as part of closing. However, we have discovered some stream implementations that have overridden `close()` to do nothing, including not to flush some buffered data. If this change causes problems, the simplest fix is usually to change the `close()` override to at least call `flush()`.
PiperOrigin-RevId: 691790805
  • Loading branch information
cpovirk authored and Google Java Core Libraries committed Oct 31, 2024
1 parent 664c80b commit 6ace8bc
Show file tree
Hide file tree
Showing 22 changed files with 2,448 additions and 68 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2019 The Guava 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 com.google.common.collect.testing;

import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;

import java.lang.annotation.Target;

/**
* Disables Animal Sniffer's checking of compatibility with older versions of Java/Android.
*
* <p>Each package's copy of this annotation needs to be listed in our {@code pom.xml}.
*/
@Target({METHOD, CONSTRUCTOR, TYPE})
@ElementTypesAreNonnullByDefault
@interface IgnoreJRERequirement {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,349 @@
/*
* Copyright (C) 2015 The Guava 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 com.google.common.collect.testing;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.testing.Helpers.assertEqualIgnoringOrder;
import static com.google.common.collect.testing.Helpers.assertEqualInOrder;
import static com.google.common.collect.testing.Platform.format;
import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableSet;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;

import com.google.common.annotations.GwtCompatible;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Ordering;
import com.google.common.primitives.Ints;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterator.OfPrimitive;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
* Tester for {@code Spliterator} implementations.
*
* @since NEXT (but since 21.0 in the JRE flavor)
*/
@GwtCompatible
@ElementTypesAreNonnullByDefault
@SuppressWarnings("Java7ApiChecker")
@IgnoreJRERequirement // Users will use this only if they're already using Spliterator.
public final class SpliteratorTester<E extends @Nullable Object> {
/** Return type from "contains the following elements" assertions. */
public interface Ordered {
/**
* Attests that the expected values must not just be present but must be present in the order
* they were given.
*/
void inOrder();
}

@IgnoreJRERequirement // *should* be redundant with the annotation on SpliteratorTester
private abstract static class GeneralSpliterator<E extends @Nullable Object> {
final Spliterator<E> spliterator;

GeneralSpliterator(Spliterator<E> spliterator) {
this.spliterator = checkNotNull(spliterator);
}

abstract void forEachRemaining(Consumer<? super E> action);

abstract boolean tryAdvance(Consumer<? super E> action);

abstract @Nullable GeneralSpliterator<E> trySplit();

final int characteristics() {
return spliterator.characteristics();
}

final long estimateSize() {
return spliterator.estimateSize();
}

final Comparator<? super E> getComparator() {
return spliterator.getComparator();
}

final long getExactSizeIfKnown() {
return spliterator.getExactSizeIfKnown();
}

final boolean hasCharacteristics(int characteristics) {
return spliterator.hasCharacteristics(characteristics);
}
}

@IgnoreJRERequirement // *should* be redundant with the annotation on SpliteratorTester
private static final class GeneralSpliteratorOfObject<E extends @Nullable Object>
extends GeneralSpliterator<E> {
GeneralSpliteratorOfObject(Spliterator<E> spliterator) {
super(spliterator);
}

@Override
void forEachRemaining(Consumer<? super E> action) {
spliterator.forEachRemaining(action);
}

@Override
boolean tryAdvance(Consumer<? super E> action) {
return spliterator.tryAdvance(action);
}

@Override
@Nullable GeneralSpliterator<E> trySplit() {
Spliterator<E> split = spliterator.trySplit();
return split == null ? null : new GeneralSpliteratorOfObject<>(split);
}
}

@IgnoreJRERequirement // *should* be redundant with the annotation on SpliteratorTester
private static final class GeneralSpliteratorOfPrimitive<
E extends @Nullable Object, C, S extends Spliterator.OfPrimitive<E, C, S>>
extends GeneralSpliterator<E> {
final OfPrimitive<E, C, S> spliteratorOfPrimitive;
final Function<Consumer<? super E>, C> consumerizer;

GeneralSpliteratorOfPrimitive(
Spliterator.OfPrimitive<E, C, S> spliterator,
Function<Consumer<? super E>, C> consumerizer) {
super(spliterator);
this.spliteratorOfPrimitive = spliterator;
this.consumerizer = consumerizer;
}

@Override
void forEachRemaining(Consumer<? super E> action) {
spliteratorOfPrimitive.forEachRemaining(consumerizer.apply(action));
}

@Override
boolean tryAdvance(Consumer<? super E> action) {
return spliteratorOfPrimitive.tryAdvance(consumerizer.apply(action));
}

@Override
@Nullable GeneralSpliterator<E> trySplit() {
Spliterator.OfPrimitive<E, C, ?> split = spliteratorOfPrimitive.trySplit();
return split == null ? null : new GeneralSpliteratorOfPrimitive<>(split, consumerizer);
}
}

/**
* Different ways of decomposing a Spliterator, all of which must produce the same elements (up to
* ordering, if Spliterator.ORDERED is not present).
*/
@IgnoreJRERequirement // *should* be redundant with the annotation on SpliteratorTester
enum SpliteratorDecompositionStrategy {
NO_SPLIT_FOR_EACH_REMAINING {
@Override
<E extends @Nullable Object> void forEach(
GeneralSpliterator<E> spliterator, Consumer<? super E> consumer) {
spliterator.forEachRemaining(consumer);
}
},
NO_SPLIT_TRY_ADVANCE {
@Override
<E extends @Nullable Object> void forEach(
GeneralSpliterator<E> spliterator, Consumer<? super E> consumer) {
while (spliterator.tryAdvance(consumer)) {
// do nothing
}
}
},
MAXIMUM_SPLIT {
@Override
<E extends @Nullable Object> void forEach(
GeneralSpliterator<E> spliterator, Consumer<? super E> consumer) {
for (GeneralSpliterator<E> prefix = trySplitTestingSize(spliterator);
prefix != null;
prefix = trySplitTestingSize(spliterator)) {
forEach(prefix, consumer);
}
long size = spliterator.getExactSizeIfKnown();
long[] counter = {0};
spliterator.forEachRemaining(
e -> {
consumer.accept(e);
counter[0]++;
});
if (size >= 0) {
assertEquals(size, counter[0]);
}
}
},
ALTERNATE_ADVANCE_AND_SPLIT {
@Override
<E extends @Nullable Object> void forEach(
GeneralSpliterator<E> spliterator, Consumer<? super E> consumer) {
while (spliterator.tryAdvance(consumer)) {
GeneralSpliterator<E> prefix = trySplitTestingSize(spliterator);
if (prefix != null) {
forEach(prefix, consumer);
}
}
}
};

abstract <E extends @Nullable Object> void forEach(
GeneralSpliterator<E> spliterator, Consumer<? super E> consumer);

static final Set<SpliteratorDecompositionStrategy> ALL_STRATEGIES =
unmodifiableSet(new LinkedHashSet<>(asList(values())));
}

private static <E extends @Nullable Object> @Nullable GeneralSpliterator<E> trySplitTestingSize(
GeneralSpliterator<E> spliterator) {
boolean subsized = spliterator.hasCharacteristics(Spliterator.SUBSIZED);
long originalSize = spliterator.estimateSize();
GeneralSpliterator<E> trySplit = spliterator.trySplit();
if (spliterator.estimateSize() > originalSize) {
fail(
format(
"estimated size of spliterator after trySplit (%s) is larger than original size (%s)",
spliterator.estimateSize(), originalSize));
}
if (trySplit != null) {
if (trySplit.estimateSize() > originalSize) {
fail(
format(
"estimated size of trySplit result (%s) is larger than original size (%s)",
trySplit.estimateSize(), originalSize));
}
}
if (subsized) {
if (trySplit != null) {
assertEquals(
"sum of estimated sizes of trySplit and original spliterator after trySplit",
originalSize,
trySplit.estimateSize() + spliterator.estimateSize());
} else {
assertEquals(
"estimated size of spliterator after failed trySplit",
originalSize,
spliterator.estimateSize());
}
}
return trySplit;
}

public static <E extends @Nullable Object> SpliteratorTester<E> of(
Supplier<Spliterator<E>> spliteratorSupplier) {
return new SpliteratorTester<>(
ImmutableSet.of(() -> new GeneralSpliteratorOfObject<>(spliteratorSupplier.get())));
}

/**
* @since NEXT (but since 28.1 in the JRE flavor)
*/
public static SpliteratorTester<Integer> ofInt(Supplier<Spliterator.OfInt> spliteratorSupplier) {
return new SpliteratorTester<>(
ImmutableSet.of(
() -> new GeneralSpliteratorOfObject<>(spliteratorSupplier.get()),
() -> new GeneralSpliteratorOfPrimitive<>(spliteratorSupplier.get(), c -> c::accept)));
}

/**
* @since NEXT (but since 28.1 in the JRE flavor)
*/
public static SpliteratorTester<Long> ofLong(Supplier<Spliterator.OfLong> spliteratorSupplier) {
return new SpliteratorTester<>(
ImmutableSet.of(
() -> new GeneralSpliteratorOfObject<>(spliteratorSupplier.get()),
() -> new GeneralSpliteratorOfPrimitive<>(spliteratorSupplier.get(), c -> c::accept)));
}

/**
* @since NEXT (but since 28.1 in the JRE flavor)
*/
public static SpliteratorTester<Double> ofDouble(
Supplier<Spliterator.OfDouble> spliteratorSupplier) {
return new SpliteratorTester<>(
ImmutableSet.of(
() -> new GeneralSpliteratorOfObject<>(spliteratorSupplier.get()),
() -> new GeneralSpliteratorOfPrimitive<>(spliteratorSupplier.get(), c -> c::accept)));
}

private final ImmutableSet<Supplier<GeneralSpliterator<E>>> spliteratorSuppliers;

private SpliteratorTester(ImmutableSet<Supplier<GeneralSpliterator<E>>> spliteratorSuppliers) {
this.spliteratorSuppliers = checkNotNull(spliteratorSuppliers);
}

@SafeVarargs
@CanIgnoreReturnValue
public final Ordered expect(Object... elements) {
return expect(asList(elements));
}

@CanIgnoreReturnValue
public final Ordered expect(Iterable<?> elements) {
List<List<E>> resultsForAllStrategies = new ArrayList<>();
for (Supplier<GeneralSpliterator<E>> spliteratorSupplier : spliteratorSuppliers) {
GeneralSpliterator<E> spliterator = spliteratorSupplier.get();
int characteristics = spliterator.characteristics();
long estimatedSize = spliterator.estimateSize();
for (SpliteratorDecompositionStrategy strategy :
SpliteratorDecompositionStrategy.ALL_STRATEGIES) {
List<E> resultsForStrategy = new ArrayList<>();
strategy.forEach(spliteratorSupplier.get(), resultsForStrategy::add);

// TODO(cpovirk): better failure messages
if ((characteristics & Spliterator.NONNULL) != 0) {
assertFalse(resultsForStrategy.contains(null));
}
if ((characteristics & Spliterator.SORTED) != 0) {
Comparator<? super E> comparator = spliterator.getComparator();
if (comparator == null) {
// A sorted spliterator with no comparator is already using natural order.
// (We could probably find a way to avoid rawtypes here if we wanted.)
@SuppressWarnings({"unchecked", "rawtypes"})
Comparator<? super E> naturalOrder =
(Comparator<? super E>) Comparator.<Comparable>naturalOrder();
comparator = naturalOrder;
}
assertTrue(Ordering.from(comparator).isOrdered(resultsForStrategy));
}
if ((characteristics & Spliterator.SIZED) != 0) {
assertEquals(Ints.checkedCast(estimatedSize), resultsForStrategy.size());
}

assertEqualIgnoringOrder(elements, resultsForStrategy);
resultsForAllStrategies.add(resultsForStrategy);
}
}
return new Ordered() {
@Override
public void inOrder() {
for (List<E> resultsForStrategy : resultsForAllStrategies) {
assertEqualInOrder(elements, resultsForStrategy);
}
}
};
}
}
Loading

0 comments on commit 6ace8bc

Please sign in to comment.