update to 30.1

This commit is contained in:
Jörg Prante 2021-03-18 22:19:14 +01:00
parent bb8e73e79a
commit bc85508c04
81 changed files with 4978 additions and 1367 deletions

View file

@ -24,7 +24,9 @@ ext {
} }
apply plugin: 'java-library' apply plugin: 'java-library'
apply from: rootProject.file('gradle/init/banner.gradle')
apply from: rootProject.file('gradle/ide/idea.gradle') apply from: rootProject.file('gradle/ide/idea.gradle')
apply from: rootProject.file('gradle/compile/repo.gradle')
apply from: rootProject.file('gradle/compile/java.gradle') apply from: rootProject.file('gradle/compile/java.gradle')
apply from: rootProject.file('gradle/test/junit5.gradle') apply from: rootProject.file('gradle/test/junit5.gradle')
apply from: rootProject.file('gradle/publishing/publication.gradle') apply from: rootProject.file('gradle/publishing/publication.gradle')

View file

@ -1,5 +1,5 @@
group = org.xbib group = org.xbib
name = guava name = guava
version = 29.0 version = 30.1
gradle.wrapper.version = 6.6.1 gradle.wrapper.version = 6.6.1

View file

@ -17,9 +17,7 @@ compileTestJava {
jar { jar {
manifest { manifest {
attributes('Implementation-Title': project.name)
attributes('Implementation-Version': project.version) attributes('Implementation-Version': project.version)
attributes('Implementation-Vendor': 'Jörg Prante')
} }
} }
@ -30,8 +28,18 @@ task sourcesJar(type: Jar, dependsOn: classes) {
task javadocJar(type: Jar, dependsOn: javadoc) { task javadocJar(type: Jar, dependsOn: javadoc) {
classifier 'javadoc' classifier 'javadoc'
from javadoc.destinationDir
} }
artifacts { artifacts {
archives sourcesJar, javadocJar archives sourcesJar, javadocJar
} }
tasks.withType(JavaCompile) {
options.encoding('UTF-8')
options.compilerArgs << '-Xlint:all'
}
javadoc {
options.addStringOption('Xdoclint:none', '-quiet')
}

View file

@ -0,0 +1,4 @@
repositories {
mavenLocal()
mavenCentral()
}

View file

@ -7,7 +7,6 @@ idea {
} }
} }
// if project was not imported via idea pluginw
clean { clean {
delete 'out' delete 'out'
} }

14
gradle/init/banner.gradle Normal file
View file

@ -0,0 +1,14 @@
gradle.projectsLoaded { g ->
printf "Host: %s\nOS: %s %s %s\nJVM: %s %s %s %s\nGradle: %s Groovy: %s Java: %s\n" +
"Build: group: %s name: %s version: %s\n",
InetAddress.getLocalHost(),
System.getProperty("os.name"),
System.getProperty("os.arch"),
System.getProperty("os.version"),
System.getProperty("java.version"),
System.getProperty("java.vm.version"),
System.getProperty("java.vm.vendor"),
System.getProperty("java.vm.name"),
gradle.gradleVersion, GroovySystem.getVersion(), JavaVersion.current(),
gradle.rootProject.group, gradle.rootProject.name, gradle.rootProject.version
}

View file

@ -53,12 +53,14 @@ if (project.hasProperty("signing.keyId")) {
} }
} }
nexusPublishing { if (project.hasProperty("ossrhUsername")) {
repositories { nexusPublishing {
sonatype { repositories {
username = project.property('ossrhUsername') sonatype {
password = project.property('ossrhPassword') username = project.property('ossrhUsername')
packageGroup = "org.xbib" password = project.property('ossrhPassword')
packageGroup = "org.xbib"
}
} }
} }
} }

Binary file not shown.

View file

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

2
gradlew vendored
View file

@ -130,7 +130,7 @@ fi
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"` APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"` JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath # We build the pattern for arguments to be converted via cygpath

21
gradlew.bat vendored
View file

@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init if "%ERRORLEVEL%" == "0" goto execute
echo. echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -54,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=% set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init if exist "%JAVA_EXE%" goto execute
echo. echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@ -64,21 +64,6 @@ echo location of your Java installation.
goto fail goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute :execute
@rem Setup the command line @rem Setup the command line
@ -86,7 +71,7 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell

View file

@ -79,7 +79,15 @@ public enum StandardSystemProperty {
/** Name of JIT compiler to use. */ /** Name of JIT compiler to use. */
JAVA_COMPILER("java.compiler"), JAVA_COMPILER("java.compiler"),
/** Path of extension directory or directories. */ /**
* Path of extension directory or directories.
*
* @deprecated This property was <a
* href="https://openjdk.java.net/jeps/220#Removed:-The-extension-mechanism">deprecated</a> in
* Java 8 and removed in Java 9. We do not plan to remove this API from Guava, but if you are
* using it, it is probably not doing what you want.
*/
@Deprecated
JAVA_EXT_DIRS("java.ext.dirs"), JAVA_EXT_DIRS("java.ext.dirs"),
/** Operating system name. */ /** Operating system name. */

View file

@ -480,8 +480,8 @@ public final class CacheBuilder<K, V> {
this.maximumWeight); this.maximumWeight);
checkState( checkState(
this.maximumSize == UNSET_INT, "maximum size was already set to %s", this.maximumSize); this.maximumSize == UNSET_INT, "maximum size was already set to %s", this.maximumSize);
this.maximumWeight = maximumWeight;
checkArgument(maximumWeight >= 0, "maximum weight must not be negative"); checkArgument(maximumWeight >= 0, "maximum weight must not be negative");
this.maximumWeight = maximumWeight;
return this; return this;
} }

View file

@ -35,7 +35,6 @@ import com.google.common.cache.CacheBuilder.NullListener;
import com.google.common.cache.CacheBuilder.OneWeigher; import com.google.common.cache.CacheBuilder.OneWeigher;
import com.google.common.cache.CacheLoader.InvalidCacheLoadException; import com.google.common.cache.CacheLoader.InvalidCacheLoadException;
import com.google.common.cache.CacheLoader.UnsupportedLoadingOperationException; import com.google.common.cache.CacheLoader.UnsupportedLoadingOperationException;
import com.google.common.cache.LocalCache.AbstractCacheSet;
import com.google.common.collect.AbstractSequentialIterator; import com.google.common.collect.AbstractSequentialIterator;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
@ -4284,7 +4283,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>
public Set<K> keySet() { public Set<K> keySet() {
// does not impact recency ordering // does not impact recency ordering
Set<K> ks = keySet; Set<K> ks = keySet;
return (ks != null) ? ks : (keySet = new KeySet(this)); return (ks != null) ? ks : (keySet = new KeySet());
} }
Collection<V> values; Collection<V> values;
@ -4293,7 +4292,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>
public Collection<V> values() { public Collection<V> values() {
// does not impact recency ordering // does not impact recency ordering
Collection<V> vs = values; Collection<V> vs = values;
return (vs != null) ? vs : (values = new Values(this)); return (vs != null) ? vs : (values = new Values());
} }
Set<Entry<K, V>> entrySet; Set<Entry<K, V>> entrySet;
@ -4303,7 +4302,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>
public Set<Entry<K, V>> entrySet() { public Set<Entry<K, V>> entrySet() {
// does not impact recency ordering // does not impact recency ordering
Set<Entry<K, V>> es = entrySet; Set<Entry<K, V>> es = entrySet;
return (es != null) ? es : (entrySet = new EntrySet(this)); return (es != null) ? es : (entrySet = new EntrySet());
} }
// Iterator Support // Iterator Support
@ -4494,25 +4493,20 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>
} }
abstract class AbstractCacheSet<T> extends AbstractSet<T> { abstract class AbstractCacheSet<T> extends AbstractSet<T> {
final ConcurrentMap<?, ?> map;
AbstractCacheSet(ConcurrentMap<?, ?> map) {
this.map = map;
}
@Override @Override
public int size() { public int size() {
return map.size(); return LocalCache.this.size();
} }
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {
return map.isEmpty(); return LocalCache.this.isEmpty();
} }
@Override @Override
public void clear() { public void clear() {
map.clear(); LocalCache.this.clear();
} }
// super.toArray() may misbehave if size() is inaccurate, at least on old versions of Android. // super.toArray() may misbehave if size() is inaccurate, at least on old versions of Android.
@ -4556,10 +4550,6 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>
final class KeySet extends AbstractCacheSet<K> { final class KeySet extends AbstractCacheSet<K> {
KeySet(ConcurrentMap<?, ?> map) {
super(map);
}
@Override @Override
public Iterator<K> iterator() { public Iterator<K> iterator() {
return new KeyIterator(); return new KeyIterator();
@ -4567,36 +4557,31 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>
@Override @Override
public boolean contains(Object o) { public boolean contains(Object o) {
return map.containsKey(o); return LocalCache.this.containsKey(o);
} }
@Override @Override
public boolean remove(Object o) { public boolean remove(Object o) {
return map.remove(o) != null; return LocalCache.this.remove(o) != null;
} }
} }
final class Values extends AbstractCollection<V> { final class Values extends AbstractCollection<V> {
private final ConcurrentMap<?, ?> map;
Values(ConcurrentMap<?, ?> map) {
this.map = map;
}
@Override @Override
public int size() { public int size() {
return map.size(); return LocalCache.this.size();
} }
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {
return map.isEmpty(); return LocalCache.this.isEmpty();
} }
@Override @Override
public void clear() { public void clear() {
map.clear(); LocalCache.this.clear();
} }
@Override @Override
@ -4612,7 +4597,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>
@Override @Override
public boolean contains(Object o) { public boolean contains(Object o) {
return map.containsValue(o); return LocalCache.this.containsValue(o);
} }
// super.toArray() may misbehave if size() is inaccurate, at least on old versions of Android. // super.toArray() may misbehave if size() is inaccurate, at least on old versions of Android.
@ -4632,10 +4617,6 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>
final class EntrySet extends AbstractCacheSet<Entry<K, V>> { final class EntrySet extends AbstractCacheSet<Entry<K, V>> {
EntrySet(ConcurrentMap<?, ?> map) {
super(map);
}
@Override @Override
public Iterator<Entry<K, V>> iterator() { public Iterator<Entry<K, V>> iterator() {
return new EntryIterator(); return new EntryIterator();

View file

@ -88,6 +88,28 @@ final class CartesianList<E> extends AbstractList<List<E>> implements RandomAcce
return computedIndex; return computedIndex;
} }
@Override
public int lastIndexOf(Object o) {
if (!(o instanceof List)) {
return -1;
}
List<?> list = (List<?>) o;
if (list.size() != axes.size()) {
return -1;
}
ListIterator<?> itr = list.listIterator();
int computedIndex = 0;
while (itr.hasNext()) {
int axisIndex = itr.nextIndex();
int elemIndex = axes.get(axisIndex).lastIndexOf(itr.next());
if (elemIndex == -1) {
return -1;
}
computedIndex += elemIndex * axesSizeProduct[axisIndex + 1];
}
return computedIndex;
}
@Override @Override
public ImmutableList<E> get(final int index) { public ImmutableList<E> get(final int index) {
checkElementIndex(index, size()); checkElementIndex(index, size());
@ -117,8 +139,21 @@ final class CartesianList<E> extends AbstractList<List<E>> implements RandomAcce
return axesSizeProduct[0]; return axesSizeProduct[0];
} }
@Override public boolean contains(Object object) {
public boolean contains(Object o) { if (!(object instanceof List)) {
return indexOf(o) != -1; return false;
}
List<?> list = (List<?>) object;
if (list.size() != axes.size()) {
return false;
}
int i = 0;
for (Object o : list) {
if (!axes.get(i).contains(o)) {
return false;
}
i++;
}
return true;
} }
} }

View file

@ -20,58 +20,291 @@ import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import java.util.Collection;
import java.util.Comparator; import java.util.Comparator;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.TreeMap;
import java.util.function.BinaryOperator;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.ToIntFunction;
import java.util.stream.Collector; import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/** Collectors utilities for {@code common.collect} internals. */ /** Collectors utilities for {@code common.collect} internals. */
@GwtCompatible @GwtCompatible
final class CollectCollectors { final class CollectCollectors {
static <T, K, V> Collector<T, ?, ImmutableBiMap<K, V>> toImmutableBiMap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction) {
checkNotNull(keyFunction);
checkNotNull(valueFunction);
return Collector.of(
ImmutableBiMap.Builder<K, V>::new,
(builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)),
ImmutableBiMap.Builder::combine,
ImmutableBiMap.Builder::build,
new Collector.Characteristics[0]);
}
private static final Collector<Object, ?, ImmutableList<Object>> TO_IMMUTABLE_LIST = private static final Collector<Object, ?, ImmutableList<Object>> TO_IMMUTABLE_LIST =
Collector.of( Collector.of(
ImmutableList::<Object>builder, ImmutableList::builder,
ImmutableList.Builder::add, ImmutableList.Builder::add,
ImmutableList.Builder::combine, ImmutableList.Builder::combine,
ImmutableList.Builder::build); ImmutableList.Builder::build);
private static final Collector<Object, ?, ImmutableSet<Object>> TO_IMMUTABLE_SET =
Collector.of(
ImmutableSet::builder,
ImmutableSet.Builder::add,
ImmutableSet.Builder::combine,
ImmutableSet.Builder::build);
@GwtIncompatible
private static final Collector<Range<Comparable<?>>, ?, ImmutableRangeSet<Comparable<?>>>
TO_IMMUTABLE_RANGE_SET =
Collector.of(
ImmutableRangeSet::builder,
ImmutableRangeSet.Builder::add,
ImmutableRangeSet.Builder::combine,
ImmutableRangeSet.Builder::build);
// Lists
@SuppressWarnings({"rawtypes", "unchecked"})
static <E> Collector<E, ?, ImmutableList<E>> toImmutableList() { static <E> Collector<E, ?, ImmutableList<E>> toImmutableList() {
return (Collector) TO_IMMUTABLE_LIST; return (Collector) TO_IMMUTABLE_LIST;
} }
// Sets
@SuppressWarnings({"rawtypes", "unchecked"})
static <E> Collector<E, ?, ImmutableSet<E>> toImmutableSet() {
return (Collector) TO_IMMUTABLE_SET;
}
static <E> Collector<E, ?, ImmutableSortedSet<E>> toImmutableSortedSet(
Comparator<? super E> comparator) {
checkNotNull(comparator);
return Collector.of(
() -> new ImmutableSortedSet.Builder<E>(comparator),
ImmutableSortedSet.Builder::add,
ImmutableSortedSet.Builder::combine,
ImmutableSortedSet.Builder::build);
}
@SuppressWarnings({"rawtypes", "unchecked"})
static <E extends Enum<E>> Collector<E, ?, ImmutableSet<E>> toImmutableEnumSet() {
return (Collector) EnumSetAccumulator.TO_IMMUTABLE_ENUM_SET;
}
private static final class EnumSetAccumulator<E extends Enum<E>> {
@SuppressWarnings({"rawtypes", "unchecked"})
static final Collector<Enum<?>, ?, ImmutableSet<? extends Enum<?>>> TO_IMMUTABLE_ENUM_SET =
(Collector)
Collector.<Enum, EnumSetAccumulator, ImmutableSet<?>>of(
EnumSetAccumulator::new,
EnumSetAccumulator::add,
EnumSetAccumulator::combine,
EnumSetAccumulator::toImmutableSet,
Collector.Characteristics.UNORDERED);
private EnumSet<E> set;
void add(E e) {
if (set == null) {
set = EnumSet.of(e);
} else {
set.add(e);
}
}
EnumSetAccumulator<E> combine(EnumSetAccumulator<E> other) {
if (this.set == null) {
return other;
} else if (other.set == null) {
return this;
} else {
this.set.addAll(other.set);
return this;
}
}
ImmutableSet<E> toImmutableSet() {
return (set == null) ? ImmutableSet.<E>of() : ImmutableEnumSet.asImmutable(set);
}
}
@GwtIncompatible
@SuppressWarnings({"rawtypes", "unchecked"})
static <E extends Comparable<? super E>>
Collector<Range<E>, ?, ImmutableRangeSet<E>> toImmutableRangeSet() {
return (Collector) TO_IMMUTABLE_RANGE_SET;
}
// Multisets
static <T, E> Collector<T, ?, ImmutableMultiset<E>> toImmutableMultiset(
Function<? super T, ? extends E> elementFunction, ToIntFunction<? super T> countFunction) {
checkNotNull(elementFunction);
checkNotNull(countFunction);
return Collector.of(
LinkedHashMultiset::create,
(multiset, t) ->
multiset.add(checkNotNull(elementFunction.apply(t)), countFunction.applyAsInt(t)),
(multiset1, multiset2) -> {
multiset1.addAll(multiset2);
return multiset1;
},
(Multiset<E> multiset) -> ImmutableMultiset.copyFromEntries(multiset.entrySet()));
}
static <T, E, M extends Multiset<E>> Collector<T, ?, M> toMultiset(
java.util.function.Function<? super T, E> elementFunction,
java.util.function.ToIntFunction<? super T> countFunction,
java.util.function.Supplier<M> multisetSupplier) {
checkNotNull(elementFunction);
checkNotNull(countFunction);
checkNotNull(multisetSupplier);
return Collector.of(
multisetSupplier,
(ms, t) -> ms.add(elementFunction.apply(t), countFunction.applyAsInt(t)),
(ms1, ms2) -> {
ms1.addAll(ms2);
return ms1;
});
}
// Maps
static <T, K, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableMap( static <T, K, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableMap(
Function<? super T, ? extends K> keyFunction, Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction) { Function<? super T, ? extends V> valueFunction) {
checkNotNull(keyFunction); checkNotNull(keyFunction);
checkNotNull(valueFunction); checkNotNull(valueFunction);
return Collector.of( return Collector.of(
ImmutableMap.Builder<K, V>::new, ImmutableMap.Builder<K, V>::new,
(builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)), (builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)),
ImmutableMap.Builder::combine, ImmutableMap.Builder::combine,
ImmutableMap.Builder::build); ImmutableMap.Builder::build);
} }
private static final Collector<Object, ?, ImmutableSet<Object>> TO_IMMUTABLE_SET = public static <T, K, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableMap(
Collector.of( Function<? super T, ? extends K> keyFunction,
ImmutableSet::<Object>builder, Function<? super T, ? extends V> valueFunction,
ImmutableSet.Builder::add, BinaryOperator<V> mergeFunction) {
ImmutableSet.Builder::combine, checkNotNull(keyFunction);
ImmutableSet.Builder::build); checkNotNull(valueFunction);
checkNotNull(mergeFunction);
return Collectors.collectingAndThen(
Collectors.toMap(keyFunction, valueFunction, mergeFunction, LinkedHashMap::new),
ImmutableMap::copyOf);
}
static <E> Collector<E, ?, ImmutableSet<E>> toImmutableSet() { static <T, K, V> Collector<T, ?, ImmutableSortedMap<K, V>> toImmutableSortedMap(
return (Collector) TO_IMMUTABLE_SET; Comparator<? super K> comparator,
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction,
BinaryOperator<V> mergeFunction) {
checkNotNull(comparator);
checkNotNull(keyFunction);
checkNotNull(valueFunction);
checkNotNull(mergeFunction);
return Collectors.collectingAndThen(
Collectors.toMap(
keyFunction, valueFunction, mergeFunction, () -> new TreeMap<K, V>(comparator)),
ImmutableSortedMap::copyOfSorted);
}
static <T, K, V> Collector<T, ?, ImmutableBiMap<K, V>> toImmutableBiMap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction) {
checkNotNull(keyFunction);
checkNotNull(valueFunction);
return Collector.of(
ImmutableBiMap.Builder<K, V>::new,
(builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)),
ImmutableBiMap.Builder::combine,
ImmutableBiMap.Builder::build,
new Collector.Characteristics[0]);
}
static <T, K extends Enum<K>, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableEnumMap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction) {
checkNotNull(keyFunction);
checkNotNull(valueFunction);
return Collector.of(
() ->
new EnumMapAccumulator<K, V>(
(v1, v2) -> {
throw new IllegalArgumentException("Multiple values for key: " + v1 + ", " + v2);
}),
(accum, t) -> {
K key = checkNotNull(keyFunction.apply(t), "Null key for input %s", t);
V newValue = checkNotNull(valueFunction.apply(t), "Null value for input %s", t);
accum.put(key, newValue);
},
EnumMapAccumulator::combine,
EnumMapAccumulator::toImmutableMap,
Collector.Characteristics.UNORDERED);
}
static <T, K extends Enum<K>, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableEnumMap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction,
BinaryOperator<V> mergeFunction) {
checkNotNull(keyFunction);
checkNotNull(valueFunction);
checkNotNull(mergeFunction);
// not UNORDERED because we don't know if mergeFunction is commutative
return Collector.of(
() -> new EnumMapAccumulator<K, V>(mergeFunction),
(accum, t) -> {
K key = checkNotNull(keyFunction.apply(t), "Null key for input %s", t);
V newValue = checkNotNull(valueFunction.apply(t), "Null value for input %s", t);
accum.put(key, newValue);
},
EnumMapAccumulator::combine,
EnumMapAccumulator::toImmutableMap);
}
private static class EnumMapAccumulator<K extends Enum<K>, V> {
private final BinaryOperator<V> mergeFunction;
private EnumMap<K, V> map = null;
EnumMapAccumulator(BinaryOperator<V> mergeFunction) {
this.mergeFunction = mergeFunction;
}
void put(K key, V value) {
if (map == null) {
map = new EnumMap<>(key.getDeclaringClass());
}
map.merge(key, value, mergeFunction);
}
EnumMapAccumulator<K, V> combine(EnumMapAccumulator<K, V> other) {
if (this.map == null) {
return other;
} else if (other.map == null) {
return this;
} else {
other.map.forEach(this::put);
return this;
}
}
ImmutableMap<K, V> toImmutableMap() {
return (map == null) ? ImmutableMap.<K, V>of() : ImmutableEnumMap.asImmutable(map);
}
}
@GwtIncompatible
static <T, K extends Comparable<? super K>, V>
Collector<T, ?, ImmutableRangeMap<K, V>> toImmutableRangeMap(
Function<? super T, Range<K>> keyFunction,
Function<? super T, ? extends V> valueFunction) {
checkNotNull(keyFunction);
checkNotNull(valueFunction);
return Collector.of(
ImmutableRangeMap::<K, V>builder,
(builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)),
ImmutableRangeMap.Builder::combine,
ImmutableRangeMap.Builder::build);
} }
static <T, K, V> Collector<T, ?, ImmutableSortedMap<K, V>> toImmutableSortedMap( static <T, K, V> Collector<T, ?, ImmutableSortedMap<K, V>> toImmutableSortedMap(
@ -92,43 +325,91 @@ final class CollectCollectors {
ImmutableSortedMap.Builder::build, ImmutableSortedMap.Builder::build,
Collector.Characteristics.UNORDERED); Collector.Characteristics.UNORDERED);
} }
// Multimaps
static <E> Collector<E, ?, ImmutableSortedSet<E>> toImmutableSortedSet( static <T, K, V> Collector<T, ?, ImmutableListMultimap<K, V>> toImmutableListMultimap(
Comparator<? super E> comparator) { Function<? super T, ? extends K> keyFunction,
checkNotNull(comparator);
return Collector.of(
() -> new ImmutableSortedSet.Builder<E>(comparator),
ImmutableSortedSet.Builder::add,
ImmutableSortedSet.Builder::combine,
ImmutableSortedSet.Builder::build);
}
@GwtIncompatible
private static final Collector<Range<Comparable>, ?, ImmutableRangeSet<Comparable>>
TO_IMMUTABLE_RANGE_SET =
Collector.of(
ImmutableRangeSet::<Comparable>builder,
ImmutableRangeSet.Builder::add,
ImmutableRangeSet.Builder::combine,
ImmutableRangeSet.Builder::build);
@GwtIncompatible
static <E extends Comparable<? super E>>
Collector<Range<E>, ?, ImmutableRangeSet<E>> toImmutableRangeSet() {
return (Collector) TO_IMMUTABLE_RANGE_SET;
}
@GwtIncompatible
static <T, K extends Comparable<? super K>, V>
Collector<T, ?, ImmutableRangeMap<K, V>> toImmutableRangeMap(
Function<? super T, Range<K>> keyFunction,
Function<? super T, ? extends V> valueFunction) { Function<? super T, ? extends V> valueFunction) {
checkNotNull(keyFunction, "keyFunction");
checkNotNull(valueFunction, "valueFunction");
return Collector.of(
ImmutableListMultimap::<K, V>builder,
(builder, t) -> builder.put(keyFunction.apply(t), valueFunction.apply(t)),
ImmutableListMultimap.Builder::combine,
ImmutableListMultimap.Builder::build);
}
static <T, K, V> Collector<T, ?, ImmutableListMultimap<K, V>> flatteningToImmutableListMultimap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends Stream<? extends V>> valuesFunction) {
checkNotNull(keyFunction);
checkNotNull(valuesFunction);
return Collectors.collectingAndThen(
flatteningToMultimap(
input -> checkNotNull(keyFunction.apply(input)),
input -> valuesFunction.apply(input).peek(Preconditions::checkNotNull),
MultimapBuilder.linkedHashKeys().arrayListValues()::<K, V>build),
ImmutableListMultimap::copyOf);
}
static <T, K, V> Collector<T, ?, ImmutableSetMultimap<K, V>> toImmutableSetMultimap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction) {
checkNotNull(keyFunction, "keyFunction");
checkNotNull(valueFunction, "valueFunction");
return Collector.of(
ImmutableSetMultimap::<K, V>builder,
(builder, t) -> builder.put(keyFunction.apply(t), valueFunction.apply(t)),
ImmutableSetMultimap.Builder::combine,
ImmutableSetMultimap.Builder::build);
}
static <T, K, V> Collector<T, ?, ImmutableSetMultimap<K, V>> flatteningToImmutableSetMultimap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends Stream<? extends V>> valuesFunction) {
checkNotNull(keyFunction);
checkNotNull(valuesFunction);
return Collectors.collectingAndThen(
flatteningToMultimap(
input -> checkNotNull(keyFunction.apply(input)),
input -> valuesFunction.apply(input).peek(Preconditions::checkNotNull),
MultimapBuilder.linkedHashKeys().linkedHashSetValues()::<K, V>build),
ImmutableSetMultimap::copyOf);
}
static <T, K, V, M extends Multimap<K, V>> Collector<T, ?, M> toMultimap(
java.util.function.Function<? super T, ? extends K> keyFunction,
java.util.function.Function<? super T, ? extends V> valueFunction,
java.util.function.Supplier<M> multimapSupplier) {
checkNotNull(keyFunction); checkNotNull(keyFunction);
checkNotNull(valueFunction); checkNotNull(valueFunction);
checkNotNull(multimapSupplier);
return Collector.of( return Collector.of(
ImmutableRangeMap::<K, V>builder, multimapSupplier,
(builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)), (multimap, input) -> multimap.put(keyFunction.apply(input), valueFunction.apply(input)),
ImmutableRangeMap.Builder::combine, (multimap1, multimap2) -> {
ImmutableRangeMap.Builder::build); multimap1.putAll(multimap2);
return multimap1;
});
}
static <T, K, V, M extends Multimap<K, V>> Collector<T, ?, M> flatteningToMultimap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends Stream<? extends V>> valueFunction,
Supplier<M> multimapSupplier) {
checkNotNull(keyFunction);
checkNotNull(valueFunction);
checkNotNull(multimapSupplier);
return Collector.of(
multimapSupplier,
(multimap, input) -> {
K key = keyFunction.apply(input);
Collection<V> valuesForKey = multimap.get(key);
valueFunction.apply(input).forEachOrdered(valuesForKey::add);
},
(multimap1, multimap2) -> {
multimap1.putAll(multimap2);
return multimap1;
});
} }
} }

View file

@ -352,11 +352,6 @@ public final class Collections2 {
return new StringBuilder((int) Math.min(size * 8L, Ints.MAX_POWER_OF_TWO)); return new StringBuilder((int) Math.min(size * 8L, Ints.MAX_POWER_OF_TWO));
} }
/** Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 */
static <T> Collection<T> cast(Iterable<T> iterable) {
return (Collection<T>) iterable;
}
/** /**
* Returns a {@link Collection} of all the permutations of the specified {@link Iterable}. * Returns a {@link Collection} of all the permutations of the specified {@link Iterable}.
* *

View file

@ -190,4 +190,79 @@ public final class Comparators {
checkNotNull(valueComparator); checkNotNull(valueComparator);
return Comparator.comparing(o -> o.orElse(null), Comparator.nullsLast(valueComparator)); return Comparator.comparing(o -> o.orElse(null), Comparator.nullsLast(valueComparator));
} }
/**
* Returns the minimum of the two values. If the values compare as 0, the first is returned.
*
* <p>The recommended solution for finding the {@code minimum} of some values depends on the type
* of your data and the number of elements you have. Read more in the Guava User Guide article on
* <a href="https://github.com/google/guava/wiki/CollectionUtilitiesExplained#comparators">{@code
* Comparators}</a>.
*
* @param a first value to compare, returned if less than or equal to b.
* @param b second value to compare.
* @throws ClassCastException if the parameters are not <i>mutually comparable</i>.
* @since 30.0
*/
@Beta
public static <T extends Comparable<? super T>> T min(T a, T b) {
return (a.compareTo(b) <= 0) ? a : b;
}
/**
* Returns the minimum of the two values, according to the given comparator. If the values compare
* as equal, the first is returned.
*
* <p>The recommended solution for finding the {@code minimum} of some values depends on the type
* of your data and the number of elements you have. Read more in the Guava User Guide article on
* <a href="https://github.com/google/guava/wiki/CollectionUtilitiesExplained#comparators">{@code
* Comparators}</a>.
*
* @param a first value to compare, returned if less than or equal to b
* @param b second value to compare.
* @throws ClassCastException if the parameters are not <i>mutually comparable</i> using the given
* comparator.
* @since 30.0
*/
@Beta
public static <T> T min(T a, T b, Comparator<T> comparator) {
return (comparator.compare(a, b) <= 0) ? a : b;
}
/**
* Returns the maximum of the two values. If the values compare as 0, the first is returned.
*
* <p>The recommended solution for finding the {@code maximum} of some values depends on the type
* of your data and the number of elements you have. Read more in the Guava User Guide article on
* <a href="https://github.com/google/guava/wiki/CollectionUtilitiesExplained#comparators">{@code
* Comparators}</a>.
*
* @param a first value to compare, returned if greater than or equal to b.
* @param b second value to compare.
* @throws ClassCastException if the parameters are not <i>mutually comparable</i>.
* @since 30.0
*/
@Beta
public static <T extends Comparable<? super T>> T max(T a, T b) {
return (a.compareTo(b) >= 0) ? a : b;
}
/**
* Returns the maximum of the two values, according to the given comparator. If the values compare
* as equal, the first is returned.
*
* <p>The recommended solution for finding the {@code maximum} of some values depends on the type
* of your data and the number of elements you have. Read more in the Guava User Guide article on
* <a href="https://github.com/google/guava/wiki/CollectionUtilitiesExplained#comparators">{@code
* Comparators}</a>.
*
* @param a first value to compare, returned if greater than or equal to b.
* @param b second value to compare.
* @throws ClassCastException if the parameters are not <i>mutually comparable</i> using the given
* comparator.
* @since 30.0
*/
@Beta
public static <T> T max(T a, T b, Comparator<T> comparator) {
return (comparator.compare(a, b) >= 0) ? a : b;
}
} }

View file

@ -191,15 +191,14 @@ public abstract class ContiguousSet<C extends Comparable> extends ImmutableSorte
/* /*
* These methods perform most headSet, subSet, and tailSet logic, besides parameter validation. * These methods perform most headSet, subSet, and tailSet logic, besides parameter validation.
*/ */
// TODO(kevinb): we can probably make these real @Overrides now @SuppressWarnings("MissingOverride") // Supermethod does not exist under GWT.
/* @Override */
abstract ContiguousSet<C> headSetImpl(C toElement, boolean inclusive); abstract ContiguousSet<C> headSetImpl(C toElement, boolean inclusive);
/* @Override */ @SuppressWarnings("MissingOverride") // Supermethod does not exist under GWT.
abstract ContiguousSet<C> subSetImpl( abstract ContiguousSet<C> subSetImpl(
C fromElement, boolean fromInclusive, C toElement, boolean toInclusive); C fromElement, boolean fromInclusive, C toElement, boolean toInclusive);
/* @Override */ @SuppressWarnings("MissingOverride") // Supermethod does not exist under GWT.
abstract ContiguousSet<C> tailSetImpl(C fromElement, boolean inclusive); abstract ContiguousSet<C> tailSetImpl(C fromElement, boolean inclusive);
/** /**

View file

@ -786,7 +786,7 @@ public abstract class FluentIterable<E> implements Iterable<E> {
checkNotNull(collection); checkNotNull(collection);
Iterable<E> iterable = getDelegate(); Iterable<E> iterable = getDelegate();
if (iterable instanceof Collection) { if (iterable instanceof Collection) {
collection.addAll(Collections2.cast(iterable)); collection.addAll((Collection<E>) iterable);
} else { } else {
for (E item : iterable) { for (E item : iterable) {
collection.add(item); collection.add(item);

View file

@ -76,8 +76,8 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject implements Ma
@Override @Override
public V remove(Object object) { public V remove(Object key) {
return delegate().remove(object); return delegate().remove(key);
} }
@Override @Override

View file

@ -413,15 +413,14 @@ public abstract class ImmutableBiMap<K, V> extends ImmutableBiMapFauxverideShim<
* <p>Since the bimap is immutable, ImmutableBiMap doesn't require special logic for keeping the * <p>Since the bimap is immutable, ImmutableBiMap doesn't require special logic for keeping the
* bimap and its inverse in sync during serialization, the way AbstractBiMap does. * bimap and its inverse in sync during serialization, the way AbstractBiMap does.
*/ */
private static class SerializedForm extends ImmutableMap.SerializedForm { private static class SerializedForm<K, V> extends ImmutableMap.SerializedForm<K, V> {
SerializedForm(ImmutableBiMap<?, ?> bimap) { SerializedForm(ImmutableBiMap<K, V> bimap) {
super(bimap); super(bimap);
} }
@Override @Override
Object readResolve() { Builder<K, V> makeBuilder(int size) {
Builder<Object, Object> builder = new Builder<>(); return new Builder<>();
return createMap(builder);
} }
private static final long serialVersionUID = 0; private static final long serialVersionUID = 0;
@ -429,6 +428,6 @@ public abstract class ImmutableBiMap<K, V> extends ImmutableBiMapFauxverideShim<
@Override @Override
Object writeReplace() { Object writeReplace() {
return new SerializedForm(this); return new SerializedForm<>(this);
} }
} }

View file

@ -35,7 +35,6 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collector; import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -83,13 +82,7 @@ public class ImmutableListMultimap<K, V> extends ImmutableMultimap<K, V>
public static <T, K, V> Collector<T, ?, ImmutableListMultimap<K, V>> toImmutableListMultimap( public static <T, K, V> Collector<T, ?, ImmutableListMultimap<K, V>> toImmutableListMultimap(
Function<? super T, ? extends K> keyFunction, Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction) { Function<? super T, ? extends V> valueFunction) {
checkNotNull(keyFunction, "keyFunction"); return CollectCollectors.toImmutableListMultimap(keyFunction, valueFunction);
checkNotNull(valueFunction, "valueFunction");
return Collector.of(
ImmutableListMultimap::<K, V>builder,
(builder, t) -> builder.put(keyFunction.apply(t), valueFunction.apply(t)),
ImmutableListMultimap.Builder::combine,
ImmutableListMultimap.Builder::build);
} }
/** /**
@ -127,14 +120,7 @@ public class ImmutableListMultimap<K, V> extends ImmutableMultimap<K, V>
Collector<T, ?, ImmutableListMultimap<K, V>> flatteningToImmutableListMultimap( Collector<T, ?, ImmutableListMultimap<K, V>> flatteningToImmutableListMultimap(
Function<? super T, ? extends K> keyFunction, Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends Stream<? extends V>> valuesFunction) { Function<? super T, ? extends Stream<? extends V>> valuesFunction) {
checkNotNull(keyFunction); return CollectCollectors.flatteningToImmutableListMultimap(keyFunction, valuesFunction);
checkNotNull(valuesFunction);
return Collectors.collectingAndThen(
Multimaps.flatteningToMultimap(
input -> checkNotNull(keyFunction.apply(input)),
input -> valuesFunction.apply(input).peek(Preconditions::checkNotNull),
MultimapBuilder.linkedHashKeys().arrayListValues()::<K, V>build),
ImmutableListMultimap::copyOf);
} }
/** Returns the empty multimap. */ /** Returns the empty multimap. */

View file

@ -36,7 +36,6 @@ import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.SortedMap; import java.util.SortedMap;
import java.util.Spliterator; import java.util.Spliterator;
@ -96,12 +95,7 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {
Function<? super T, ? extends K> keyFunction, Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction, Function<? super T, ? extends V> valueFunction,
BinaryOperator<V> mergeFunction) { BinaryOperator<V> mergeFunction) {
checkNotNull(keyFunction); return CollectCollectors.toImmutableMap(keyFunction, valueFunction, mergeFunction);
checkNotNull(valueFunction);
checkNotNull(mergeFunction);
return Collectors.collectingAndThen(
Collectors.toMap(keyFunction, valueFunction, mergeFunction, LinkedHashMap::new),
ImmutableMap::copyOf);
} }
/** /**
@ -889,37 +883,85 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {
* reconstructed using public factory methods. This ensures that the implementation types remain * reconstructed using public factory methods. This ensures that the implementation types remain
* as implementation details. * as implementation details.
*/ */
static class SerializedForm implements Serializable { static class SerializedForm<K, V> implements Serializable {
private final Object[] keys; // This object retains references to collections returned by keySet() and value(). This saves
private final Object[] values; // bytes when the both the map and its keySet or value collection are written to the same
// instance of ObjectOutputStream.
SerializedForm(ImmutableMap<?, ?> map) { // TODO(b/160980469): remove support for the old serialization format after some time
keys = new Object[map.size()]; private static final boolean USE_LEGACY_SERIALIZATION = true;
values = new Object[map.size()];
int i = 0; private final Object keys;
for (Entry<?, ?> entry : map.entrySet()) { private final Object values;
keys[i] = entry.getKey();
values[i] = entry.getValue(); SerializedForm(ImmutableMap<K, V> map) {
i++; if (USE_LEGACY_SERIALIZATION) {
Object[] keys = new Object[map.size()];
Object[] values = new Object[map.size()];
int i = 0;
for (Entry<?, ?> entry : map.entrySet()) {
keys[i] = entry.getKey();
values[i] = entry.getValue();
i++;
}
this.keys = keys;
this.values = values;
return;
} }
this.keys = map.keySet();
this.values = map.values();
} }
Object readResolve() { @SuppressWarnings("unchecked")
Builder<Object, Object> builder = new Builder<>(keys.length); final Object readResolve() {
return createMap(builder); if (!(this.keys instanceof ImmutableSet)) {
return legacyReadResolve();
}
ImmutableSet<K> keySet = (ImmutableSet<K>) this.keys;
ImmutableCollection<V> values = (ImmutableCollection<V>) this.values;
Builder<K, V> builder = makeBuilder(keySet.size());
UnmodifiableIterator<K> keyIter = keySet.iterator();
UnmodifiableIterator<V> valueIter = values.iterator();
while (keyIter.hasNext()) {
builder.put(keyIter.next(), valueIter.next());
}
return builder.build();
} }
Object createMap(Builder<Object, Object> builder) { @SuppressWarnings("unchecked")
final Object legacyReadResolve() {
K[] keys = (K[]) this.keys;
V[] values = (V[]) this.values;
Builder<K, V> builder = makeBuilder(keys.length);
for (int i = 0; i < keys.length; i++) { for (int i = 0; i < keys.length; i++) {
builder.put(keys[i], values[i]); builder.put(keys[i], values[i]);
} }
return builder.build(); return builder.build();
} }
/**
* Returns a builder that builds the unserialized type. Subclasses should override this method.
*/
Builder<K, V> makeBuilder(int size) {
return new Builder<>(size);
}
private static final long serialVersionUID = 0; private static final long serialVersionUID = 0;
} }
/**
* Returns a serializable form of this object. Non-public subclasses should not override this
* method. Publicly-accessible subclasses must override this method and should return a subclass
* of SerializedForm whose readResolve() method returns objects of the subclass type.
*/
Object writeReplace() { Object writeReplace() {
return new SerializedForm(this); return new SerializedForm<>(this);
} }
} }

View file

@ -75,12 +75,6 @@ final class ImmutableMapKeySet<K, V> extends IndexedImmutableSet<K> {
return true; return true;
} }
@GwtIncompatible // serialization
@Override
Object writeReplace() {
return new KeySetSerializedForm<K>(map);
}
@GwtIncompatible // serialization @GwtIncompatible // serialization
private static class KeySetSerializedForm<K> implements Serializable { private static class KeySetSerializedForm<K> implements Serializable {
final ImmutableMap<K, ?> map; final ImmutableMap<K, ?> map;

View file

@ -100,12 +100,6 @@ final class ImmutableMapValues<K, V> extends ImmutableCollection<V> {
map.forEach((k, v) -> action.accept(v)); map.forEach((k, v) -> action.accept(v));
} }
@GwtIncompatible // serialization
@Override
Object writeReplace() {
return new SerializedForm<V>(map);
}
@GwtIncompatible // serialization @GwtIncompatible // serialization
private static class SerializedForm<V> implements Serializable { private static class SerializedForm<V> implements Serializable {
final ImmutableMap<?, V> map; final ImmutableMap<?, V> map;

View file

@ -64,7 +64,7 @@ public abstract class ImmutableMultiset<E> extends ImmutableMultisetGwtSerializa
* @since 21.0 * @since 21.0
*/ */
public static <E> Collector<E, ?, ImmutableMultiset<E>> toImmutableMultiset() { public static <E> Collector<E, ?, ImmutableMultiset<E>> toImmutableMultiset() {
return toImmutableMultiset(Function.identity(), e -> 1); return CollectCollectors.toImmutableMultiset(Function.identity(), e -> 1);
} }
/** /**
@ -80,17 +80,7 @@ public abstract class ImmutableMultiset<E> extends ImmutableMultisetGwtSerializa
*/ */
public static <T, E> Collector<T, ?, ImmutableMultiset<E>> toImmutableMultiset( public static <T, E> Collector<T, ?, ImmutableMultiset<E>> toImmutableMultiset(
Function<? super T, ? extends E> elementFunction, ToIntFunction<? super T> countFunction) { Function<? super T, ? extends E> elementFunction, ToIntFunction<? super T> countFunction) {
checkNotNull(elementFunction); return CollectCollectors.toImmutableMultiset(elementFunction, countFunction);
checkNotNull(countFunction);
return Collector.of(
LinkedHashMultiset::create,
(multiset, t) ->
multiset.add(checkNotNull(elementFunction.apply(t)), countFunction.applyAsInt(t)),
(multiset1, multiset2) -> {
multiset1.addAll(multiset2);
return multiset1;
},
(Multiset<E> multiset) -> copyFromEntries(multiset.entrySet()));
} }
/** Returns the empty immutable multiset. */ /** Returns the empty immutable multiset. */

View file

@ -514,13 +514,11 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> implements
} }
@Override @Override
public Builder<E> add(E... elements) { public Builder<E> add(E... elements) {
super.add(elements); super.add(elements);
return this; return this;
} }
@Override
/** /**
* Adds each element of {@code elements} to the {@code ImmutableSet}, ignoring duplicate * Adds each element of {@code elements} to the {@code ImmutableSet}, ignoring duplicate
* elements (only the first duplicate element is added). * elements (only the first duplicate element is added).
@ -529,7 +527,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> implements
* @return this {@code Builder} object * @return this {@code Builder} object
* @throws NullPointerException if {@code elements} is null or contains a null element * @throws NullPointerException if {@code elements} is null or contains a null element
*/ */
@Override
public Builder<E> addAll(Iterable<? extends E> elements) { public Builder<E> addAll(Iterable<? extends E> elements) {
super.addAll(elements); super.addAll(elements);
return this; return this;

View file

@ -22,7 +22,6 @@ import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
@ -38,7 +37,6 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collector; import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -87,13 +85,7 @@ public class ImmutableSetMultimap<K, V> extends ImmutableMultimap<K, V>
public static <T, K, V> Collector<T, ?, ImmutableSetMultimap<K, V>> toImmutableSetMultimap( public static <T, K, V> Collector<T, ?, ImmutableSetMultimap<K, V>> toImmutableSetMultimap(
Function<? super T, ? extends K> keyFunction, Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction) { Function<? super T, ? extends V> valueFunction) {
checkNotNull(keyFunction, "keyFunction"); return CollectCollectors.toImmutableSetMultimap(keyFunction, valueFunction);
checkNotNull(valueFunction, "valueFunction");
return Collector.of(
ImmutableSetMultimap::<K, V>builder,
(builder, t) -> builder.put(keyFunction.apply(t), valueFunction.apply(t)),
ImmutableSetMultimap.Builder::combine,
ImmutableSetMultimap.Builder::build);
} }
/** /**
@ -140,14 +132,7 @@ public class ImmutableSetMultimap<K, V> extends ImmutableMultimap<K, V>
Collector<T, ?, ImmutableSetMultimap<K, V>> flatteningToImmutableSetMultimap( Collector<T, ?, ImmutableSetMultimap<K, V>> flatteningToImmutableSetMultimap(
Function<? super T, ? extends K> keyFunction, Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends Stream<? extends V>> valuesFunction) { Function<? super T, ? extends Stream<? extends V>> valuesFunction) {
checkNotNull(keyFunction); return CollectCollectors.flatteningToImmutableSetMultimap(keyFunction, valuesFunction);
checkNotNull(valuesFunction);
return Collectors.collectingAndThen(
Multimaps.flatteningToMultimap(
input -> checkNotNull(keyFunction.apply(input)),
input -> valuesFunction.apply(input).peek(Preconditions::checkNotNull),
MultimapBuilder.linkedHashKeys().linkedHashSetValues()::<K, V>build),
ImmutableSetMultimap::copyOf);
} }
/** Returns the empty multimap. */ /** Returns the empty multimap. */

View file

@ -95,14 +95,7 @@ public final class ImmutableSortedMap<K, V> extends ImmutableSortedMapFauxveride
Function<? super T, ? extends K> keyFunction, Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction, Function<? super T, ? extends V> valueFunction,
BinaryOperator<V> mergeFunction) { BinaryOperator<V> mergeFunction) {
checkNotNull(comparator); return CollectCollectors.toImmutableSortedMap(comparator, keyFunction, valueFunction, mergeFunction);
checkNotNull(keyFunction);
checkNotNull(valueFunction);
checkNotNull(mergeFunction);
return Collectors.collectingAndThen(
Collectors.toMap(
keyFunction, valueFunction, mergeFunction, () -> new TreeMap<K, V>(comparator)),
ImmutableSortedMap::copyOfSorted);
} }
/* /*
@ -916,19 +909,18 @@ public final class ImmutableSortedMap<K, V> extends ImmutableSortedMapFauxveride
* are reconstructed using public factory methods. This ensures that the implementation types * are reconstructed using public factory methods. This ensures that the implementation types
* remain as implementation details. * remain as implementation details.
*/ */
private static class SerializedForm extends ImmutableMap.SerializedForm { private static class SerializedForm<K, V> extends ImmutableMap.SerializedForm<K, V> {
private final Comparator<Object> comparator; private final Comparator<? super K> comparator;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
SerializedForm(ImmutableSortedMap<?, ?> sortedMap) { SerializedForm(ImmutableSortedMap<K, V> sortedMap) {
super(sortedMap); super(sortedMap);
comparator = (Comparator<Object>) sortedMap.comparator(); comparator = sortedMap.comparator();
} }
@Override @Override
Object readResolve() { Builder<K, V> makeBuilder(int size) {
Builder<Object, Object> builder = new Builder<>(comparator); return new Builder<>(comparator);
return createMap(builder);
} }
private static final long serialVersionUID = 0; private static final long serialVersionUID = 0;
@ -936,7 +928,7 @@ public final class ImmutableSortedMap<K, V> extends ImmutableSortedMapFauxveride
@Override @Override
Object writeReplace() { Object writeReplace() {
return new SerializedForm(this); return new SerializedForm<>(this);
} }
// This class is never actually serialized directly, but we have to make the // This class is never actually serialized directly, but we have to make the

View file

@ -20,10 +20,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtCompatible;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.google.common.collect.Tables.AbstractCell;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -63,15 +61,7 @@ public abstract class ImmutableTable<R, C, V> extends AbstractTable<R, C, V>
Function<? super T, ? extends R> rowFunction, Function<? super T, ? extends R> rowFunction,
Function<? super T, ? extends C> columnFunction, Function<? super T, ? extends C> columnFunction,
Function<? super T, ? extends V> valueFunction) { Function<? super T, ? extends V> valueFunction) {
checkNotNull(rowFunction, "rowFunction"); return TableCollectors.toImmutableTable(rowFunction, columnFunction, valueFunction);
checkNotNull(columnFunction, "columnFunction");
checkNotNull(valueFunction, "valueFunction");
return Collector.of(
() -> new ImmutableTable.Builder<R, C, V>(),
(builder, t) ->
builder.put(rowFunction.apply(t), columnFunction.apply(t), valueFunction.apply(t)),
(b1, b2) -> b1.combine(b2),
b -> b.build());
} }
/** /**
@ -90,88 +80,7 @@ public abstract class ImmutableTable<R, C, V> extends AbstractTable<R, C, V>
Function<? super T, ? extends C> columnFunction, Function<? super T, ? extends C> columnFunction,
Function<? super T, ? extends V> valueFunction, Function<? super T, ? extends V> valueFunction,
BinaryOperator<V> mergeFunction) { BinaryOperator<V> mergeFunction) {
return TableCollectors.toImmutableTable(rowFunction, columnFunction, valueFunction, mergeFunction);
checkNotNull(rowFunction, "rowFunction");
checkNotNull(columnFunction, "columnFunction");
checkNotNull(valueFunction, "valueFunction");
checkNotNull(mergeFunction, "mergeFunction");
/*
* No mutable Table exactly matches the insertion order behavior of ImmutableTable.Builder, but
* the Builder can't efficiently support merging of duplicate values. Getting around this
* requires some work.
*/
return Collector.of(
() -> new CollectorState<R, C, V>()
/* GWT isn't currently playing nicely with constructor references? */ ,
(state, input) ->
state.put(
rowFunction.apply(input),
columnFunction.apply(input),
valueFunction.apply(input),
mergeFunction),
(s1, s2) -> s1.combine(s2, mergeFunction),
state -> state.toTable());
}
private static final class CollectorState<R, C, V> {
final List<MutableCell<R, C, V>> insertionOrder = new ArrayList<>();
final Table<R, C, MutableCell<R, C, V>> table = HashBasedTable.create();
void put(R row, C column, V value, BinaryOperator<V> merger) {
MutableCell<R, C, V> oldCell = table.get(row, column);
if (oldCell == null) {
MutableCell<R, C, V> cell = new MutableCell<>(row, column, value);
insertionOrder.add(cell);
table.put(row, column, cell);
} else {
oldCell.merge(value, merger);
}
}
CollectorState<R, C, V> combine(CollectorState<R, C, V> other, BinaryOperator<V> merger) {
for (MutableCell<R, C, V> cell : other.insertionOrder) {
put(cell.getRowKey(), cell.getColumnKey(), cell.getValue(), merger);
}
return this;
}
ImmutableTable<R, C, V> toTable() {
return copyOf(insertionOrder);
}
}
private static final class MutableCell<R, C, V> extends AbstractCell<R, C, V> {
private final R row;
private final C column;
private V value;
MutableCell(R row, C column, V value) {
this.row = checkNotNull(row, "row");
this.column = checkNotNull(column, "column");
this.value = checkNotNull(value, "value");
}
@Override
public R getRowKey() {
return row;
}
@Override
public C getColumnKey() {
return column;
}
@Override
public V getValue() {
return value;
}
void merge(V value, BinaryOperator<V> mergeFunction) {
checkNotNull(value, "value");
this.value = checkNotNull(mergeFunction.apply(this.value, value), "mergeFunction.apply");
}
} }
/** Returns an empty immutable table. */ /** Returns an empty immutable table. */
@ -209,7 +118,7 @@ public abstract class ImmutableTable<R, C, V> extends AbstractTable<R, C, V>
} }
} }
private static <R, C, V> ImmutableTable<R, C, V> copyOf( public static <R, C, V> ImmutableTable<R, C, V> copyOf(
Iterable<? extends Cell<? extends R, ? extends C, ? extends V>> cells) { Iterable<? extends Cell<? extends R, ? extends C, ? extends V>> cells) {
ImmutableTable.Builder<R, C, V> builder = ImmutableTable.builder(); ImmutableTable.Builder<R, C, V> builder = ImmutableTable.builder();
for (Cell<? extends R, ? extends C, ? extends V> cell : cells) { for (Cell<? extends R, ? extends C, ? extends V> cell : cells) {

View file

@ -21,8 +21,12 @@ import com.google.common.annotations.GwtIncompatible;
/** /**
* Provides equivalent behavior to {@link String#intern} for other immutable types. Common * Provides similar behavior to {@link String#intern} for any immutable type. Common implementations
* implementations are available from the {@link Interners} class. * are available from the {@link Interners} class.
*
* <p>Note that {@code String.intern()} has some well-known performance limitations, and should
* generally be avoided. Prefer {@link Interners#newWeakInterner} or another {@code Interner}
* implementation even for {@code String} interning.
* *
* @author Kevin Bourrillion * @author Kevin Bourrillion
* @since 3.0 * @since 3.0

View file

@ -314,7 +314,7 @@ public final class Iterables {
public static <T> boolean addAll(Collection<T> addTo, Iterable<? extends T> elementsToAdd) { public static <T> boolean addAll(Collection<T> addTo, Iterable<? extends T> elementsToAdd) {
if (elementsToAdd instanceof Collection) { if (elementsToAdd instanceof Collection) {
Collection<? extends T> c = Collections2.cast(elementsToAdd); Collection<? extends T> c = (Collection<? extends T>) elementsToAdd;
return addTo.addAll(c); return addTo.addAll(c);
} }
return Iterators.addAll(addTo, checkNotNull(elementsToAdd).iterator()); return Iterators.addAll(addTo, checkNotNull(elementsToAdd).iterator());
@ -814,7 +814,7 @@ public final class Iterables {
*/ */
public static <T> T getLast(Iterable<? extends T> iterable, T defaultValue) { public static <T> T getLast(Iterable<? extends T> iterable, T defaultValue) {
if (iterable instanceof Collection) { if (iterable instanceof Collection) {
Collection<? extends T> c = Collections2.cast(iterable); Collection<? extends T> c = (Collection<? extends T>) iterable;
if (c.isEmpty()) { if (c.isEmpty()) {
return defaultValue; return defaultValue;
} else if (iterable instanceof List) { } else if (iterable instanceof List) {

View file

@ -126,8 +126,8 @@ public final class Lists {
checkNotNull(elements); // for GWT checkNotNull(elements); // for GWT
// Let ArrayList's sizing logic work, if possible // Let ArrayList's sizing logic work, if possible
return (elements instanceof Collection) return (elements instanceof Collection)
? new ArrayList<>(Collections2.cast(elements)) ? new ArrayList<>((Collection<? extends E>) elements)
: newArrayList(elements.iterator()); : newArrayList(elements.iterator());
} }
/** /**
@ -264,8 +264,9 @@ public final class Lists {
Iterable<? extends E> elements) { Iterable<? extends E> elements) {
// We copy elements to an ArrayList first, rather than incurring the // We copy elements to an ArrayList first, rather than incurring the
// quadratic cost of adding them to the COWAL directly. // quadratic cost of adding them to the COWAL directly.
Collection<? extends E> elementsCollection = Collection<? extends E> elementsCollection = (elements instanceof Collection)
(elements instanceof Collection) ? Collections2.cast(elements) : newArrayList(elements); ? (Collection<? extends E>) elements
: newArrayList(elements);
return new CopyOnWriteArrayList<>(elementsCollection); return new CopyOnWriteArrayList<>(elementsCollection);
} }

View file

@ -64,7 +64,7 @@ import java.util.concurrent.locks.ReentrantLock;
* @author Charles Fry * @author Charles Fry
* @author Doug Lea ({@code ConcurrentHashMap}) * @author Doug Lea ({@code ConcurrentHashMap})
*/ */
// TODO(kak/cpovirk): Consider removing from this class. // TODO(kak): Consider removing @CanIgnoreReturnValue from this class.
@GwtIncompatible @GwtIncompatible
@SuppressWarnings("GuardedBy") // TODO(b/35466881): Fix or suppress. @SuppressWarnings("GuardedBy") // TODO(b/35466881): Fix or suppress.
class MapMakerInternalMap< class MapMakerInternalMap<

View file

@ -172,37 +172,6 @@ public final class Maps {
return ImmutableEnumMap.asImmutable(enumMap); return ImmutableEnumMap.asImmutable(enumMap);
} }
private static class Accumulator<K extends Enum<K>, V> {
private final BinaryOperator<V> mergeFunction;
private EnumMap<K, V> map = null;
Accumulator(BinaryOperator<V> mergeFunction) {
this.mergeFunction = mergeFunction;
}
void put(K key, V value) {
if (map == null) {
map = new EnumMap<>(key.getDeclaringClass());
}
map.merge(key, value, mergeFunction);
}
Accumulator<K, V> combine(Accumulator<K, V> other) {
if (this.map == null) {
return other;
} else if (other.map == null) {
return this;
} else {
other.map.forEach(this::put);
return this;
}
}
ImmutableMap<K, V> toImmutableMap() {
return (map == null) ? ImmutableMap.<K, V>of() : ImmutableEnumMap.asImmutable(map);
}
}
/** /**
* Returns a {@link Collector} that accumulates elements into an {@code ImmutableMap} whose keys * Returns a {@link Collector} that accumulates elements into an {@code ImmutableMap} whose keys
* and values are the result of applying the provided mapping functions to the input elements. The * and values are the result of applying the provided mapping functions to the input elements. The
@ -220,22 +189,7 @@ public final class Maps {
public static <T, K extends Enum<K>, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableEnumMap( public static <T, K extends Enum<K>, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableEnumMap(
java.util.function.Function<? super T, ? extends K> keyFunction, java.util.function.Function<? super T, ? extends K> keyFunction,
java.util.function.Function<? super T, ? extends V> valueFunction) { java.util.function.Function<? super T, ? extends V> valueFunction) {
checkNotNull(keyFunction); return CollectCollectors.toImmutableEnumMap(keyFunction, valueFunction);
checkNotNull(valueFunction);
return Collector.of(
() ->
new Accumulator<K, V>(
(v1, v2) -> {
throw new IllegalArgumentException("Multiple values for key: " + v1 + ", " + v2);
}),
(accum, t) -> {
K key = checkNotNull(keyFunction.apply(t), "Null key for input %s", t);
V newValue = checkNotNull(valueFunction.apply(t), "Null value for input %s", t);
accum.put(key, newValue);
},
Accumulator::combine,
Accumulator::toImmutableMap,
Collector.Characteristics.UNORDERED);
} }
/** /**
@ -253,19 +207,7 @@ public final class Maps {
java.util.function.Function<? super T, ? extends K> keyFunction, java.util.function.Function<? super T, ? extends K> keyFunction,
java.util.function.Function<? super T, ? extends V> valueFunction, java.util.function.Function<? super T, ? extends V> valueFunction,
BinaryOperator<V> mergeFunction) { BinaryOperator<V> mergeFunction) {
checkNotNull(keyFunction); return CollectCollectors.toImmutableEnumMap(keyFunction, valueFunction, mergeFunction);
checkNotNull(valueFunction);
checkNotNull(mergeFunction);
// not UNORDERED because we don't know if mergeFunction is commutative
return Collector.of(
() -> new Accumulator<K, V>(mergeFunction),
(accum, t) -> {
K key = checkNotNull(keyFunction.apply(t), "Null key for input %s", t);
V newValue = checkNotNull(valueFunction.apply(t), "Null value for input %s", t);
accum.put(key, newValue);
},
Accumulator::combine,
Accumulator::toImmutableMap);
} }
/** /**

View file

@ -49,6 +49,7 @@ import java.util.NoSuchElementException;
import java.util.Set; import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.Spliterator; import java.util.Spliterator;
import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collector; import java.util.stream.Collector;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -104,21 +105,11 @@ public final class Multimaps {
* *
* @since 21.0 * @since 21.0
*/ */
@Beta
public static <T, K, V, M extends Multimap<K, V>> Collector<T, ?, M> toMultimap( public static <T, K, V, M extends Multimap<K, V>> Collector<T, ?, M> toMultimap(
java.util.function.Function<? super T, ? extends K> keyFunction, java.util.function.Function<? super T, ? extends K> keyFunction,
java.util.function.Function<? super T, ? extends V> valueFunction, java.util.function.Function<? super T, ? extends V> valueFunction,
java.util.function.Supplier<M> multimapSupplier) { java.util.function.Supplier<M> multimapSupplier) {
checkNotNull(keyFunction); return CollectCollectors.toMultimap(keyFunction, valueFunction, multimapSupplier);
checkNotNull(valueFunction);
checkNotNull(multimapSupplier);
return Collector.of(
multimapSupplier,
(multimap, input) -> multimap.put(keyFunction.apply(input), valueFunction.apply(input)),
(multimap1, multimap2) -> {
multimap1.putAll(multimap2);
return multimap1;
});
} }
/** /**
@ -695,6 +686,11 @@ public final class Multimaps {
return result; return result;
} }
@Override
public void forEach(BiConsumer<? super K, ? super V> consumer) {
delegate.forEach(checkNotNull(consumer));
}
@Override @Override
public Collection<V> get(K key) { public Collection<V> get(K key) {
return unmodifiableValueCollection(delegate.get(key)); return unmodifiableValueCollection(delegate.get(key));

View file

@ -77,16 +77,7 @@ public final class Multisets {
java.util.function.Function<? super T, E> elementFunction, java.util.function.Function<? super T, E> elementFunction,
java.util.function.ToIntFunction<? super T> countFunction, java.util.function.ToIntFunction<? super T> countFunction,
java.util.function.Supplier<M> multisetSupplier) { java.util.function.Supplier<M> multisetSupplier) {
checkNotNull(elementFunction); return CollectCollectors.toMultiset(elementFunction, countFunction, multisetSupplier);
checkNotNull(countFunction);
checkNotNull(multisetSupplier);
return Collector.of(
multisetSupplier,
(ms, t) -> ms.add(elementFunction.apply(t), countFunction.applyAsInt(t)),
(ms1, ms2) -> {
ms1.addAll(ms2);
return ms1;
});
} }
/** /**

View file

@ -582,8 +582,8 @@ public abstract class Ordering<T> implements Comparator<T> {
* <p><b>Implementation note:</b> this method is invoked by the default implementations of the * <p><b>Implementation note:</b> this method is invoked by the default implementations of the
* other {@code min} overloads, so overriding it will affect their behavior. * other {@code min} overloads, so overriding it will affect their behavior.
* *
* <p><b>Java 8 users:</b> Use {@code Collections.min(Arrays.asList(a, b), thisComparator)} * <p><b>Note:</b> Consider using {@code Comparators.min(a, b, thisComparator)} instead. If {@code
* instead (but note that it does not guarantee which tied minimum element is returned). * thisComparator} is {@link Ordering#natural}, then use {@code Comparators.min(a, b)}.
* *
* @param a value to compare, returned if less than or equal to b. * @param a value to compare, returned if less than or equal to b.
* @param b value to compare. * @param b value to compare.

View file

@ -74,7 +74,7 @@ public final class Queues {
*/ */
public static <E> ArrayDeque<E> newArrayDeque(Iterable<? extends E> elements) { public static <E> ArrayDeque<E> newArrayDeque(Iterable<? extends E> elements) {
if (elements instanceof Collection) { if (elements instanceof Collection) {
return new ArrayDeque<E>(Collections2.cast(elements)); return new ArrayDeque<E>((Collection<? extends E>) elements);
} }
ArrayDeque<E> deque = new ArrayDeque<E>(); ArrayDeque<E> deque = new ArrayDeque<E>();
Iterables.addAll(deque, elements); Iterables.addAll(deque, elements);
@ -97,7 +97,7 @@ public final class Queues {
public static <E> ConcurrentLinkedQueue<E> newConcurrentLinkedQueue( public static <E> ConcurrentLinkedQueue<E> newConcurrentLinkedQueue(
Iterable<? extends E> elements) { Iterable<? extends E> elements) {
if (elements instanceof Collection) { if (elements instanceof Collection) {
return new ConcurrentLinkedQueue<E>(Collections2.cast(elements)); return new ConcurrentLinkedQueue<E>((Collection<? extends E>) elements);
} }
ConcurrentLinkedQueue<E> queue = new ConcurrentLinkedQueue<E>(); ConcurrentLinkedQueue<E> queue = new ConcurrentLinkedQueue<E>();
Iterables.addAll(queue, elements); Iterables.addAll(queue, elements);
@ -137,7 +137,7 @@ public final class Queues {
@GwtIncompatible // LinkedBlockingDeque @GwtIncompatible // LinkedBlockingDeque
public static <E> LinkedBlockingDeque<E> newLinkedBlockingDeque(Iterable<? extends E> elements) { public static <E> LinkedBlockingDeque<E> newLinkedBlockingDeque(Iterable<? extends E> elements) {
if (elements instanceof Collection) { if (elements instanceof Collection) {
return new LinkedBlockingDeque<E>(Collections2.cast(elements)); return new LinkedBlockingDeque<E>((Collection<? extends E>) elements);
} }
LinkedBlockingDeque<E> deque = new LinkedBlockingDeque<E>(); LinkedBlockingDeque<E> deque = new LinkedBlockingDeque<E>();
Iterables.addAll(deque, elements); Iterables.addAll(deque, elements);
@ -173,7 +173,7 @@ public final class Queues {
@GwtIncompatible // LinkedBlockingQueue @GwtIncompatible // LinkedBlockingQueue
public static <E> LinkedBlockingQueue<E> newLinkedBlockingQueue(Iterable<? extends E> elements) { public static <E> LinkedBlockingQueue<E> newLinkedBlockingQueue(Iterable<? extends E> elements) {
if (elements instanceof Collection) { if (elements instanceof Collection) {
return new LinkedBlockingQueue<E>(Collections2.cast(elements)); return new LinkedBlockingQueue<E>((Collection<? extends E>) elements);
} }
LinkedBlockingQueue<E> queue = new LinkedBlockingQueue<E>(); LinkedBlockingQueue<E> queue = new LinkedBlockingQueue<E>();
Iterables.addAll(queue, elements); Iterables.addAll(queue, elements);
@ -207,7 +207,7 @@ public final class Queues {
public static <E extends Comparable> PriorityBlockingQueue<E> newPriorityBlockingQueue( public static <E extends Comparable> PriorityBlockingQueue<E> newPriorityBlockingQueue(
Iterable<? extends E> elements) { Iterable<? extends E> elements) {
if (elements instanceof Collection) { if (elements instanceof Collection) {
return new PriorityBlockingQueue<E>(Collections2.cast(elements)); return new PriorityBlockingQueue<E>((Collection<? extends E>) elements);
} }
PriorityBlockingQueue<E> queue = new PriorityBlockingQueue<E>(); PriorityBlockingQueue<E> queue = new PriorityBlockingQueue<E>();
Iterables.addAll(queue, elements); Iterables.addAll(queue, elements);
@ -237,7 +237,7 @@ public final class Queues {
public static <E extends Comparable> PriorityQueue<E> newPriorityQueue( public static <E extends Comparable> PriorityQueue<E> newPriorityQueue(
Iterable<? extends E> elements) { Iterable<? extends E> elements) {
if (elements instanceof Collection) { if (elements instanceof Collection) {
return new PriorityQueue<E>(Collections2.cast(elements)); return new PriorityQueue<E>((Collection<? extends E>) elements);
} }
PriorityQueue<E> queue = new PriorityQueue<E>(); PriorityQueue<E> queue = new PriorityQueue<E>();
Iterables.addAll(queue, elements); Iterables.addAll(queue, elements);
@ -322,7 +322,7 @@ public final class Queues {
} }
/** /**
* Drains the queue as {@linkplain #drain(BlockingQueue, Collection, int, Duration)}, but with a * Drains the queue as {@linkplain #drain(BlockingQueue, Collection, int, long, TimeUnit)}, but with a
* different behavior in case it is interrupted while waiting. In that case, the operation will * different behavior in case it is interrupted while waiting. In that case, the operation will
* continue as usual, and in the end the thread's interruption status will be set (no {@code * continue as usual, and in the end the thread's interruption status will be set (no {@code
* InterruptedException} is thrown). * InterruptedException} is thrown).

View file

@ -576,6 +576,21 @@ public final class Range<C extends Comparable> extends RangeGwtSerializationDepe
* @since 27.0 * @since 27.0
*/ */
public Range<C> gap(Range<C> otherRange) { public Range<C> gap(Range<C> otherRange) {
/*
* For an explanation of the basic principle behind this check, see
* https://stackoverflow.com/a/35754308/28465
*
* In that explanation's notation, our `overlap` check would be `x1 < y2 && y1 < x2`. We've
* flipped one part of the check so that we're using "less than" in both cases (rather than a
* mix of "less than" and "greater than"). We've also switched to "strictly less than" rather
* than "less than or equal to" because of *handwave* the difference between "endpoints of
* inclusive ranges" and "Cuts."
*/
if (lowerBound.compareTo(otherRange.upperBound) < 0
&& otherRange.lowerBound.compareTo(upperBound) < 0) {
throw new IllegalArgumentException(
"Ranges have a nonempty intersection: " + this + ", " + otherRange);
}
boolean isThisFirst = this.lowerBound.compareTo(otherRange.lowerBound) < 0; boolean isThisFirst = this.lowerBound.compareTo(otherRange.lowerBound) < 0;
Range<C> firstRange = isThisFirst ? this : otherRange; Range<C> firstRange = isThisFirst ? this : otherRange;
Range<C> secondRange = isThisFirst ? otherRange : this; Range<C> secondRange = isThisFirst ? otherRange : this;

View file

@ -207,10 +207,10 @@ final class RegularImmutableMap<K, V> extends ImmutableMap<K, V> {
} }
@GwtCompatible(emulated = true) @GwtCompatible(emulated = true)
private static final class KeySet<K, V> extends IndexedImmutableSet<K> { private static final class KeySet<K> extends IndexedImmutableSet<K> {
private final RegularImmutableMap<K, V> map; private final RegularImmutableMap<K, ?> map;
KeySet(RegularImmutableMap<K, V> map) { KeySet(RegularImmutableMap<K, ?> map) {
this.map = map; this.map = map;
} }
@ -234,12 +234,6 @@ final class RegularImmutableMap<K, V> extends ImmutableMap<K, V> {
return map.size(); return map.size();
} }
@GwtIncompatible // serialization
@Override
Object writeReplace() {
return new SerializedForm<K>(map);
}
@GwtIncompatible // serialization @GwtIncompatible // serialization
private static class SerializedForm<K> implements Serializable { private static class SerializedForm<K> implements Serializable {
final ImmutableMap<K, ?> map; final ImmutableMap<K, ?> map;
@ -284,12 +278,6 @@ final class RegularImmutableMap<K, V> extends ImmutableMap<K, V> {
return true; return true;
} }
@GwtIncompatible // serialization
@Override
Object writeReplace() {
return new SerializedForm<V>(map);
}
@GwtIncompatible // serialization @GwtIncompatible // serialization
private static class SerializedForm<V> implements Serializable { private static class SerializedForm<V> implements Serializable {
final ImmutableMap<?, V> map; final ImmutableMap<?, V> map;

View file

@ -138,42 +138,6 @@ public final class Sets {
} }
} }
private static final class Accumulator<E extends Enum<E>> {
static final Collector<Enum<?>, ?, ImmutableSet<? extends Enum<?>>> TO_IMMUTABLE_ENUM_SET =
(Collector)
Collector.<Enum, Accumulator, ImmutableSet<?>>of(
Accumulator::new,
Accumulator::add,
Accumulator::combine,
Accumulator::toImmutableSet,
Collector.Characteristics.UNORDERED);
private EnumSet<E> set;
void add(E e) {
if (set == null) {
set = EnumSet.of(e);
} else {
set.add(e);
}
}
Accumulator<E> combine(Accumulator<E> other) {
if (this.set == null) {
return other;
} else if (other.set == null) {
return this;
} else {
this.set.addAll(other.set);
return this;
}
}
ImmutableSet<E> toImmutableSet() {
return (set == null) ? ImmutableSet.<E>of() : ImmutableEnumSet.asImmutable(set);
}
}
/** /**
* Returns a {@code Collector} that accumulates the input elements into a new {@code ImmutableSet} * Returns a {@code Collector} that accumulates the input elements into a new {@code ImmutableSet}
* with an implementation specialized for enums. Unlike {@link ImmutableSet#toImmutableSet}, the * with an implementation specialized for enums. Unlike {@link ImmutableSet#toImmutableSet}, the
@ -182,7 +146,7 @@ public final class Sets {
* @since 21.0 * @since 21.0
*/ */
public static <E extends Enum<E>> Collector<E, ?, ImmutableSet<E>> toImmutableEnumSet() { public static <E extends Enum<E>> Collector<E, ?, ImmutableSet<E>> toImmutableEnumSet() {
return (Collector) Accumulator.TO_IMMUTABLE_ENUM_SET; return CollectCollectors.toImmutableEnumSet();
} }
/** /**
@ -254,8 +218,8 @@ public final class Sets {
*/ */
public static <E> HashSet<E> newHashSet(Iterable<? extends E> elements) { public static <E> HashSet<E> newHashSet(Iterable<? extends E> elements) {
return (elements instanceof Collection) return (elements instanceof Collection)
? new HashSet<E>(Collections2.cast(elements)) ? new HashSet<E>((Collection<? extends E>) elements)
: newHashSet(elements.iterator()); : newHashSet(elements.iterator());
} }
/** /**
@ -359,7 +323,7 @@ public final class Sets {
*/ */
public static <E> LinkedHashSet<E> newLinkedHashSet(Iterable<? extends E> elements) { public static <E> LinkedHashSet<E> newLinkedHashSet(Iterable<? extends E> elements) {
if (elements instanceof Collection) { if (elements instanceof Collection) {
return new LinkedHashSet<E>(Collections2.cast(elements)); return new LinkedHashSet<E>((Collection<? extends E>) elements);
} }
LinkedHashSet<E> set = newLinkedHashSet(); LinkedHashSet<E> set = newLinkedHashSet();
Iterables.addAll(set, elements); Iterables.addAll(set, elements);
@ -487,8 +451,8 @@ public final class Sets {
// quadratic cost of adding them to the COWAS directly. // quadratic cost of adding them to the COWAS directly.
Collection<? extends E> elementsCollection = Collection<? extends E> elementsCollection =
(elements instanceof Collection) (elements instanceof Collection)
? Collections2.cast(elements) ? (Collection<? extends E>) elements
: Lists.newArrayList(elements); : Lists.newArrayList(elements);
return new CopyOnWriteArraySet<E>(elementsCollection); return new CopyOnWriteArraySet<E>(elementsCollection);
} }
@ -1427,6 +1391,25 @@ public final class Sets {
return delegate; return delegate;
} }
@Override
public boolean contains(Object object) {
if (!(object instanceof List)) {
return false;
}
List<?> list = (List<?>) object;
if (list.size() != axes.size()) {
return false;
}
int i = 0;
for (Object o : list) {
if (!axes.get(i).contains(o)) {
return false;
}
i++;
}
return true;
}
@Override @Override
public boolean equals(Object object) { public boolean equals(Object object) {
// Warning: this is broken if size() == 0, so it is critical that we // Warning: this is broken if size() == 0, so it is critical that we
@ -1576,7 +1559,7 @@ public final class Sets {
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj instanceof PowerSet) { if (obj instanceof PowerSet) {
PowerSet<?> that = (PowerSet<?>) obj; PowerSet<?> that = (PowerSet<?>) obj;
return inputSet.equals(that.inputSet); return inputSet.keySet().equals(that.inputSet.keySet());
} }
return super.equals(obj); return super.equals(obj);
} }

View file

@ -42,6 +42,7 @@ final class SingletonImmutableBiMap<K, V> extends ImmutableBiMap<K, V> {
checkEntryNotNull(singleKey, singleValue); checkEntryNotNull(singleKey, singleValue);
this.singleKey = singleKey; this.singleKey = singleKey;
this.singleValue = singleValue; this.singleValue = singleValue;
this.inverse = null;
} }
private SingletonImmutableBiMap(K singleKey, V singleValue, ImmutableBiMap<V, K> inverse) { private SingletonImmutableBiMap(K singleKey, V singleValue, ImmutableBiMap<V, K> inverse) {
@ -92,14 +93,21 @@ final class SingletonImmutableBiMap<K, V> extends ImmutableBiMap<K, V> {
transient ImmutableBiMap<V, K> inverse; transient ImmutableBiMap<V, K> inverse;
private transient ImmutableBiMap<V, K> lazyInverse;
@Override @Override
public ImmutableBiMap<V, K> inverse() { public ImmutableBiMap<V, K> inverse() {
// racy single-check idiom // racy single-check idiom
ImmutableBiMap<V, K> result = inverse; if (inverse != null) {
if (result == null) { return inverse;
return inverse = new SingletonImmutableBiMap<>(singleValue, singleKey, this);
} else { } else {
return result; // racy single-check idiom
ImmutableBiMap<V, K> result = lazyInverse;
if (result == null) {
return lazyInverse = new SingletonImmutableBiMap<>(singleValue, singleKey, this);
} else {
return result;
}
} }
} }
} }

View file

@ -0,0 +1,200 @@
/*
* Copyright (C) 2009 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;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.GwtCompatible;
import com.google.common.collect.Tables.AbstractCell;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
/** Collectors utilities for {@code common.collect.Table} internals. */
@GwtCompatible
final class TableCollectors {
static <T, R, C, V> Collector<T, ?, ImmutableTable<R, C, V>> toImmutableTable(
Function<? super T, ? extends R> rowFunction,
Function<? super T, ? extends C> columnFunction,
Function<? super T, ? extends V> valueFunction) {
checkNotNull(rowFunction, "rowFunction");
checkNotNull(columnFunction, "columnFunction");
checkNotNull(valueFunction, "valueFunction");
return Collector.of(
(Supplier<ImmutableTable.Builder<R, C, V>>) ImmutableTable.Builder::new,
(builder, t) ->
builder.put(rowFunction.apply(t), columnFunction.apply(t), valueFunction.apply(t)),
ImmutableTable.Builder::combine,
ImmutableTable.Builder::build);
}
static <T, R, C, V> Collector<T, ?, ImmutableTable<R, C, V>> toImmutableTable(
Function<? super T, ? extends R> rowFunction,
Function<? super T, ? extends C> columnFunction,
Function<? super T, ? extends V> valueFunction,
BinaryOperator<V> mergeFunction) {
checkNotNull(rowFunction, "rowFunction");
checkNotNull(columnFunction, "columnFunction");
checkNotNull(valueFunction, "valueFunction");
checkNotNull(mergeFunction, "mergeFunction");
/*
* No mutable Table exactly matches the insertion order behavior of ImmutableTable.Builder, but
* the Builder can't efficiently support merging of duplicate values. Getting around this
* requires some work.
*/
return Collector.of(
() -> new ImmutableTableCollectorState<R, C, V>()
/* GWT isn't currently playing nicely with constructor references? */ ,
(state, input) ->
state.put(
rowFunction.apply(input),
columnFunction.apply(input),
valueFunction.apply(input),
mergeFunction),
(s1, s2) -> s1.combine(s2, mergeFunction),
state -> state.toTable());
}
static <T, R, C, V, I extends Table<R, C, V>> Collector<T, ?, I> toTable(
Function<? super T, ? extends R> rowFunction,
Function<? super T, ? extends C> columnFunction,
Function<? super T, ? extends V> valueFunction,
Supplier<I> tableSupplier) {
return toTable(
rowFunction,
columnFunction,
valueFunction,
(v1, v2) -> {
throw new IllegalStateException("Conflicting values " + v1 + " and " + v2);
},
tableSupplier);
}
static <T, R, C, V, I extends Table<R, C, V>> Collector<T, ?, I> toTable(
Function<? super T, ? extends R> rowFunction,
Function<? super T, ? extends C> columnFunction,
Function<? super T, ? extends V> valueFunction,
BinaryOperator<V> mergeFunction,
Supplier<I> tableSupplier) {
checkNotNull(rowFunction);
checkNotNull(columnFunction);
checkNotNull(valueFunction);
checkNotNull(mergeFunction);
checkNotNull(tableSupplier);
return Collector.of(
tableSupplier,
(table, input) ->
mergeTables(
table,
rowFunction.apply(input),
columnFunction.apply(input),
valueFunction.apply(input),
mergeFunction),
(table1, table2) -> {
for (Table.Cell<R, C, V> cell2 : table2.cellSet()) {
mergeTables(
table1, cell2.getRowKey(), cell2.getColumnKey(), cell2.getValue(), mergeFunction);
}
return table1;
});
}
private static final class ImmutableTableCollectorState<R, C, V> {
final List<MutableCell<R, C, V>> insertionOrder = new ArrayList<>();
final Table<R, C, MutableCell<R, C, V>> table = HashBasedTable.create();
void put(R row, C column, V value, BinaryOperator<V> merger) {
MutableCell<R, C, V> oldCell = table.get(row, column);
if (oldCell == null) {
MutableCell<R, C, V> cell = new MutableCell<>(row, column, value);
insertionOrder.add(cell);
table.put(row, column, cell);
} else {
oldCell.merge(value, merger);
}
}
ImmutableTableCollectorState<R, C, V> combine(
ImmutableTableCollectorState<R, C, V> other, BinaryOperator<V> merger) {
for (MutableCell<R, C, V> cell : other.insertionOrder) {
put(cell.getRowKey(), cell.getColumnKey(), cell.getValue(), merger);
}
return this;
}
ImmutableTable<R, C, V> toTable() {
return ImmutableTable.copyOf(insertionOrder);
}
}
private static final class MutableCell<R, C, V> extends AbstractCell<R, C, V> {
private final R row;
private final C column;
private V value;
MutableCell(R row, C column, V value) {
this.row = checkNotNull(row, "row");
this.column = checkNotNull(column, "column");
this.value = checkNotNull(value, "value");
}
@Override
public R getRowKey() {
return row;
}
@Override
public C getColumnKey() {
return column;
}
@Override
public V getValue() {
return value;
}
void merge(V value, BinaryOperator<V> mergeFunction) {
checkNotNull(value, "value");
this.value = checkNotNull(mergeFunction.apply(this.value, value), "mergeFunction.apply");
}
}
private static <R, C, V> void mergeTables(
Table<R, C, V> table, R row, C column, V value, BinaryOperator<V> mergeFunction) {
checkNotNull(value);
V oldValue = table.get(row, column);
if (oldValue == null) {
table.put(row, column, value);
} else {
V newValue = mergeFunction.apply(oldValue, value);
if (newValue == null) {
table.remove(row, column);
} else {
table.put(row, column, newValue);
}
}
}
private TableCollectors() {}
}

View file

@ -68,14 +68,7 @@ public final class Tables {
java.util.function.Function<? super T, ? extends C> columnFunction, java.util.function.Function<? super T, ? extends C> columnFunction,
java.util.function.Function<? super T, ? extends V> valueFunction, java.util.function.Function<? super T, ? extends V> valueFunction,
java.util.function.Supplier<I> tableSupplier) { java.util.function.Supplier<I> tableSupplier) {
return toTable( return TableCollectors.toTable(rowFunction, columnFunction, valueFunction, tableSupplier);
rowFunction,
columnFunction,
valueFunction,
(v1, v2) -> {
throw new IllegalStateException("Conflicting values " + v1 + " and " + v2);
},
tableSupplier);
} }
/** /**
@ -98,26 +91,7 @@ public final class Tables {
java.util.function.Function<? super T, ? extends V> valueFunction, java.util.function.Function<? super T, ? extends V> valueFunction,
BinaryOperator<V> mergeFunction, BinaryOperator<V> mergeFunction,
java.util.function.Supplier<I> tableSupplier) { java.util.function.Supplier<I> tableSupplier) {
checkNotNull(rowFunction); return TableCollectors.toTable(rowFunction, columnFunction, valueFunction, mergeFunction, tableSupplier);
checkNotNull(columnFunction);
checkNotNull(valueFunction);
checkNotNull(mergeFunction);
checkNotNull(tableSupplier);
return Collector.of(
tableSupplier,
(table, input) ->
merge(
table,
rowFunction.apply(input),
columnFunction.apply(input),
valueFunction.apply(input),
mergeFunction),
(table1, table2) -> {
for (Table.Cell<R, C, V> cell2 : table2.cellSet()) {
merge(table1, cell2.getRowKey(), cell2.getColumnKey(), cell2.getValue(), mergeFunction);
}
return table1;
});
} }
private static <R, C, V> void merge( private static <R, C, V> void merge(

View file

@ -119,7 +119,6 @@ public final class TreeRangeMap<K extends Comparable, V> implements RangeMap<K,
@Override @Override
public void put(Range<K> range, V value) { public void put(Range<K> range, V value) {
// don't short-circuit if the range is empty - it may be between two ranges we can coalesce.
if (!range.isEmpty()) { if (!range.isEmpty()) {
checkNotNull(value); checkNotNull(value);
remove(range); remove(range);
@ -129,6 +128,7 @@ public final class TreeRangeMap<K extends Comparable, V> implements RangeMap<K,
@Override @Override
public void putCoalescing(Range<K> range, V value) { public void putCoalescing(Range<K> range, V value) {
// don't short-circuit if the range is empty - it may be between two ranges we can coalesce.
if (entriesByLowerBound.isEmpty()) { if (entriesByLowerBound.isEmpty()) {
put(range, value); put(range, value);
return; return;
@ -508,7 +508,7 @@ public final class TreeRangeMap<K extends Comparable, V> implements RangeMap<K,
@Override @Override
public void putCoalescing(Range<K> range, V value) { public void putCoalescing(Range<K> range, V value) {
if (entriesByLowerBound.isEmpty() || range.isEmpty() || !subRange.encloses(range)) { if (entriesByLowerBound.isEmpty() || !subRange.encloses(range)) {
put(range, value); put(range, value);
return; return;
} }

View file

@ -892,7 +892,7 @@ public class TreeRangeSet<C extends Comparable<?>> extends AbstractRangeSet<C>
"Cannot add range %s to subRangeSet(%s)", "Cannot add range %s to subRangeSet(%s)",
rangeToAdd, rangeToAdd,
restriction); restriction);
super.add(rangeToAdd); TreeRangeSet.this.add(rangeToAdd);
} }
@Override @Override

View file

@ -23,9 +23,10 @@ import java.lang.annotation.Target;
/** /**
* Marks a method as an event subscriber. * Marks a method as an event subscriber.
* *
* <p>The type of event will be indicated by the method's first (and only) parameter. If this * <p>The type of event will be indicated by the method's first (and only) parameter, which cannot
* annotation is applied to methods with zero parameters, or more than one parameter, the object * be primitive. If this annotation is applied to methods with zero parameters, or more than one
* containing the method will not be able to register for event delivery from the {@link EventBus}. * parameter, the object containing the method will not be able to register for event delivery from
* the {@link EventBus}.
* *
* <p>Unless also annotated with @{@link AllowConcurrentEvents}, event subscriber methods will be * <p>Unless also annotated with @{@link AllowConcurrentEvents}, event subscriber methods will be
* invoked serially by each event bus that they are registered with. * invoked serially by each event bus that they are registered with.

View file

@ -16,6 +16,7 @@ package com.google.common.eventbus;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Throwables.throwIfUnchecked;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
@ -31,6 +32,7 @@ import com.google.common.collect.Iterators;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.common.primitives.Primitives;
import com.google.common.reflect.TypeToken; import com.google.common.reflect.TypeToken;
import com.google.common.util.concurrent.UncheckedExecutionException; import com.google.common.util.concurrent.UncheckedExecutionException;
@ -170,7 +172,12 @@ final class SubscriberRegistry {
} }
private static ImmutableList<Method> getAnnotatedMethods(Class<?> clazz) { private static ImmutableList<Method> getAnnotatedMethods(Class<?> clazz) {
return subscriberMethodsCache.getUnchecked(clazz); try {
return subscriberMethodsCache.getUnchecked(clazz);
} catch (UncheckedExecutionException e) {
throwIfUnchecked(e.getCause());
throw e;
}
} }
private static ImmutableList<Method> getAnnotatedMethodsNotCached(Class<?> clazz) { private static ImmutableList<Method> getAnnotatedMethodsNotCached(Class<?> clazz) {
@ -187,7 +194,14 @@ final class SubscriberRegistry {
+ "Subscriber methods must have exactly 1 parameter.", + "Subscriber methods must have exactly 1 parameter.",
method, method,
parameterTypes.length); parameterTypes.length);
checkArgument(
!parameterTypes[0].isPrimitive(),
"@Subscribe method %s's parameter is %s. "
+ "Subscriber methods cannot accept primitives. "
+ "Consider changing the parameter to %s.",
method,
parameterTypes[0].getName(),
Primitives.wrap(parameterTypes[0]).getSimpleName());
MethodIdentifier ident = new MethodIdentifier(method); MethodIdentifier ident = new MethodIdentifier(method);
if (!identifiers.containsKey(ident)) { if (!identifiers.containsKey(ident)) {
identifiers.put(ident, method); identifiers.put(ident, method);

View file

@ -22,13 +22,10 @@ import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.Beta; import com.google.common.annotations.Beta;
import com.google.common.collect.AbstractIterator; import com.google.common.collect.AbstractIterator;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.UnmodifiableIterator;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Deque; import java.util.Deque;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Queue;
import java.util.Set; import java.util.Set;
@ -63,6 +60,11 @@ import java.util.Set;
*/ */
@Beta @Beta
public abstract class Traverser<N> { public abstract class Traverser<N> {
private final SuccessorsFunction<N> successorFunction;
private Traverser(SuccessorsFunction<N> successorFunction) {
this.successorFunction = checkNotNull(successorFunction);
}
/** /**
* Creates a new traverser for the given general {@code graph}. * Creates a new traverser for the given general {@code graph}.
@ -88,10 +90,14 @@ public abstract class Traverser<N> {
* *
* @param graph {@link SuccessorsFunction} representing a general graph that may have cycles. * @param graph {@link SuccessorsFunction} representing a general graph that may have cycles.
*/ */
public static <N> Traverser<N> forGraph(SuccessorsFunction<N> graph) { public static <N> Traverser<N> forGraph(final SuccessorsFunction<N> graph) {
checkNotNull(graph); return new Traverser<N>(graph) {
return new GraphTraverser<>(graph); @Override
} Traversal<N> newTraversal() {
return Traversal.inGraph(graph);
}
};
}
/** /**
* Creates a new traverser for a directed acyclic graph that has at most one path from the start * Creates a new traverser for a directed acyclic graph that has at most one path from the start
@ -174,7 +180,12 @@ public abstract class Traverser<N> {
if (tree instanceof Network) { if (tree instanceof Network) {
checkArgument(((Network<?, ?>) tree).isDirected(), "Undirected networks can never be trees."); checkArgument(((Network<?, ?>) tree).isDirected(), "Undirected networks can never be trees.");
} }
return new TreeTraverser<>(tree); return new Traverser<N>(tree) {
@Override
Traversal<N> newTraversal() {
return Traversal.inTree(tree);
}
};
} }
/** /**
@ -208,7 +219,9 @@ public abstract class Traverser<N> {
* *
* @throws IllegalArgumentException if {@code startNode} is not an element of the graph * @throws IllegalArgumentException if {@code startNode} is not an element of the graph
*/ */
public abstract Iterable<N> breadthFirst(N startNode); public final Iterable<N> breadthFirst(N startNode) {
return breadthFirst(ImmutableSet.of(startNode));
}
/** /**
* Returns an unmodifiable {@code Iterable} over the nodes reachable from any of the {@code * Returns an unmodifiable {@code Iterable} over the nodes reachable from any of the {@code
@ -220,7 +233,15 @@ public abstract class Traverser<N> {
* @see #breadthFirst(Object) * @see #breadthFirst(Object)
* @since 24.1 * @since 24.1
*/ */
public abstract Iterable<N> breadthFirst(Iterable<? extends N> startNodes); public final Iterable<N> breadthFirst(Iterable<? extends N> startNodes) {
final ImmutableSet<N> validated = validate(startNodes);
return new Iterable<N>() {
@Override
public Iterator<N> iterator() {
return newTraversal().breadthFirst(validated.iterator());
}
};
}
/** /**
* Returns an unmodifiable {@code Iterable} over the nodes reachable from {@code startNode}, in * Returns an unmodifiable {@code Iterable} over the nodes reachable from {@code startNode}, in
@ -253,7 +274,9 @@ public abstract class Traverser<N> {
* *
* @throws IllegalArgumentException if {@code startNode} is not an element of the graph * @throws IllegalArgumentException if {@code startNode} is not an element of the graph
*/ */
public abstract Iterable<N> depthFirstPreOrder(N startNode); public final Iterable<N> depthFirstPreOrder(N startNode) {
return depthFirstPreOrder(ImmutableSet.of(startNode));
}
/** /**
* Returns an unmodifiable {@code Iterable} over the nodes reachable from any of the {@code * Returns an unmodifiable {@code Iterable} over the nodes reachable from any of the {@code
@ -265,7 +288,15 @@ public abstract class Traverser<N> {
* @see #depthFirstPreOrder(Object) * @see #depthFirstPreOrder(Object)
* @since 24.1 * @since 24.1
*/ */
public abstract Iterable<N> depthFirstPreOrder(Iterable<? extends N> startNodes); public final Iterable<N> depthFirstPreOrder(Iterable<? extends N> startNodes) {
final ImmutableSet<N> validated = validate(startNodes);
return new Iterable<N>() {
@Override
public Iterator<N> iterator() {
return newTraversal().preOrder(validated.iterator());
}
};
}
/** /**
* Returns an unmodifiable {@code Iterable} over the nodes reachable from {@code startNode}, in * Returns an unmodifiable {@code Iterable} over the nodes reachable from {@code startNode}, in
@ -298,7 +329,9 @@ public abstract class Traverser<N> {
* *
* @throws IllegalArgumentException if {@code startNode} is not an element of the graph * @throws IllegalArgumentException if {@code startNode} is not an element of the graph
*/ */
public abstract Iterable<N> depthFirstPostOrder(N startNode); public final Iterable<N> depthFirstPostOrder(N startNode) {
return depthFirstPostOrder(ImmutableSet.of(startNode));
}
/** /**
* Returns an unmodifiable {@code Iterable} over the nodes reachable from any of the {@code * Returns an unmodifiable {@code Iterable} over the nodes reachable from any of the {@code
@ -310,352 +343,156 @@ public abstract class Traverser<N> {
* @see #depthFirstPostOrder(Object) * @see #depthFirstPostOrder(Object)
* @since 24.1 * @since 24.1
*/ */
public abstract Iterable<N> depthFirstPostOrder(Iterable<? extends N> startNodes); public final Iterable<N> depthFirstPostOrder(Iterable<? extends N> startNodes) {
final ImmutableSet<N> validated = validate(startNodes);
// Avoid subclasses outside of this class return new Iterable<N>() {
private Traverser() {}
private static final class GraphTraverser<N> extends Traverser<N> {
private final SuccessorsFunction<N> graph;
GraphTraverser(SuccessorsFunction<N> graph) {
this.graph = checkNotNull(graph);
}
@Override
public Iterable<N> breadthFirst(final N startNode) {
checkNotNull(startNode);
return breadthFirst(ImmutableSet.of(startNode));
}
@Override
public Iterable<N> breadthFirst(final Iterable<? extends N> startNodes) {
checkNotNull(startNodes);
if (Iterables.isEmpty(startNodes)) {
return ImmutableSet.of();
}
for (N startNode : startNodes) {
checkThatNodeIsInGraph(startNode);
}
return new Iterable<N>() {
@Override
public Iterator<N> iterator() {
return new BreadthFirstIterator(startNodes);
}
};
}
@Override
public Iterable<N> depthFirstPreOrder(final N startNode) {
checkNotNull(startNode);
return depthFirstPreOrder(ImmutableSet.of(startNode));
}
@Override
public Iterable<N> depthFirstPreOrder(final Iterable<? extends N> startNodes) {
checkNotNull(startNodes);
if (Iterables.isEmpty(startNodes)) {
return ImmutableSet.of();
}
for (N startNode : startNodes) {
checkThatNodeIsInGraph(startNode);
}
return new Iterable<N>() {
@Override
public Iterator<N> iterator() {
return new DepthFirstIterator(startNodes, Order.PREORDER);
}
};
}
@Override
public Iterable<N> depthFirstPostOrder(final N startNode) {
checkNotNull(startNode);
return depthFirstPostOrder(ImmutableSet.of(startNode));
}
@Override
public Iterable<N> depthFirstPostOrder(final Iterable<? extends N> startNodes) {
checkNotNull(startNodes);
if (Iterables.isEmpty(startNodes)) {
return ImmutableSet.of();
}
for (N startNode : startNodes) {
checkThatNodeIsInGraph(startNode);
}
return new Iterable<N>() {
@Override
public Iterator<N> iterator() {
return new DepthFirstIterator(startNodes, Order.POSTORDER);
}
};
}
@SuppressWarnings("CheckReturnValue")
private void checkThatNodeIsInGraph(N startNode) {
// successors() throws an IllegalArgumentException for nodes that are not an element of the
// graph.
graph.successors(startNode);
}
private final class BreadthFirstIterator extends UnmodifiableIterator<N> {
private final Queue<N> queue = new ArrayDeque<>();
private final Set<N> visited = new HashSet<>();
BreadthFirstIterator(Iterable<? extends N> roots) {
for (N root : roots) {
// add all roots to the queue, skipping duplicates
if (visited.add(root)) {
queue.add(root);
}
}
}
@Override @Override
public boolean hasNext() { public Iterator<N> iterator() {
return !queue.isEmpty(); return newTraversal().postOrder(validated.iterator());
} }
};
@Override
public N next() {
N current = queue.remove();
for (N neighbor : graph.successors(current)) {
if (visited.add(neighbor)) {
queue.add(neighbor);
}
}
return current;
}
}
private final class DepthFirstIterator extends AbstractIterator<N> {
private final Deque<NodeAndSuccessors> stack = new ArrayDeque<>();
private final Set<N> visited = new HashSet<>();
private final Order order;
DepthFirstIterator(Iterable<? extends N> roots, Order order) {
stack.push(new NodeAndSuccessors(null, roots));
this.order = order;
}
@Override
protected N computeNext() {
while (true) {
if (stack.isEmpty()) {
return endOfData();
}
NodeAndSuccessors nodeAndSuccessors = stack.getFirst();
boolean firstVisit = visited.add(nodeAndSuccessors.node);
boolean lastVisit = !nodeAndSuccessors.successorIterator.hasNext();
boolean produceNode =
(firstVisit && order == Order.PREORDER) || (lastVisit && order == Order.POSTORDER);
if (lastVisit) {
stack.pop();
} else {
// we need to push a neighbor, but only if we haven't already seen it
N successor = nodeAndSuccessors.successorIterator.next();
if (!visited.contains(successor)) {
stack.push(withSuccessors(successor));
}
}
if (produceNode && nodeAndSuccessors.node != null) {
return nodeAndSuccessors.node;
}
}
}
NodeAndSuccessors withSuccessors(N node) {
return new NodeAndSuccessors(node, graph.successors(node));
}
/** A simple tuple of a node and a partially iterated {@link Iterator} of its successors. */
private final class NodeAndSuccessors {
final N node;
final Iterator<? extends N> successorIterator;
NodeAndSuccessors(N node, Iterable<? extends N> successors) {
this.node = node;
this.successorIterator = successors.iterator();
}
}
}
} }
private static final class TreeTraverser<N> extends Traverser<N> { abstract Traversal<N> newTraversal();
private final SuccessorsFunction<N> tree;
TreeTraverser(SuccessorsFunction<N> tree) { @SuppressWarnings("CheckReturnValue")
this.tree = checkNotNull(tree); private ImmutableSet<N> validate(Iterable<? extends N> startNodes) {
} ImmutableSet<N> copy = ImmutableSet.copyOf(startNodes);
for (N node : copy) {
@Override successorFunction.successors(node); // Will throw if node doesn't exist
public Iterable<N> breadthFirst(final N startNode) {
checkNotNull(startNode);
return breadthFirst(ImmutableSet.of(startNode));
}
@Override
public Iterable<N> breadthFirst(final Iterable<? extends N> startNodes) {
checkNotNull(startNodes);
if (Iterables.isEmpty(startNodes)) {
return ImmutableSet.of();
}
for (N startNode : startNodes) {
checkThatNodeIsInTree(startNode);
}
return new Iterable<N>() {
@Override
public Iterator<N> iterator() {
return new BreadthFirstIterator(startNodes);
}
};
}
@Override
public Iterable<N> depthFirstPreOrder(final N startNode) {
checkNotNull(startNode);
return depthFirstPreOrder(ImmutableSet.of(startNode));
}
@Override
public Iterable<N> depthFirstPreOrder(final Iterable<? extends N> startNodes) {
checkNotNull(startNodes);
if (Iterables.isEmpty(startNodes)) {
return ImmutableSet.of();
}
for (N node : startNodes) {
checkThatNodeIsInTree(node);
}
return new Iterable<N>() {
@Override
public Iterator<N> iterator() {
return new DepthFirstPreOrderIterator(startNodes);
}
};
}
@Override
public Iterable<N> depthFirstPostOrder(final N startNode) {
checkNotNull(startNode);
return depthFirstPostOrder(ImmutableSet.of(startNode));
}
@Override
public Iterable<N> depthFirstPostOrder(final Iterable<? extends N> startNodes) {
checkNotNull(startNodes);
if (Iterables.isEmpty(startNodes)) {
return ImmutableSet.of();
}
for (N startNode : startNodes) {
checkThatNodeIsInTree(startNode);
}
return new Iterable<N>() {
@Override
public Iterator<N> iterator() {
return new DepthFirstPostOrderIterator(startNodes);
}
};
}
@SuppressWarnings("CheckReturnValue")
private void checkThatNodeIsInTree(N startNode) {
// successors() throws an IllegalArgumentException for nodes that are not an element of the
// graph.
tree.successors(startNode);
}
private final class BreadthFirstIterator extends UnmodifiableIterator<N> {
private final Queue<N> queue = new ArrayDeque<>();
BreadthFirstIterator(Iterable<? extends N> roots) {
for (N root : roots) {
queue.add(root);
}
}
@Override
public boolean hasNext() {
return !queue.isEmpty();
}
@Override
public N next() {
N current = queue.remove();
Iterables.addAll(queue, tree.successors(current));
return current;
}
}
private final class DepthFirstPreOrderIterator extends UnmodifiableIterator<N> {
private final Deque<Iterator<? extends N>> stack = new ArrayDeque<>();
DepthFirstPreOrderIterator(Iterable<? extends N> roots) {
stack.addLast(roots.iterator());
}
@Override
public boolean hasNext() {
return !stack.isEmpty();
}
@Override
public N next() {
Iterator<? extends N> iterator = stack.getLast(); // throws NoSuchElementException if empty
N result = checkNotNull(iterator.next());
if (!iterator.hasNext()) {
stack.removeLast();
}
Iterator<? extends N> childIterator = tree.successors(result).iterator();
if (childIterator.hasNext()) {
stack.addLast(childIterator);
}
return result;
}
}
private final class DepthFirstPostOrderIterator extends AbstractIterator<N> {
private final ArrayDeque<NodeAndChildren> stack = new ArrayDeque<>();
DepthFirstPostOrderIterator(Iterable<? extends N> roots) {
stack.addLast(new NodeAndChildren(null, roots));
}
@Override
protected N computeNext() {
while (!stack.isEmpty()) {
NodeAndChildren top = stack.getLast();
if (top.childIterator.hasNext()) {
N child = top.childIterator.next();
stack.addLast(withChildren(child));
} else {
stack.removeLast();
if (top.node != null) {
return top.node;
}
}
}
return endOfData();
}
NodeAndChildren withChildren(N node) {
return new NodeAndChildren(node, tree.successors(node));
}
/** A simple tuple of a node and a partially iterated {@link Iterator} of its children. */
private final class NodeAndChildren {
final N node;
final Iterator<? extends N> childIterator;
NodeAndChildren(N node, Iterable<? extends N> children) {
this.node = node;
this.childIterator = children.iterator();
}
}
} }
return copy;
} }
private enum Order { /**
PREORDER, * Abstracts away the difference between traversing a graph vs. a tree. For a tree, we just take
POSTORDER * the next element from the next non-empty iterator; for graph, we need to loop through the next
* non-empty iterator to find first unvisited node.
*/
private abstract static class Traversal<N> {
final SuccessorsFunction<N> successorFunction;
Traversal(SuccessorsFunction<N> successorFunction) {
this.successorFunction = successorFunction;
}
static <N> Traversal<N> inGraph(SuccessorsFunction<N> graph) {
final Set<N> visited = new HashSet<>();
return new Traversal<N>(graph) {
@Override
N visitNext(Deque<Iterator<? extends N>> horizon) {
Iterator<? extends N> top = horizon.getFirst();
while (top.hasNext()) {
N element = checkNotNull(top.next());
if (visited.add(element)) {
return element;
}
}
horizon.removeFirst();
return null;
}
};
}
static <N> Traversal<N> inTree(SuccessorsFunction<N> tree) {
return new Traversal<N>(tree) {
@Override
N visitNext(Deque<Iterator<? extends N>> horizon) {
Iterator<? extends N> top = horizon.getFirst();
if (top.hasNext()) {
return checkNotNull(top.next());
}
horizon.removeFirst();
return null;
}
};
}
final Iterator<N> breadthFirst(Iterator<? extends N> startNodes) {
return topDown(startNodes, InsertionOrder.BACK);
}
final Iterator<N> preOrder(Iterator<? extends N> startNodes) {
return topDown(startNodes, InsertionOrder.FRONT);
}
/**
* In top-down traversal, an ancestor node is always traversed before any of its descendant
* nodes. The traversal order among descendant nodes (particularly aunts and nieces) are
* determined by the {@code InsertionOrder} parameter: nieces are placed at the FRONT before
* aunts for pre-order; while in BFS they are placed at the BACK after aunts.
*/
private Iterator<N> topDown(Iterator<? extends N> startNodes, final InsertionOrder order) {
final Deque<Iterator<? extends N>> horizon = new ArrayDeque<>();
horizon.add(startNodes);
return new AbstractIterator<N>() {
@Override
protected N computeNext() {
do {
N next = visitNext(horizon);
if (next != null) {
Iterator<? extends N> successors = successorFunction.successors(next).iterator();
if (successors.hasNext()) {
// BFS: horizon.addLast(successors)
// Pre-order: horizon.addFirst(successors)
order.insertInto(horizon, successors);
}
return next;
}
} while (!horizon.isEmpty());
return endOfData();
}
};
}
final Iterator<N> postOrder(Iterator<? extends N> startNodes) {
final Deque<N> ancestorStack = new ArrayDeque<>();
final Deque<Iterator<? extends N>> horizon = new ArrayDeque<>();
horizon.add(startNodes);
return new AbstractIterator<N>() {
@Override
protected N computeNext() {
for (N next = visitNext(horizon); next != null; next = visitNext(horizon)) {
Iterator<? extends N> successors = successorFunction.successors(next).iterator();
if (!successors.hasNext()) {
return next;
}
horizon.addFirst(successors);
ancestorStack.push(next);
}
return ancestorStack.isEmpty() ? endOfData() : ancestorStack.pop();
}
};
}
/**
* Visits the next node from the top iterator of {@code horizon} and returns the visited node.
* Null is returned to indicate reaching the end of the top iterator.
*
* <p>For example, if horizon is {@code [[a, b], [c, d], [e]]}, {@code visitNext()} will return
* {@code [a, b, null, c, d, null, e, null]} sequentially, encoding the topological structure.
* (Note, however, that the callers of {@code visitNext()} often insert additional iterators
* into {@code horizon} between calls to {@code visitNext()}. This causes them to receive
* additional values interleaved with those shown above.)
*/
abstract N visitNext(Deque<Iterator<? extends N>> horizon);
}
/** Poor man's method reference for {@code Deque::addFirst} and {@code Deque::addLast}. */
private enum InsertionOrder {
FRONT {
@Override
<T> void insertInto(Deque<T> deque, T value) {
deque.addFirst(value);
}
},
BACK {
@Override
<T> void insertInto(Deque<T> deque, T value) {
deque.addLast(value);
}
};
abstract <T> void insertInto(Deque<T> deque, T value);
} }
} }

View file

@ -110,18 +110,18 @@ final class Crc32cHashFunction extends AbstractHashFunction {
0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351
}; };
private int crc = 0; private int crc = ~0;
@Override @Override
public void update(byte b) { public void update(byte b) {
crc ^= 0xFFFFFFFF; crc ^= 0xFFFFFFFF;
// See Hacker's Delight 2nd Edition, Figure 14-7. // See Hacker's Delight 2nd Edition, Figure 14-7.
crc = ~((crc >>> 8) ^ CRC_TABLE[(crc ^ b) & 0xFF]); crc = (crc >>> 8) ^ CRC_TABLE[(crc ^ b) & 0xFF];
} }
@Override @Override
public HashCode hash() { public HashCode hash() {
return HashCode.fromInt(crc); return HashCode.fromInt(~crc);
} }
} }
} }

View file

@ -39,7 +39,7 @@ import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel; import java.nio.channels.WritableByteChannel;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Arrays; import java.util.Arrays;
import java.util.Deque; import java.util.Queue;
/** /**
* Provides utility methods for working with byte arrays and I/O streams. * Provides utility methods for working with byte arrays and I/O streams.
@ -164,7 +164,7 @@ public final class ByteStreams {
* a total combined length of {@code totalLen} bytes) followed by all bytes remaining in the given * a total combined length of {@code totalLen} bytes) followed by all bytes remaining in the given
* input stream. * input stream.
*/ */
private static byte[] toByteArrayInternal(InputStream in, Deque<byte[]> bufs, int totalLen) private static byte[] toByteArrayInternal(InputStream in, Queue<byte[]> bufs, int totalLen)
throws IOException { throws IOException {
// Starting with an 8k buffer, double the size of each sucessive buffer. Buffers are retained // Starting with an 8k buffer, double the size of each sucessive buffer. Buffers are retained
// in a deque so that there's no copying between buffers while reading and so all of the bytes // in a deque so that there's no copying between buffers while reading and so all of the bytes
@ -195,11 +195,11 @@ public final class ByteStreams {
} }
} }
private static byte[] combineBuffers(Deque<byte[]> bufs, int totalLen) { private static byte[] combineBuffers(Queue<byte[]> bufs, int totalLen) {
byte[] result = new byte[totalLen]; byte[] result = new byte[totalLen];
int remaining = totalLen; int remaining = totalLen;
while (remaining > 0) { while (remaining > 0) {
byte[] buf = bufs.removeFirst(); byte[] buf = bufs.remove();
int bytesToCopy = Math.min(remaining, buf.length); int bytesToCopy = Math.min(remaining, buf.length);
int resultOffset = totalLen - remaining; int resultOffset = totalLen - remaining;
System.arraycopy(buf, 0, result, resultOffset, bytesToCopy); System.arraycopy(buf, 0, result, resultOffset, bytesToCopy);
@ -252,7 +252,7 @@ public final class ByteStreams {
} }
// the stream was longer, so read the rest normally // the stream was longer, so read the rest normally
Deque<byte[]> bufs = new ArrayDeque<byte[]>(TO_BYTE_ARRAY_DEQUE_SIZE + 2); Queue<byte[]> bufs = new ArrayDeque<byte[]>(TO_BYTE_ARRAY_DEQUE_SIZE + 2);
bufs.add(bytes); bufs.add(bytes);
bufs.add(new byte[] {(byte) b}); bufs.add(new byte[] {(byte) b});
return toByteArrayInternal(in, bufs, bytes.length + 1); return toByteArrayInternal(in, bufs, bytes.length + 1);

View file

@ -24,6 +24,7 @@ import com.google.common.base.Joiner;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.TreeTraverser; import com.google.common.collect.TreeTraverser;
@ -398,6 +399,11 @@ public final class Files {
* be exploited to create security vulnerabilities, especially when executable files are to be * be exploited to create security vulnerabilities, especially when executable files are to be
* written into the directory. * written into the directory.
* *
* <p>Depending on the environmment that this code is run in, the system temporary directory (and
* thus the directory this method creates) may be more visible that a program would like - files
* written to this directory may be read or overwritten by hostile programs running on the same
* machine.
*
* <p>This method assumes that the temporary volume is writable, has free inodes and free blocks, * <p>This method assumes that the temporary volume is writable, has free inodes and free blocks,
* and that it will not be called thousands of times per second. * and that it will not be called thousands of times per second.
* *
@ -812,36 +818,6 @@ public final class Files {
return (dotIndex == -1) ? fileName : fileName.substring(0, dotIndex); return (dotIndex == -1) ? fileName : fileName.substring(0, dotIndex);
} }
/**
* Returns a {@link TreeTraverser} instance for {@link File} trees.
*
* <p><b>Warning:</b> {@code File} provides no support for symbolic links, and as such there is no
* way to ensure that a symbolic link to a directory is not followed when traversing the tree. In
* this case, iterables created by this traverser could contain files that are outside of the
* given directory or even be infinite if there is a symbolic link loop.
*
* @since 15.0
* @deprecated The returned {@link TreeTraverser} type is deprecated. Use the replacement method
* {@link #fileTraverser()} instead with the same semantics as this method.
*/
@Deprecated
static TreeTraverser<File> fileTreeTraverser() {
return FILE_TREE_TRAVERSER;
}
private static final TreeTraverser<File> FILE_TREE_TRAVERSER =
new TreeTraverser<File>() {
@Override
public Iterable<File> children(File file) {
return fileTreeChildren(file);
}
@Override
public String toString() {
return "Files.fileTreeTraverser()";
}
};
/** /**
* Returns a {@link Traverser} instance for the file and directory tree. The returned traverser * Returns a {@link Traverser} instance for the file and directory tree. The returned traverser
* starts from a {@link File} and will return all files and directories it encounters. * starts from a {@link File} and will return all files and directories it encounters.
@ -870,12 +846,16 @@ public final class Files {
} }
private static final SuccessorsFunction<File> FILE_TREE = private static final SuccessorsFunction<File> FILE_TREE =
new SuccessorsFunction<File>() { file -> {
@Override // check isDirectory() just because it may be faster than listFiles() on a non-directory
public Iterable<File> successors(File file) { if (file.isDirectory()) {
return fileTreeChildren(file); File[] files = file.listFiles();
} if (files != null) {
}; return Collections.unmodifiableList(Arrays.asList(files));
}
}
return ImmutableList.of(); };
private static Iterable<File> fileTreeChildren(File file) { private static Iterable<File> fileTreeChildren(File file) {
// check isDirectory() just because it may be faster than listFiles() on a non-directory // check isDirectory() just because it may be faster than listFiles() on a non-directory

View file

@ -0,0 +1,81 @@
/*
* Copyright (C) 2020 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.math;
import com.google.common.annotations.GwtIncompatible;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* A class for arithmetic on {@link BigDecimal} that is not covered by its built-in methods.
*
* @author Louis Wasserman
* @since 30.0
*/
@GwtIncompatible
public class BigDecimalMath {
private BigDecimalMath() {}
/**
* Returns {@code x}, rounded to a {@code double} with the specified rounding mode. If {@code x}
* is precisely representable as a {@code double}, its {@code double} value will be returned;
* otherwise, the rounding will choose between the two nearest representable values with {@code
* mode}.
*
* <p>For the case of {@link RoundingMode#HALF_DOWN}, {@code HALF_UP}, and {@code HALF_EVEN},
* infinite {@code double} values are considered infinitely far away. For example, 2^2000 is not
* representable as a double, but {@code roundToDouble(BigDecimal.valueOf(2).pow(2000), HALF_UP)}
* will return {@code Double.MAX_VALUE}, not {@code Double.POSITIVE_INFINITY}.
*
* <p>For the case of {@link RoundingMode#HALF_EVEN}, this implementation uses the IEEE 754
* default rounding mode: if the two nearest representable values are equally near, the one with
* the least significant bit zero is chosen. (In such cases, both of the nearest representable
* values are even integers; this method returns the one that is a multiple of a greater power of
* two.)
*
* @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code x}
* is not precisely representable as a {@code double}
* @since 30.0
*/
public static double roundToDouble(BigDecimal x, RoundingMode mode) {
return BigDecimalToDoubleRounder.INSTANCE.roundToDouble(x, mode);
}
private static class BigDecimalToDoubleRounder extends ToDoubleRounder<BigDecimal> {
static final BigDecimalToDoubleRounder INSTANCE = new BigDecimalToDoubleRounder();
private BigDecimalToDoubleRounder() {}
@Override
double roundToDoubleArbitrarily(BigDecimal bigDecimal) {
return bigDecimal.doubleValue();
}
@Override
int sign(BigDecimal bigDecimal) {
return bigDecimal.signum();
}
@Override
BigDecimal toX(double d, RoundingMode mode) {
return new BigDecimal(d);
}
@Override
BigDecimal minus(BigDecimal a, BigDecimal b) {
return a.subtract(b);
}
}
}

View file

@ -21,6 +21,7 @@ import static com.google.common.math.MathPreconditions.checkPositive;
import static com.google.common.math.MathPreconditions.checkRoundingUnnecessary; import static com.google.common.math.MathPreconditions.checkRoundingUnnecessary;
import static java.math.RoundingMode.CEILING; import static java.math.RoundingMode.CEILING;
import static java.math.RoundingMode.FLOOR; import static java.math.RoundingMode.FLOOR;
import static java.math.RoundingMode.HALF_DOWN;
import static java.math.RoundingMode.HALF_EVEN; import static java.math.RoundingMode.HALF_EVEN;
import com.google.common.annotations.Beta; import com.google.common.annotations.Beta;
@ -56,7 +57,7 @@ public final class BigIntegerMath {
*/ */
@Beta @Beta
public static BigInteger ceilingPowerOfTwo(BigInteger x) { public static BigInteger ceilingPowerOfTwo(BigInteger x) {
return BigInteger.ZERO.setBit(log2(x, RoundingMode.CEILING)); return BigInteger.ZERO.setBit(log2(x, CEILING));
} }
/** /**
@ -68,7 +69,7 @@ public final class BigIntegerMath {
*/ */
@Beta @Beta
public static BigInteger floorPowerOfTwo(BigInteger x) { public static BigInteger floorPowerOfTwo(BigInteger x) {
return BigInteger.ZERO.setBit(log2(x, RoundingMode.FLOOR)); return BigInteger.ZERO.setBit(log2(x, FLOOR));
} }
/** Returns {@code true} if {@code x} represents a power of two. */ /** Returns {@code true} if {@code x} represents a power of two. */
@ -306,6 +307,59 @@ public final class BigIntegerMath {
return DoubleMath.roundToBigInteger(Math.sqrt(DoubleUtils.bigToDouble(x)), HALF_EVEN); return DoubleMath.roundToBigInteger(Math.sqrt(DoubleUtils.bigToDouble(x)), HALF_EVEN);
} }
/**
* Returns {@code x}, rounded to a {@code double} with the specified rounding mode. If {@code x}
* is precisely representable as a {@code double}, its {@code double} value will be returned;
* otherwise, the rounding will choose between the two nearest representable values with {@code
* mode}.
*
* <p>For the case of {@link RoundingMode#HALF_DOWN}, {@code HALF_UP}, and {@code HALF_EVEN},
* infinite {@code double} values are considered infinitely far away. For example, 2^2000 is not
* representable as a double, but {@code roundToDouble(BigInteger.valueOf(2).pow(2000), HALF_UP)}
* will return {@code Double.MAX_VALUE}, not {@code Double.POSITIVE_INFINITY}.
*
* <p>For the case of {@link RoundingMode#HALF_EVEN}, this implementation uses the IEEE 754
* default rounding mode: if the two nearest representable values are equally near, the one with
* the least significant bit zero is chosen. (In such cases, both of the nearest representable
* values are even integers; this method returns the one that is a multiple of a greater power of
* two.)
*
* @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code x}
* is not precisely representable as a {@code double}
* @since 30.0
*/
@GwtIncompatible
public static double roundToDouble(BigInteger x, RoundingMode mode) {
return BigIntegerToDoubleRounder.INSTANCE.roundToDouble(x, mode);
}
@GwtIncompatible
private static class BigIntegerToDoubleRounder extends ToDoubleRounder<BigInteger> {
static final BigIntegerToDoubleRounder INSTANCE = new BigIntegerToDoubleRounder();
private BigIntegerToDoubleRounder() {}
@Override
double roundToDoubleArbitrarily(BigInteger bigInteger) {
return DoubleUtils.bigToDouble(bigInteger);
}
@Override
int sign(BigInteger bigInteger) {
return bigInteger.signum();
}
@Override
BigInteger toX(double d, RoundingMode mode) {
return DoubleMath.roundToBigInteger(d, mode);
}
@Override
BigInteger minus(BigInteger a, BigInteger b) {
return a.subtract(b);
}
}
/** /**
* Returns the result of dividing {@code p} by {@code q}, rounding using the specified {@code * Returns the result of dividing {@code p} by {@code q}, rounding using the specified {@code
* RoundingMode}. * RoundingMode}.
@ -432,7 +486,7 @@ public final class BigIntegerMath {
long numeratorAccum = n; long numeratorAccum = n;
long denominatorAccum = 1; long denominatorAccum = 1;
int bits = LongMath.log2(n, RoundingMode.CEILING); int bits = LongMath.log2(n, CEILING);
int numeratorBits = bits; int numeratorBits = bits;

View file

@ -29,6 +29,7 @@ import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.GwtIncompatible;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.primitives.Longs;
import com.google.common.primitives.UnsignedLongs; import com.google.common.primitives.UnsignedLongs;
import java.math.BigInteger; import java.math.BigInteger;
import java.math.RoundingMode; import java.math.RoundingMode;
@ -1004,10 +1005,30 @@ public final class LongMath {
checkNonNegative("n", n); checkNonNegative("n", n);
return false; return false;
} }
if (n == 2 || n == 3 || n == 5 || n == 7 || n == 11 || n == 13) { if (n < 66) {
return true; // Encode all primes less than 66 into mask without 0 and 1.
long mask =
(1L << (2 - 2))
| (1L << (3 - 2))
| (1L << (5 - 2))
| (1L << (7 - 2))
| (1L << (11 - 2))
| (1L << (13 - 2))
| (1L << (17 - 2))
| (1L << (19 - 2))
| (1L << (23 - 2))
| (1L << (29 - 2))
| (1L << (31 - 2))
| (1L << (37 - 2))
| (1L << (41 - 2))
| (1L << (43 - 2))
| (1L << (47 - 2))
| (1L << (53 - 2))
| (1L << (59 - 2))
| (1L << (61 - 2));
// Look up n within the mask.
return ((mask >> ((int) n - 2)) & 1) != 0;
} }
if ((SIEVE_30 & (1 << (n % 30))) != 0) { if ((SIEVE_30 & (1 << (n % 30))) != 0) {
return false; return false;
} }
@ -1069,10 +1090,10 @@ public final class LongMath {
@Override @Override
long mulMod(long a, long b, long m) { long mulMod(long a, long b, long m) {
/* /*
* NOTE(lowasser, 2015-Feb-12): Benchmarks suggest that changing this to * lowasser, 2015-Feb-12: Benchmarks suggest that changing this to UnsignedLongs.remainder
* UnsignedLongs.remainder and increasing the threshold to 2^32 doesn't pay for itself, and * and increasing the threshold to 2^32 doesn't pay for itself, and adding another enum
* adding another enum constant hurts performance further -- I suspect because bimorphic * constant hurts performance further -- I suspect because bimorphic implementation is a
* implementation is a sweet spot for the JVM. * sweet spot for the JVM.
*/ */
return (a * b) % m; return (a * b) % m;
} }
@ -1203,5 +1224,125 @@ public final class LongMath {
} }
} }
/**
* Returns {@code x}, rounded to a {@code double} with the specified rounding mode. If {@code x}
* is precisely representable as a {@code double}, its {@code double} value will be returned;
* otherwise, the rounding will choose between the two nearest representable values with {@code
* mode}.
*
* <p>For the case of {@link RoundingMode#HALF_EVEN}, this implementation uses the IEEE 754
* default rounding mode: if the two nearest representable values are equally near, the one with
* the least significant bit zero is chosen. (In such cases, both of the nearest representable
* values are even integers; this method returns the one that is a multiple of a greater power of
* two.)
*
* @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code x}
* is not precisely representable as a {@code double}
* @since 30.0
*/
@SuppressWarnings("deprecation")
@GwtIncompatible
public static double roundToDouble(long x, RoundingMode mode) {
// Logic adapted from ToDoubleRounder.
double roundArbitrarily = (double) x;
long roundArbitrarilyAsLong = (long) roundArbitrarily;
int cmpXToRoundArbitrarily;
if (roundArbitrarilyAsLong == Long.MAX_VALUE) {
/*
* For most values, the conversion from roundArbitrarily to roundArbitrarilyAsLong is
* lossless. In that case we can compare x to roundArbitrarily using Longs.compare(x,
* roundArbitrarilyAsLong). The exception is for values where the conversion to double rounds
* up to give roundArbitrarily equal to 2^63, so the conversion back to long overflows and
* roundArbitrarilyAsLong is Long.MAX_VALUE. (This is the only way this condition can occur as
* otherwise the conversion back to long pads with zero bits.) In this case we know that
* roundArbitrarily > x. (This is important when x == Long.MAX_VALUE ==
* roundArbitrarilyAsLong.)
*/
cmpXToRoundArbitrarily = -1;
} else {
cmpXToRoundArbitrarily = Longs.compare(x, roundArbitrarilyAsLong);
}
switch (mode) {
case UNNECESSARY:
checkRoundingUnnecessary(cmpXToRoundArbitrarily == 0);
return roundArbitrarily;
case FLOOR:
return (cmpXToRoundArbitrarily >= 0)
? roundArbitrarily
: DoubleUtils.nextDown(roundArbitrarily);
case CEILING:
return (cmpXToRoundArbitrarily <= 0) ? roundArbitrarily : Math.nextUp(roundArbitrarily);
case DOWN:
if (x >= 0) {
return (cmpXToRoundArbitrarily >= 0)
? roundArbitrarily
: DoubleUtils.nextDown(roundArbitrarily);
} else {
return (cmpXToRoundArbitrarily <= 0) ? roundArbitrarily : Math.nextUp(roundArbitrarily);
}
case UP:
if (x >= 0) {
return (cmpXToRoundArbitrarily <= 0) ? roundArbitrarily : Math.nextUp(roundArbitrarily);
} else {
return (cmpXToRoundArbitrarily >= 0)
? roundArbitrarily
: DoubleUtils.nextDown(roundArbitrarily);
}
case HALF_DOWN:
case HALF_UP:
case HALF_EVEN:
{
long roundFloor;
double roundFloorAsDouble;
long roundCeiling;
double roundCeilingAsDouble;
if (cmpXToRoundArbitrarily >= 0) {
roundFloorAsDouble = roundArbitrarily;
roundFloor = roundArbitrarilyAsLong;
roundCeilingAsDouble = Math.nextUp(roundArbitrarily);
roundCeiling = (long) Math.ceil(roundCeilingAsDouble);
} else {
roundCeilingAsDouble = roundArbitrarily;
roundCeiling = roundArbitrarilyAsLong;
roundFloorAsDouble = DoubleUtils.nextDown(roundArbitrarily);
roundFloor = (long) Math.floor(roundFloorAsDouble);
}
long deltaToFloor = x - roundFloor;
long deltaToCeiling = roundCeiling - x;
if (roundCeiling == Long.MAX_VALUE) {
// correct for Long.MAX_VALUE as discussed above: roundCeilingAsDouble must be 2^63, but
// roundCeiling is 2^63-1.
deltaToCeiling++;
}
int diff = Longs.compare(deltaToFloor, deltaToCeiling);
if (diff < 0) { // closer to floor
return roundFloorAsDouble;
} else if (diff > 0) { // closer to ceiling
return roundCeilingAsDouble;
}
// halfway between the representable values; do the half-whatever logic
switch (mode) {
case HALF_EVEN:
return ((DoubleUtils.getSignificand(roundFloorAsDouble) & 1L) == 0)
? roundFloorAsDouble
: roundCeilingAsDouble;
case HALF_DOWN:
return (x >= 0) ? roundFloorAsDouble : roundCeilingAsDouble;
case HALF_UP:
return (x >= 0) ? roundCeilingAsDouble : roundFloorAsDouble;
default:
throw new AssertionError("impossible");
}
}
}
throw new AssertionError("impossible");
}
private LongMath() {} private LongMath() {}
} }

View file

@ -424,11 +424,11 @@ public final class Stats implements Serializable {
return false; return false;
} }
Stats other = (Stats) obj; Stats other = (Stats) obj;
return (count == other.count) return count == other.count
&& (doubleToLongBits(mean) == doubleToLongBits(other.mean)) && doubleToLongBits(mean) == doubleToLongBits(other.mean)
&& (doubleToLongBits(sumOfSquaresOfDeltas) == doubleToLongBits(other.sumOfSquaresOfDeltas)) && doubleToLongBits(sumOfSquaresOfDeltas) == doubleToLongBits(other.sumOfSquaresOfDeltas)
&& (doubleToLongBits(min) == doubleToLongBits(other.min)) && doubleToLongBits(min) == doubleToLongBits(other.min)
&& (doubleToLongBits(max) == doubleToLongBits(other.max)); && doubleToLongBits(max) == doubleToLongBits(other.max);
} }
/** /**

View file

@ -0,0 +1,152 @@
/*
* Copyright (C) 2020 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.math;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.math.MathPreconditions.checkRoundingUnnecessary;
import com.google.common.annotations.GwtIncompatible;
import java.math.RoundingMode;
/**
* Helper type to implement rounding {@code X} to a representable {@code double} value according to
* a {@link RoundingMode}.
*/
@GwtIncompatible
abstract class ToDoubleRounder<X extends Number & Comparable<X>> {
/**
* Returns x rounded to either the greatest double less than or equal to the precise value of x,
* or the least double greater than or equal to the precise value of x.
*/
abstract double roundToDoubleArbitrarily(X x);
/** Returns the sign of x: either -1, 0, or 1. */
abstract int sign(X x);
/** Returns d's value as an X, rounded with the specified mode. */
abstract X toX(double d, RoundingMode mode);
/** Returns a - b, guaranteed that both arguments are nonnegative. */
abstract X minus(X a, X b);
/** Rounds {@code x} to a {@code double}. */
final double roundToDouble(X x, RoundingMode mode) {
checkNotNull(x, "x");
checkNotNull(mode, "mode");
double roundArbitrarily = roundToDoubleArbitrarily(x);
if (Double.isInfinite(roundArbitrarily)) {
switch (mode) {
case DOWN:
case HALF_EVEN:
case HALF_DOWN:
case HALF_UP:
return Double.MAX_VALUE * sign(x);
case FLOOR:
return (roundArbitrarily == Double.POSITIVE_INFINITY)
? Double.MAX_VALUE
: Double.NEGATIVE_INFINITY;
case CEILING:
return (roundArbitrarily == Double.POSITIVE_INFINITY)
? Double.POSITIVE_INFINITY
: -Double.MAX_VALUE;
case UP:
return roundArbitrarily;
case UNNECESSARY:
throw new ArithmeticException(x + " cannot be represented precisely as a double");
}
}
X roundArbitrarilyAsX = toX(roundArbitrarily, RoundingMode.UNNECESSARY);
int cmpXToRoundArbitrarily = x.compareTo(roundArbitrarilyAsX);
switch (mode) {
case UNNECESSARY:
checkRoundingUnnecessary(cmpXToRoundArbitrarily == 0);
return roundArbitrarily;
case FLOOR:
return (cmpXToRoundArbitrarily >= 0)
? roundArbitrarily
: DoubleUtils.nextDown(roundArbitrarily);
case CEILING:
return (cmpXToRoundArbitrarily <= 0) ? roundArbitrarily : Math.nextUp(roundArbitrarily);
case DOWN:
if (sign(x) >= 0) {
return (cmpXToRoundArbitrarily >= 0)
? roundArbitrarily
: DoubleUtils.nextDown(roundArbitrarily);
} else {
return (cmpXToRoundArbitrarily <= 0) ? roundArbitrarily : Math.nextUp(roundArbitrarily);
}
case UP:
if (sign(x) >= 0) {
return (cmpXToRoundArbitrarily <= 0) ? roundArbitrarily : Math.nextUp(roundArbitrarily);
} else {
return (cmpXToRoundArbitrarily >= 0)
? roundArbitrarily
: DoubleUtils.nextDown(roundArbitrarily);
}
case HALF_DOWN:
case HALF_UP:
case HALF_EVEN:
{
X roundFloor;
double roundFloorAsDouble;
X roundCeiling;
double roundCeilingAsDouble;
if (cmpXToRoundArbitrarily >= 0) {
roundFloorAsDouble = roundArbitrarily;
roundFloor = roundArbitrarilyAsX;
roundCeilingAsDouble = Math.nextUp(roundArbitrarily);
if (roundCeilingAsDouble == Double.POSITIVE_INFINITY) {
return roundFloorAsDouble;
}
roundCeiling = toX(roundCeilingAsDouble, RoundingMode.CEILING);
} else {
roundCeilingAsDouble = roundArbitrarily;
roundCeiling = roundArbitrarilyAsX;
roundFloorAsDouble = DoubleUtils.nextDown(roundArbitrarily);
if (roundFloorAsDouble == Double.NEGATIVE_INFINITY) {
return roundCeilingAsDouble;
}
roundFloor = toX(roundFloorAsDouble, RoundingMode.FLOOR);
}
X deltaToFloor = minus(x, roundFloor);
X deltaToCeiling = minus(roundCeiling, x);
int diff = deltaToFloor.compareTo(deltaToCeiling);
if (diff < 0) { // closer to floor
return roundFloorAsDouble;
} else if (diff > 0) { // closer to ceiling
return roundCeilingAsDouble;
}
// halfway between the representable values; do the half-whatever logic
switch (mode) {
case HALF_EVEN:
// roundFloorAsDouble and roundCeilingAsDouble are neighbors, so precisely
// one of them should have an even long representation
return ((Double.doubleToRawLongBits(roundFloorAsDouble) & 1L) == 0)
? roundFloorAsDouble
: roundCeilingAsDouble;
case HALF_DOWN:
return (sign(x) >= 0) ? roundFloorAsDouble : roundCeilingAsDouble;
case HALF_UP:
return (sign(x) >= 0) ? roundCeilingAsDouble : roundFloorAsDouble;
default:
throw new AssertionError("impossible");
}
}
}
throw new AssertionError("impossible");
}
}

View file

@ -129,6 +129,13 @@ public final class HttpHeaders {
public static final String MAX_FORWARDS = "Max-Forwards"; public static final String MAX_FORWARDS = "Max-Forwards";
/** The HTTP {@code Origin} header field name. */ /** The HTTP {@code Origin} header field name. */
public static final String ORIGIN = "Origin"; public static final String ORIGIN = "Origin";
/**
* The HTTP <a href="https://github.com/WICG/origin-isolation">{@code Origin-Isolation}</a> header
* field name.
*
* @since 30.1
*/
public static final String ORIGIN_ISOLATION = "Origin-Isolation";
/** The HTTP {@code Proxy-Authorization} header field name. */ /** The HTTP {@code Proxy-Authorization} header field name. */
public static final String PROXY_AUTHORIZATION = "Proxy-Authorization"; public static final String PROXY_AUTHORIZATION = "Proxy-Authorization";
/** The HTTP {@code Range} header field name. */ /** The HTTP {@code Range} header field name. */
@ -266,6 +273,21 @@ public final class HttpHeaders {
* @since 20.0 * @since 20.0
*/ */
public static final String X_WEBKIT_CSP_REPORT_ONLY = "X-WebKit-CSP-Report-Only"; public static final String X_WEBKIT_CSP_REPORT_ONLY = "X-WebKit-CSP-Report-Only";
/**
* The HTTP <a href="https://wicg.github.io/cross-origin-embedder-policy/#COEP">{@code
* Cross-Origin-Embedder-Policy}</a> header field name.
*
* @since 30.0
*/
public static final String CROSS_ORIGIN_EMBEDDER_POLICY = "Cross-Origin-Embedder-Policy";
/**
* The HTTP <a href="https://wicg.github.io/cross-origin-embedder-policy/#COEP-RO">{@code
* Cross-Origin-Embedder-Policy-Report-Only}</a> header field name.
*
* @since 30.0
*/
public static final String CROSS_ORIGIN_EMBEDDER_POLICY_REPORT_ONLY =
"Cross-Origin-Embedder-Policy-Report-Only";
/** /**
* The HTTP Cross-Origin-Opener-Policy header field name. * The HTTP Cross-Origin-Opener-Policy header field name.
* *
@ -389,6 +411,12 @@ public final class HttpHeaders {
* @since 15.0 * @since 15.0
*/ */
@Beta public static final String PUBLIC_KEY_PINS = "Public-Key-Pins"; @Beta public static final String PUBLIC_KEY_PINS = "Public-Key-Pins";
/**
* The HTTP {@code X-Request-ID} header field name.
*
* @since 30.1
*/
public static final String X_REQUEST_ID = "X-Request-ID";
/** /**
* The HTTP <a href="http://tools.ietf.org/html/draft-evans-palmer-key-pinning">{@code * The HTTP <a href="http://tools.ietf.org/html/draft-evans-palmer-key-pinning">{@code
* Public-Key-Pins-Report-Only}</a> header field name. * Public-Key-Pins-Report-Only}</a> header field name.
@ -459,6 +487,56 @@ public final class HttpHeaders {
*/ */
public static final String X_MOZ = "X-Moz"; public static final String X_MOZ = "X-Moz";
/**
* The HTTP <a href="https://wicg.github.io/ua-client-hints/#sec-ch-ua">{@code Sec-CH-UA}</a>
* header field name.
*
* @since 30.0
*/
public static final String SEC_CH_UA = "Sec-CH-UA";
/**
* The HTTP <a href="https://wicg.github.io/ua-client-hints/#sec-ch-arch">{@code
* Sec-CH-UA-Arch}</a> header field name.
*
* @since 30.0
*/
public static final String SEC_CH_UA_ARCH = "Sec-CH-UA-Arch";
/**
* The HTTP <a href="https://wicg.github.io/ua-client-hints/#sec-ch-model">{@code
* Sec-CH-UA-Model}</a> header field name.
*
* @since 30.0
*/
public static final String SEC_CH_UA_MODEL = "Sec-CH-UA-Model";
/**
* The HTTP <a href="https://wicg.github.io/ua-client-hints/#sec-ch-platform">{@code
* Sec-CH-UA-Platform}</a> header field name.
*
* @since 30.0
*/
public static final String SEC_CH_UA_PLATFORM = "Sec-CH-UA-Platform";
/**
* The HTTP <a href="https://wicg.github.io/ua-client-hints/#sec-ch-platform-version">{@code
* Sec-CH-UA-Platform-Version}</a> header field name.
*
* @since 30.0
*/
public static final String SEC_CH_UA_PLATFORM_VERSION = "Sec-CH-UA-Platform-Version";
/**
* The HTTP <a href="https://wicg.github.io/ua-client-hints/#sec-ch-full-version">{@code
* Sec-CH-UA-Full-Version}</a> header field name.
*
* @since 30.0
*/
public static final String SEC_CH_UA_FULL_VERSION = "Sec-CH-UA-Full-Version";
/**
* The HTTP <a href="https://wicg.github.io/ua-client-hints/#sec-ch-mobile">{@code
* Sec-CH-UA-Mobile}</a> header field name.
*
* @since 30.0
*/
public static final String SEC_CH_UA_MOBILE = "Sec-CH-UA-Mobile";
/** /**
* The HTTP <a href="https://w3c.github.io/webappsec-fetch-metadata/">{@code Sec-Fetch-Dest}</a> * The HTTP <a href="https://w3c.github.io/webappsec-fetch-metadata/">{@code Sec-Fetch-Dest}</a>
* header field name. * header field name.

View file

@ -19,9 +19,9 @@ import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.Beta; import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.CharMatcher;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.common.hash.Hashing; import com.google.common.hash.Hashing;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
@ -32,7 +32,6 @@ import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -104,6 +103,10 @@ public final class InetAddresses {
private static final int IPV6_PART_COUNT = 8; private static final int IPV6_PART_COUNT = 8;
private static final Splitter IPV4_SPLITTER = Splitter.on('.').limit(IPV4_PART_COUNT); private static final Splitter IPV4_SPLITTER = Splitter.on('.').limit(IPV4_PART_COUNT);
private static final Splitter IPV6_SPLITTER = Splitter.on(':').limit(IPV6_PART_COUNT + 2); private static final Splitter IPV6_SPLITTER = Splitter.on(':').limit(IPV6_PART_COUNT + 2);
private static final char IPV4_DELIMITER = '.';
private static final char IPV6_DELIMITER = ':';
private static final CharMatcher IPV4_DELIMITER_MATCHER = CharMatcher.is(IPV4_DELIMITER);
private static final CharMatcher IPV6_DELIMITER_MATCHER = CharMatcher.is(IPV6_DELIMITER);
private static final Inet4Address LOOPBACK4 = (Inet4Address) forString("127.0.0.1"); private static final Inet4Address LOOPBACK4 = (Inet4Address) forString("127.0.0.1");
private static final Inet4Address ANY4 = (Inet4Address) forString("0.0.0.0"); private static final Inet4Address ANY4 = (Inet4Address) forString("0.0.0.0");
@ -196,81 +199,100 @@ public final class InetAddresses {
} }
return textToNumericFormatV6(ipString); return textToNumericFormatV6(ipString);
} else if (hasDot) { } else if (hasDot) {
if (percentIndex != -1) {
return null; // Scope IDs are not supported for IPV4
}
return textToNumericFormatV4(ipString); return textToNumericFormatV4(ipString);
} }
return null; return null;
} }
private static byte [] textToNumericFormatV4(String ipString) { private static byte [] textToNumericFormatV4(String ipString) {
byte[] bytes = new byte[IPV4_PART_COUNT]; if (IPV4_DELIMITER_MATCHER.countIn(ipString) + 1 != IPV4_PART_COUNT) {
int i = 0; return null; // Wrong number of parts
try {
for (String octet : IPV4_SPLITTER.split(ipString)) {
bytes[i++] = parseOctet(octet);
}
} catch (NumberFormatException ex) {
return null;
} }
byte[] bytes = new byte[IPV4_PART_COUNT];
return i == IPV4_PART_COUNT ? bytes : null; int start = 0;
// Iterate through the parts of the ip string.
// Invariant: start is always the beginning of an octet.
for (int i = 0; i < IPV4_PART_COUNT; i++) {
int end = ipString.indexOf(IPV4_DELIMITER, start);
if (end == -1) {
end = ipString.length();
}
try {
bytes[i] = parseOctet(ipString, start, end);
} catch (NumberFormatException ex) {
return null;
}
start = end + 1;
}
return bytes;
} }
private static byte [] textToNumericFormatV6(String ipString) { private static byte[] textToNumericFormatV6(String ipString) {
// An address can have [2..8] colons, and N colons make N+1 parts. // An address can have [2..8] colons.
List<String> parts = IPV6_SPLITTER.splitToList(ipString); int delimiterCount = IPV6_DELIMITER_MATCHER.countIn(ipString);
if (parts.size() < 3 || parts.size() > IPV6_PART_COUNT + 1) { if (delimiterCount < 2 || delimiterCount > IPV6_PART_COUNT) {
return null; return null;
} }
int partsSkipped = IPV6_PART_COUNT - (delimiterCount + 1); // estimate; may be modified later
// Disregarding the endpoints, find "::" with nothing in between. boolean hasSkip = false;
// This indicates that a run of zeroes has been skipped. // Scan for the appearance of ::, to mark a skip-format IPV6 string and adjust the partsSkipped
int skipIndex = -1; // estimate.
for (int i = 1; i < parts.size() - 1; i++) { for (int i = 0; i < ipString.length() - 1; i++) {
if (parts.get(i).length() == 0) { if (ipString.charAt(i) == IPV6_DELIMITER && ipString.charAt(i + 1) == IPV6_DELIMITER) {
if (skipIndex >= 0) { if (hasSkip) {
return null; // Can't have more than one :: return null; // Can't have more than one ::
} }
skipIndex = i; hasSkip = true;
partsSkipped++; // :: means we skipped an extra part in between the two delimiters.
if (i == 0) {
partsSkipped++; // Begins with ::, so we skipped the part preceding the first :
}
if (i == ipString.length() - 2) {
partsSkipped++; // Ends with ::, so we skipped the part after the last :
}
} }
} }
if (ipString.charAt(0) == IPV6_DELIMITER && ipString.charAt(1) != IPV6_DELIMITER) {
int partsHi; // Number of parts to copy from above/before the "::" return null; // ^: requires ^::
int partsLo; // Number of parts to copy from below/after the "::" }
if (skipIndex >= 0) { if (ipString.charAt(ipString.length() - 1) == IPV6_DELIMITER
// If we found a "::", then check if it also covers the endpoints. && ipString.charAt(ipString.length() - 2) != IPV6_DELIMITER) {
partsHi = skipIndex; return null; // :$ requires ::$
partsLo = parts.size() - skipIndex - 1; }
if (parts.get(0).length() == 0 && --partsHi != 0) { if (hasSkip && partsSkipped <= 0) {
return null; // ^: requires ^:: return null; // :: must expand to at least one '0'
} }
if (Iterables.getLast(parts).length() == 0 && --partsLo != 0) { if (!hasSkip && delimiterCount + 1 != IPV6_PART_COUNT) {
return null; // :$ requires ::$ return null; // Incorrect number of parts
}
} else {
// Otherwise, allocate the entire address to partsHi. The endpoints
// could still be empty, but parseHextet() will check for that.
partsHi = parts.size();
partsLo = 0;
} }
// If we found a ::, then we must have skipped at least one part.
// Otherwise, we must have exactly the right number of parts.
int partsSkipped = IPV6_PART_COUNT - (partsHi + partsLo);
if (!(skipIndex >= 0 ? partsSkipped >= 1 : partsSkipped == 0)) {
return null;
}
// Now parse the hextets into a byte array.
ByteBuffer rawBytes = ByteBuffer.allocate(2 * IPV6_PART_COUNT); ByteBuffer rawBytes = ByteBuffer.allocate(2 * IPV6_PART_COUNT);
try { try {
for (int i = 0; i < partsHi; i++) { // Iterate through the parts of the ip string.
rawBytes.putShort(parseHextet(parts.get(i))); // Invariant: start is always the beginning of a hextet, or the second ':' of the skip
// sequence "::"
int start = 0;
if (ipString.charAt(0) == IPV6_DELIMITER) {
start = 1;
} }
for (int i = 0; i < partsSkipped; i++) { while (start < ipString.length()) {
rawBytes.putShort((short) 0); int end = ipString.indexOf(IPV6_DELIMITER, start);
} if (end == -1) {
for (int i = partsLo; i > 0; i--) { end = ipString.length();
rawBytes.putShort(parseHextet(parts.get(parts.size() - i))); }
if (ipString.charAt(start) == IPV6_DELIMITER) {
// expand zeroes
for (int i = 0; i < partsSkipped; i++) {
rawBytes.putShort((short) 0);
}
} else {
rawBytes.putShort(parseHextet(ipString, start, end));
}
start = end + 1;
} }
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
return null; return null;
@ -291,23 +313,45 @@ public final class InetAddresses {
return initialPart + penultimate + ":" + ultimate; return initialPart + penultimate + ":" + ultimate;
} }
private static byte parseOctet(String ipPart) { private static byte parseOctet(String ipString, int start, int end) {
// Note: we already verified that this string contains only hex digits. // Note: we already verified that this string contains only hex digits, but the string may still
int octet = Integer.parseInt(ipPart); // contain non-decimal characters.
int length = end - start;
if (length <= 0 || length > 3) {
throw new NumberFormatException();
}
// Disallow leading zeroes, because no clear standard exists on // Disallow leading zeroes, because no clear standard exists on
// whether these should be interpreted as decimal or octal. // whether these should be interpreted as decimal or octal.
if (octet > 255 || (ipPart.startsWith("0") && ipPart.length() > 1)) { if (length > 1 && ipString.charAt(start) == '0') {
throw new NumberFormatException();
}
int octet = 0;
for (int i = start; i < end; i++) {
octet *= 10;
int digit = Character.digit(ipString.charAt(i), 10);
if (digit < 0) {
throw new NumberFormatException();
}
octet += digit;
}
if (octet > 255) {
throw new NumberFormatException(); throw new NumberFormatException();
} }
return (byte) octet; return (byte) octet;
} }
private static short parseHextet(String ipPart) { // Parse a hextet out of the ipString from start (inclusive) to end (exclusive)
private static short parseHextet(String ipString, int start, int end) {
// Note: we already verified that this string contains only hex digits. // Note: we already verified that this string contains only hex digits.
int hextet = Integer.parseInt(ipPart, 16); int length = end - start;
if (hextet > 0xffff) { if (length <= 0 || length > 4) {
throw new NumberFormatException(); throw new NumberFormatException();
} }
int hextet = 0;
for (int i = start; i < end; i++) {
hextet = hextet << 4;
hextet |= Character.digit(ipString.charAt(i), 16);
}
return (short) hextet; return (short) hextet;
} }

View file

@ -101,6 +101,7 @@ public final class MediaType {
private static final String IMAGE_TYPE = "image"; private static final String IMAGE_TYPE = "image";
private static final String TEXT_TYPE = "text"; private static final String TEXT_TYPE = "text";
private static final String VIDEO_TYPE = "video"; private static final String VIDEO_TYPE = "video";
private static final String FONT_TYPE = "font";
private static final String WILDCARD = "*"; private static final String WILDCARD = "*";
@ -140,6 +141,7 @@ public final class MediaType {
public static final MediaType ANY_AUDIO_TYPE = createConstant(AUDIO_TYPE, WILDCARD); public static final MediaType ANY_AUDIO_TYPE = createConstant(AUDIO_TYPE, WILDCARD);
public static final MediaType ANY_VIDEO_TYPE = createConstant(VIDEO_TYPE, WILDCARD); public static final MediaType ANY_VIDEO_TYPE = createConstant(VIDEO_TYPE, WILDCARD);
public static final MediaType ANY_APPLICATION_TYPE = createConstant(APPLICATION_TYPE, WILDCARD); public static final MediaType ANY_APPLICATION_TYPE = createConstant(APPLICATION_TYPE, WILDCARD);
public static final MediaType ANY_FONT_TYPE = createConstant(FONT_TYPE, WILDCARD);
/* text types */ /* text types */
public static final MediaType CACHE_MANIFEST_UTF_8 = public static final MediaType CACHE_MANIFEST_UTF_8 =
@ -695,6 +697,60 @@ public final class MediaType {
public static final MediaType ZIP = createConstant(APPLICATION_TYPE, "zip"); public static final MediaType ZIP = createConstant(APPLICATION_TYPE, "zip");
/**
* A collection of font outlines as defined by <a href="https://tools.ietf.org/html/rfc8081">RFC
* 8081</a>.
*
* @since 30.0
*/
public static final MediaType FONT_COLLECTION = createConstant(FONT_TYPE, "collection");
/**
* <a href="https://en.wikipedia.org/wiki/OpenType">Open Type Font Format</a> (OTF) as defined by
* <a href="https://tools.ietf.org/html/rfc8081">RFC 8081</a>.
*
* @since 30.0
*/
public static final MediaType FONT_OTF = createConstant(FONT_TYPE, "otf");
/**
* <a href="https://en.wikipedia.org/wiki/SFNT">Spline or Scalable Font Format</a> (SFNT). <a
* href="https://tools.ietf.org/html/rfc8081">RFC 8081</a> declares this to be the correct media
* type for SFNT, but {@link #SFNT application/font-sfnt} may be necessary in certain situations
* for compatibility.
*
* @since 30.0
*/
public static final MediaType FONT_SFNT = createConstant(FONT_TYPE, "sfnt");
/**
* <a href="https://en.wikipedia.org/wiki/TrueType">True Type Font Format</a> (TTF) as defined by
* <a href="https://tools.ietf.org/html/rfc8081">RFC 8081</a>.
*
* @since 30.0
*/
public static final MediaType FONT_TTF = createConstant(FONT_TYPE, "ttf");
/**
* <a href="http://en.wikipedia.org/wiki/Web_Open_Font_Format">Web Open Font Format</a> (WOFF). <a
* href="https://tools.ietf.org/html/rfc8081">RFC 8081</a> declares this to be the correct media
* type for SFNT, but {@link #WOFF application/font-woff} may be necessary in certain situations
* for compatibility.
*
* @since 30.0
*/
public static final MediaType FONT_WOFF = createConstant(FONT_TYPE, "woff");
/**
* <a href="http://en.wikipedia.org/wiki/Web_Open_Font_Format">Web Open Font Format</a> (WOFF2).
* <a href="https://tools.ietf.org/html/rfc8081">RFC 8081</a> declares this to be the correct
* media type for SFNT, but {@link #WOFF2 application/font-woff2} may be necessary in certain
* situations for compatibility.
*
* @since 30.0
*/
public static final MediaType FONT_WOFF2 = createConstant(FONT_TYPE, "woff2");
private final String type; private final String type;
private final String subtype; private final String subtype;
private final ImmutableListMultimap<String, String> parameters; private final ImmutableListMultimap<String, String> parameters;
@ -931,6 +987,15 @@ public final class MediaType {
return create(AUDIO_TYPE, subtype); return create(AUDIO_TYPE, subtype);
} }
/**
* Creates a media type with the "font" type and the given subtype.
*
* @throws IllegalArgumentException if subtype is invalid
*/
static MediaType createFontType(String subtype) {
return create(FONT_TYPE, subtype);
}
/** /**
* Creates a media type with the "image" type and the given subtype. * Creates a media type with the "image" type and the given subtype.
* *

View file

@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkElementIndex; import static com.google.common.base.Preconditions.checkElementIndex;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkPositionIndexes; import static com.google.common.base.Preconditions.checkPositionIndexes;
import static com.google.common.base.Strings.lenientFormat;
import static java.lang.Double.NEGATIVE_INFINITY; import static java.lang.Double.NEGATIVE_INFINITY;
import static java.lang.Double.POSITIVE_INFINITY; import static java.lang.Double.POSITIVE_INFINITY;
@ -251,8 +252,13 @@ public final class Doubles {
*/ */
@Beta @Beta
public static double constrainToRange(double value, double min, double max) { public static double constrainToRange(double value, double min, double max) {
checkArgument(min <= max, "min (%s) must be less than or equal to max (%s)", min, max); // avoid auto-boxing by not using Preconditions.checkArgument(); see Guava issue 3984
return Math.min(Math.max(value, min), max); // Reject NaN by testing for the good case (min <= max) instead of the bad (min > max).
if (min <= max) {
return Math.min(Math.max(value, min), max);
}
throw new IllegalArgumentException(
lenientFormat("min (%s) must be less than or equal to max (%s)", min, max));
} }
/** /**

View file

@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkElementIndex; import static com.google.common.base.Preconditions.checkElementIndex;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkPositionIndexes; import static com.google.common.base.Preconditions.checkPositionIndexes;
import static com.google.common.base.Strings.lenientFormat;
import static java.lang.Float.NEGATIVE_INFINITY; import static java.lang.Float.NEGATIVE_INFINITY;
import static java.lang.Float.POSITIVE_INFINITY; import static java.lang.Float.POSITIVE_INFINITY;
@ -246,8 +247,13 @@ public final class Floats {
*/ */
@Beta @Beta
public static float constrainToRange(float value, float min, float max) { public static float constrainToRange(float value, float min, float max) {
checkArgument(min <= max, "min (%s) must be less than or equal to max (%s)", min, max); // avoid auto-boxing by not using Preconditions.checkArgument(); see Guava issue 3984
return Math.min(Math.max(value, min), max); // Reject NaN by testing for the good case (min <= max) instead of the bad (min > max).
if (min <= max) {
return Math.min(Math.max(value, min), max);
}
throw new IllegalArgumentException(
lenientFormat("min (%s) must be less than or equal to max (%s)", min, max));
} }
/** /**

View file

@ -30,9 +30,6 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
import com.google.common.io.ByteSource; import com.google.common.io.ByteSource;
import com.google.common.io.CharSource; import com.google.common.io.CharSource;
import com.google.common.io.Resources; import com.google.common.io.Resources;
@ -46,7 +43,7 @@ import java.nio.charset.Charset;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map.Entry; import java.util.Map;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Set; import java.util.Set;
import java.util.jar.Attributes; import java.util.jar.Attributes;
@ -78,14 +75,6 @@ import java.util.logging.Logger;
public final class ClassPath { public final class ClassPath {
private static final Logger logger = Logger.getLogger(ClassPath.class.getName()); private static final Logger logger = Logger.getLogger(ClassPath.class.getName());
private static final Predicate<ClassInfo> IS_TOP_LEVEL =
new Predicate<ClassInfo>() {
@Override
public boolean apply(ClassInfo info) {
return info.className.indexOf('$') == -1;
}
};
/** Separator for the Class-Path manifest attribute value in jar files. */ /** Separator for the Class-Path manifest attribute value in jar files. */
private static final Splitter CLASS_PATH_ATTRIBUTE_SEPARATOR = private static final Splitter CLASS_PATH_ATTRIBUTE_SEPARATOR =
Splitter.on(" ").omitEmptyStrings(); Splitter.on(" ").omitEmptyStrings();
@ -115,9 +104,21 @@ public final class ClassPath {
* failed. * failed.
*/ */
public static ClassPath from(ClassLoader classloader) throws IOException { public static ClassPath from(ClassLoader classloader) throws IOException {
DefaultScanner scanner = new DefaultScanner(); ImmutableSet<LocationInfo> locations = locationsFrom(classloader);
scanner.scan(classloader);
return new ClassPath(scanner.getResources()); // Add all locations to the scanned set so that in a classpath [jar1, jar2], where jar1 has a
// manifest with Class-Path pointing to jar2, we won't scan jar2 twice.
Set<File> scanned = new HashSet<>();
for (LocationInfo location : locations) {
scanned.add(location.file());
}
// Scan all locations
ImmutableSet.Builder<ResourceInfo> builder = ImmutableSet.builder();
for (LocationInfo location : locations) {
builder.addAll(location.scanResources(scanned));
}
return new ClassPath(builder.build());
} }
/** /**
@ -137,9 +138,20 @@ public final class ClassPath {
return FluentIterable.from(resources).filter(ClassInfo.class).toSet(); return FluentIterable.from(resources).filter(ClassInfo.class).toSet();
} }
/** Returns all top level classes loadable from the current class path. */ /**
public ImmutableSet<ClassInfo> getTopLevelClasses() { * Returns all top level classes loadable from the current class path. Note that "top-level-ness"
return FluentIterable.from(resources).filter(ClassInfo.class).filter(IS_TOP_LEVEL).toSet(); * is determined heuristically by class name (see {@link ClassInfo#isTopLevel}).
*/ public ImmutableSet<ClassInfo> getTopLevelClasses() {
return FluentIterable.from(resources)
.filter(ClassInfo.class)
.filter(
new Predicate<ClassInfo>() {
@Override
public boolean apply(ClassInfo info) {
return info.isTopLevel();
}
})
.toSet();
} }
/** Returns all top level classes whose package name is {@code packageName}. */ /** Returns all top level classes whose package name is {@code packageName}. */
@ -178,19 +190,21 @@ public final class ClassPath {
*/ */
@Beta @Beta
public static class ResourceInfo { public static class ResourceInfo {
private final File file;
private final String resourceName; private final String resourceName;
final ClassLoader loader; final ClassLoader loader;
static ResourceInfo of(String resourceName, ClassLoader loader) { static ResourceInfo of(File file, String resourceName, ClassLoader loader) {
if (resourceName.endsWith(CLASS_FILE_NAME_EXTENSION)) { if (resourceName.endsWith(CLASS_FILE_NAME_EXTENSION)) {
return new ClassInfo(resourceName, loader); return new ClassInfo(file, resourceName, loader);
} else { } else {
return new ResourceInfo(resourceName, loader); return new ResourceInfo(file, resourceName, loader);
} }
} }
ResourceInfo(String resourceName, ClassLoader loader) { ResourceInfo(File file, String resourceName, ClassLoader loader) {
this.file = checkNotNull(file);
this.resourceName = checkNotNull(resourceName); this.resourceName = checkNotNull(resourceName);
this.loader = checkNotNull(loader); this.loader = checkNotNull(loader);
} }
@ -239,6 +253,11 @@ public final class ClassPath {
return resourceName; return resourceName;
} }
/** Returns the file that includes this resource. */
final File getFile() {
return file;
}
@Override @Override
public int hashCode() { public int hashCode() {
return resourceName.hashCode(); return resourceName.hashCode();
@ -269,8 +288,8 @@ public final class ClassPath {
public static final class ClassInfo extends ResourceInfo { public static final class ClassInfo extends ResourceInfo {
private final String className; private final String className;
ClassInfo(String resourceName, ClassLoader loader) { ClassInfo(File file, String resourceName, ClassLoader loader) {
super(resourceName, loader); super(file, resourceName, loader);
this.className = getClassName(resourceName); this.className = getClassName(resourceName);
} }
@ -317,6 +336,18 @@ public final class ClassPath {
return className; return className;
} }
/**
* Returns true if the class name "looks to be" top level (not nested), that is, it includes no
* '$' in the name. This method may return false for a top-level class that's intentionally
* named with the '$' character. If this is a concern, you could use {@link #load} and then
* check on the loaded {@link Class} object instead.
*
* @since 30.1
*/
public boolean isTopLevel() {
return className.indexOf('$') == -1;
}
/** /**
* Loads (but doesn't link or initialize) the class. * Loads (but doesn't link or initialize) the class.
* *
@ -338,37 +369,66 @@ public final class ClassPath {
} }
} }
/**
* Abstract class that scans through the class path represented by a {@link ClassLoader} and calls /*
* {@link #scanDirectory} and {@link #scanJarFile} for directories and jar files on the class path * Returns all locations that {@code classloader} and parent loaders load classes and resources
* respectively. * from. Callers can {@linkplain LocationInfo#scanResources scan} individual locations selectively
* or even in parallel.
*/ */
abstract static class Scanner { static ImmutableSet<LocationInfo> locationsFrom(ClassLoader classloader) {
ImmutableSet.Builder<LocationInfo> builder = ImmutableSet.builder();
for (Map.Entry<File, ClassLoader> entry : getClassPathEntries(classloader).entrySet()) {
builder.add(new LocationInfo(entry.getKey(), entry.getValue()));
}
return builder.build();
}
// We only scan each file once independent of the classloader that resource might be associated /**
// with. * Represents a single location (a directory or a jar file) in the class path and is responsible
private final Set<File> scannedUris = Sets.newHashSet(); * for scanning resources from this location.
*/
static final class LocationInfo {
final File home;
private final ClassLoader classloader;
public final void scan(ClassLoader classloader) throws IOException { LocationInfo(File home, ClassLoader classloader) {
for (Entry<File, ClassLoader> entry : getClassPathEntries(classloader).entrySet()) { this.home = checkNotNull(home);
scan(entry.getKey(), entry.getValue()); this.classloader = checkNotNull(classloader);
}
} }
@VisibleForTesting /** Returns the file this location is from. */
final void scan(File file, ClassLoader classloader) throws IOException { public final File file() {
if (scannedUris.add(file.getCanonicalFile())) { return home;
scanFrom(file, classloader);
}
} }
/** Called when a directory is scanned for resource files. */ /** Scans this location and returns all scanned resources. */
protected abstract void scanDirectory(ClassLoader loader, File directory) throws IOException; public ImmutableSet<ResourceInfo> scanResources() throws IOException {
return scanResources(new HashSet<File>());
}
/** Called when a jar file is scanned for resource entries. */ /**
protected abstract void scanJarFile(ClassLoader loader, JarFile file) throws IOException; * Scans this location and returns all scanned resources.
*
* <p>This file and jar files from "Class-Path" entry in the scanned manifest files will be
* added to {@code scannedFiles}.
*
* <p>A file will be scanned at most once even if specified multiple times by one or multiple
* jar files' "Class-Path" manifest entries. Particularly, if a jar file from the "Class-Path"
* manifest entry is already in {@code scannedFiles}, either because it was scanned earlier, or
* it was intentionally added to the set by the caller, it will not be scanned again.
*
* <p>Note that when you call {@code location.scanResources(scannedFiles)}, the location will
* always be scanned even if {@code scannedFiles} already contains it.
*/
public ImmutableSet<ResourceInfo> scanResources(Set<File> scannedFiles) throws IOException {
ImmutableSet.Builder<ResourceInfo> builder = ImmutableSet.builder();
scannedFiles.add(home);
scan(home, scannedFiles, builder);
return builder.build();
}
private void scanFrom(File file, ClassLoader classloader) throws IOException { private void scan(File file, Set<File> scannedUris, ImmutableSet.Builder<ResourceInfo> builder)
throws IOException {
try { try {
if (!file.exists()) { if (!file.exists()) {
return; return;
@ -379,13 +439,15 @@ public final class ClassPath {
return; return;
} }
if (file.isDirectory()) { if (file.isDirectory()) {
scanDirectory(classloader, file); scanDirectory(file, builder);
} else { } else {
scanJar(file, classloader); scanJar(file, scannedUris, builder);
} }
} }
private void scanJar(File file, ClassLoader classloader) throws IOException { private void scanJar(
File file, Set<File> scannedUris, ImmutableSet.Builder<ResourceInfo> builder)
throws IOException {
JarFile jarFile; JarFile jarFile;
try { try {
jarFile = new JarFile(file); jarFile = new JarFile(file);
@ -395,142 +457,37 @@ public final class ClassPath {
} }
try { try {
for (File path : getClassPathFromManifest(file, jarFile.getManifest())) { for (File path : getClassPathFromManifest(file, jarFile.getManifest())) {
scan(path, classloader); // We only scan each file once independent of the classloader that file might be
// associated with.
if (scannedUris.add(path.getCanonicalFile())) {
scan(path, scannedUris, builder);
}
} }
scanJarFile(classloader, jarFile); scanJarFile(jarFile, builder);
} finally { } finally {
try { try {
jarFile.close(); jarFile.close();
} catch (IOException ignored) { } catch (IOException ignored) { // similar to try-with-resources, but don't fail scanning
} }
} }
} }
/** private void scanJarFile(JarFile file, ImmutableSet.Builder<ResourceInfo> builder) {
* Returns the class path URIs specified by the {@code Class-Path} manifest attribute, according
* to <a
* href="http://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Main_Attributes">JAR
* File Specification</a>. If {@code manifest} is null, it means the jar file has no manifest,
* and an empty set will be returned.
*/
@VisibleForTesting
static ImmutableSet<File> getClassPathFromManifest(File jarFile, Manifest manifest) {
if (manifest == null) {
return ImmutableSet.of();
}
ImmutableSet.Builder<File> builder = ImmutableSet.builder();
String classpathAttribute =
manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH.toString());
if (classpathAttribute != null) {
for (String path : CLASS_PATH_ATTRIBUTE_SEPARATOR.split(classpathAttribute)) {
URL url;
try {
url = getClassPathEntry(jarFile, path);
} catch (MalformedURLException e) {
// Ignore bad entry
logger.warning("Invalid Class-Path entry: " + path);
continue;
}
if (url.getProtocol().equals("file")) {
builder.add(toFile(url));
}
}
}
return builder.build();
}
@VisibleForTesting
static ImmutableMap<File, ClassLoader> getClassPathEntries(ClassLoader classloader) {
LinkedHashMap<File, ClassLoader> entries = Maps.newLinkedHashMap();
// Search parent first, since it's the order ClassLoader#loadClass() uses.
ClassLoader parent = classloader.getParent();
if (parent != null) {
entries.putAll(getClassPathEntries(parent));
}
for (URL url : getClassLoaderUrls(classloader)) {
if (url.getProtocol().equals("file")) {
File file = toFile(url);
if (!entries.containsKey(file)) {
entries.put(file, classloader);
}
}
}
return ImmutableMap.copyOf(entries);
}
private static ImmutableList<URL> getClassLoaderUrls(ClassLoader classloader) {
if (classloader instanceof URLClassLoader) {
return ImmutableList.copyOf(((URLClassLoader) classloader).getURLs());
}
if (classloader.equals(ClassLoader.getSystemClassLoader())) {
return parseJavaClassPath();
}
return ImmutableList.of();
}
/**
* Returns the URLs in the class path specified by the {@code java.class.path} {@linkplain
* System#getProperty system property}.
*/
@VisibleForTesting // TODO(b/65488446): Make this a public API.
static ImmutableList<URL> parseJavaClassPath() {
ImmutableList.Builder<URL> urls = ImmutableList.builder();
for (String entry : Splitter.on(PATH_SEPARATOR.value()).split(JAVA_CLASS_PATH.value())) {
try {
try {
urls.add(new File(entry).toURI().toURL());
} catch (SecurityException e) { // File.toURI checks to see if the file is a directory
urls.add(new URL("file", null, new File(entry).getAbsolutePath()));
}
} catch (MalformedURLException e) {
logger.log(WARNING, "malformed classpath entry: " + entry, e);
}
}
return urls.build();
}
/**
* Returns the absolute uri of the Class-Path entry value as specified in <a
* href="http://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Main_Attributes">JAR
* File Specification</a>. Even though the specification only talks about relative urls,
* absolute urls are actually supported too (for example, in Maven surefire plugin).
*/
@VisibleForTesting
static URL getClassPathEntry(File jarFile, String path) throws MalformedURLException {
return new URL(jarFile.toURI().toURL(), path);
}
}
@VisibleForTesting
static final class DefaultScanner extends Scanner {
private final SetMultimap<ClassLoader, String> resources =
MultimapBuilder.hashKeys().linkedHashSetValues().build();
ImmutableSet<ResourceInfo> getResources() {
ImmutableSet.Builder<ResourceInfo> builder = ImmutableSet.builder();
for (Entry<ClassLoader, String> entry : resources.entries()) {
builder.add(ResourceInfo.of(entry.getValue(), entry.getKey()));
}
return builder.build();
}
@Override
protected void scanJarFile(ClassLoader classloader, JarFile file) {
Enumeration<JarEntry> entries = file.entries(); Enumeration<JarEntry> entries = file.entries();
while (entries.hasMoreElements()) { while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement(); JarEntry entry = entries.nextElement();
if (entry.isDirectory() || entry.getName().equals(JarFile.MANIFEST_NAME)) { if (entry.isDirectory() || entry.getName().equals(JarFile.MANIFEST_NAME)) {
continue; continue;
} }
resources.get(classloader).add(entry.getName()); builder.add(ResourceInfo.of(new File(file.getName()), entry.getName(), classloader));
} }
} }
@Override private void scanDirectory(File directory, ImmutableSet.Builder<ResourceInfo> builder)
protected void scanDirectory(ClassLoader classloader, File directory) throws IOException { throws IOException {
Set<File> currentPath = new HashSet<>(); Set<File> currentPath = new HashSet<>();
currentPath.add(directory.getCanonicalFile()); currentPath.add(directory.getCanonicalFile());
scanDirectory(directory, classloader, "", currentPath); scanDirectory(directory, "", currentPath, builder);
} }
/** /**
@ -539,15 +496,17 @@ public final class ClassPath {
* cycles; otherwise symlinks are traversed. * cycles; otherwise symlinks are traversed.
* *
* @param directory the root of the directory to scan * @param directory the root of the directory to scan
* @param classloader the classloader that includes resources found in {@code directory}
* @param packagePrefix resource path prefix inside {@code classloader} for any files found * @param packagePrefix resource path prefix inside {@code classloader} for any files found
* under {@code directory} * under {@code directory}
* @param currentPath canonical files already visited in the current directory tree path, for * @param currentPath canonical files already visited in the current directory tree path, for
* cycle elimination * cycle elimination
*/ */
private void scanDirectory( private void scanDirectory(
File directory, ClassLoader classloader, String packagePrefix, Set<File> currentPath) File directory,
throws IOException { String packagePrefix,
Set<File> currentPath,
ImmutableSet.Builder<ResourceInfo> builder)
throws IOException {
File[] files = directory.listFiles(); File[] files = directory.listFiles();
if (files == null) { if (files == null) {
logger.warning("Cannot read directory " + directory); logger.warning("Cannot read directory " + directory);
@ -559,17 +518,132 @@ public final class ClassPath {
if (f.isDirectory()) { if (f.isDirectory()) {
File deref = f.getCanonicalFile(); File deref = f.getCanonicalFile();
if (currentPath.add(deref)) { if (currentPath.add(deref)) {
scanDirectory(deref, classloader, packagePrefix + name + "/", currentPath); scanDirectory(deref, packagePrefix + name + "/", currentPath, builder);
currentPath.remove(deref); currentPath.remove(deref);
} }
} else { } else {
String resourceName = packagePrefix + name; String resourceName = packagePrefix + name;
if (!resourceName.equals(JarFile.MANIFEST_NAME)) { if (!resourceName.equals(JarFile.MANIFEST_NAME)) {
resources.get(classloader).add(resourceName); builder.add(ResourceInfo.of(f, resourceName, classloader));
} }
} }
} }
} }
@Override
public boolean equals(Object obj) {
if (obj instanceof LocationInfo) {
LocationInfo that = (LocationInfo) obj;
return home.equals(that.home) && classloader.equals(that.classloader);
}
return false;
}
@Override
public int hashCode() {
return home.hashCode();
}
@Override
public String toString() {
return home.toString();
}
}
/**
* Returns the class path URIs specified by the {@code Class-Path} manifest attribute, according
* to <a
* href="http://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Main_Attributes">JAR
* File Specification</a>. If {@code manifest} is null, it means the jar file has no manifest, and
* an empty set will be returned.
*/
@VisibleForTesting
static ImmutableSet<File> getClassPathFromManifest(File jarFile, Manifest manifest) {
if (manifest == null) {
return ImmutableSet.of();
}
ImmutableSet.Builder<File> builder = ImmutableSet.builder();
String classpathAttribute =
manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH.toString());
if (classpathAttribute != null) {
for (String path : CLASS_PATH_ATTRIBUTE_SEPARATOR.split(classpathAttribute)) {
URL url;
try {
url = getClassPathEntry(jarFile, path);
} catch (MalformedURLException e) {
// Ignore bad entry
logger.warning("Invalid Class-Path entry: " + path);
continue;
}
if (url.getProtocol().equals("file")) {
builder.add(toFile(url));
}
}
}
return builder.build();
}
@VisibleForTesting
static ImmutableMap<File, ClassLoader> getClassPathEntries(ClassLoader classloader) {
LinkedHashMap<File, ClassLoader> entries = Maps.newLinkedHashMap();
// Search parent first, since it's the order ClassLoader#loadClass() uses.
ClassLoader parent = classloader.getParent();
if (parent != null) {
entries.putAll(getClassPathEntries(parent));
}
for (URL url : getClassLoaderUrls(classloader)) {
if (url.getProtocol().equals("file")) {
File file = toFile(url);
if (!entries.containsKey(file)) {
entries.put(file, classloader);
}
}
}
return ImmutableMap.copyOf(entries);
}
private static ImmutableList<URL> getClassLoaderUrls(ClassLoader classloader) {
if (classloader instanceof URLClassLoader) {
return ImmutableList.copyOf(((URLClassLoader) classloader).getURLs());
}
if (classloader.equals(ClassLoader.getSystemClassLoader())) {
return parseJavaClassPath();
}
return ImmutableList.of();
}
/**
* Returns the URLs in the class path specified by the {@code java.class.path} {@linkplain
* System#getProperty system property}.
*/
@VisibleForTesting // TODO(b/65488446): Make this a public API.
static ImmutableList<URL> parseJavaClassPath() {
ImmutableList.Builder<URL> urls = ImmutableList.builder();
for (String entry : Splitter.on(PATH_SEPARATOR.value()).split(JAVA_CLASS_PATH.value())) {
try {
try {
urls.add(new File(entry).toURI().toURL());
} catch (SecurityException e) { // File.toURI checks to see if the file is a directory
urls.add(new URL("file", null, new File(entry).getAbsolutePath()));
}
} catch (MalformedURLException e) {
logger.log(WARNING, "malformed classpath entry: " + entry, e);
}
}
return urls.build();
}
/**
* Returns the absolute uri of the Class-Path entry value as specified in <a
* href="http://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Main_Attributes">JAR
* File Specification</a>. Even though the specification only talks about relative urls, absolute
* urls are actually supported too (for example, in Maven surefire plugin).
*/
@VisibleForTesting
static URL getClassPathEntry(File jarFile, String path) throws MalformedURLException {
return new URL(jarFile.toURI().toURL(), path);
} }
@VisibleForTesting @VisibleForTesting

View file

@ -33,6 +33,8 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static java.lang.Integer.toHexString;
import static java.lang.System.identityHashCode;
import static java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater; import static java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater;
/** /**
@ -396,7 +398,7 @@ public abstract class AbstractFuture<V> extends InternalFutureFailureAccess
node.setNext(oldHead); node.setNext(oldHead);
if (ATOMIC_HELPER.casWaiters(this, oldHead, node)) { if (ATOMIC_HELPER.casWaiters(this, oldHead, node)) {
while (true) { while (true) {
LockSupport.parkNanos(this, remainingNanos); OverflowAvoidingLockSupport.parkNanos(this, remainingNanos);
// Check interruption first, if we woke up due to interruption we need to honor that. // Check interruption first, if we woke up due to interruption we need to honor that.
if (Thread.interrupted()) { if (Thread.interrupted()) {
removeWaiter(node); removeWaiter(node);
@ -561,6 +563,8 @@ public abstract class AbstractFuture<V> extends InternalFutureFailureAccess
* #wasInterrupted} as necessary. This ensures that the work is done even if the future is * #wasInterrupted} as necessary. This ensures that the work is done even if the future is
* cancelled without a call to {@code cancel}, such as by calling {@code * cancelled without a call to {@code cancel}, such as by calling {@code
* setFuture(cancelledFuture)}. * setFuture(cancelledFuture)}.
* <p>Beware of completing a future while holding a lock. Its listeners may do slow work or
* acquire other locks, risking deadlocks.
*/ */
@Override @Override
public boolean cancel(boolean mayInterruptIfRunning) { public boolean cancel(boolean mayInterruptIfRunning) {
@ -580,7 +584,7 @@ public abstract class AbstractFuture<V> extends InternalFutureFailureAccess
while (true) { while (true) {
if (ATOMIC_HELPER.casValue(abstractFuture, localValue, valueToSet)) { if (ATOMIC_HELPER.casValue(abstractFuture, localValue, valueToSet)) {
rValue = true; rValue = true;
// We call interuptTask before calling complete(), which is consistent with // We call interruptTask before calling complete(), which is consistent with
// FutureTask // FutureTask
if (mayInterruptIfRunning) { if (mayInterruptIfRunning) {
abstractFuture.interruptTask(); abstractFuture.interruptTask();
@ -714,6 +718,9 @@ public abstract class AbstractFuture<V> extends InternalFutureFailureAccess
* known yet. That result, though not yet known, cannot be overridden by a call to a {@code set*} * known yet. That result, though not yet known, cannot be overridden by a call to a {@code set*}
* method, only by a call to {@link #cancel}. * method, only by a call to {@link #cancel}.
* *
* <p>Beware of completing a future while holding a lock. Its listeners may do slow work or
* acquire other locks, risking deadlocks.
*
* @param throwable the exception to be used as the failed result * @param throwable the exception to be used as the failed result
* @return true if the attempt was accepted, completing the {@code Future} * @return true if the attempt was accepted, completing the {@code Future}
*/ */
@ -747,6 +754,9 @@ public abstract class AbstractFuture<V> extends InternalFutureFailureAccess
* invoke the {@link #interruptTask} method, and the {@link #wasInterrupted} method will not * invoke the {@link #interruptTask} method, and the {@link #wasInterrupted} method will not
* return {@code true}. * return {@code true}.
* *
* <p>Beware of completing a future while holding a lock. Its listeners may do slow work or
* acquire other locks, risking deadlocks.
*
* @param future the future to delegate to * @param future the future to delegate to
* @return true if the attempt was accepted, indicating that the {@code Future} was not previously * @return true if the attempt was accepted, indicating that the {@code Future} was not previously
* cancelled or set. * cancelled or set.
@ -1031,7 +1041,14 @@ public abstract class AbstractFuture<V> extends InternalFutureFailureAccess
// TODO(user): move parts into a default method on ListenableFuture? // TODO(user): move parts into a default method on ListenableFuture?
@Override @Override
public String toString() { public String toString() {
StringBuilder builder = new StringBuilder().append(super.toString()).append("[status="); // TODO(cpovirk): Presize to something plausible?
StringBuilder builder = new StringBuilder();
if (getClass().getName().startsWith("com.google.common.util.concurrent.")) {
builder.append(getClass().getSimpleName());
} else {
builder.append(getClass().getName());
}
builder.append('@').append(toHexString(identityHashCode(this))).append("[status=");
if (isCancelled()) { if (isCancelled()) {
builder.append("CANCELLED"); builder.append("CANCELLED");
} else if (isDone()) { } else if (isDone()) {
@ -1079,7 +1096,8 @@ public abstract class AbstractFuture<V> extends InternalFutureFailureAccess
private void addDoneString(StringBuilder builder) { private void addDoneString(StringBuilder builder) {
try { try {
V value = getUninterruptibly(this); V value = getUninterruptibly(this);
builder.append("SUCCESS, result=[").append(userObjectToString(value)).append("]"); builder.append("SUCCESS, result=[");
appendResultObject(builder, value);
} catch (ExecutionException e) { } catch (ExecutionException e) {
builder.append("FAILURE, cause=[").append(e.getCause()).append("]"); builder.append("FAILURE, cause=[").append(e.getCause()).append("]");
} catch (CancellationException e) { } catch (CancellationException e) {
@ -1089,6 +1107,24 @@ public abstract class AbstractFuture<V> extends InternalFutureFailureAccess
} }
} }
/**
* Any object can be the result of a Future, and not every object has a reasonable toString()
* implementation. Using a reconstruction of the default Object.toString() prevents OOMs and stack
* overflows, and helps avoid sensitive data inadvertently ending up in exception messages.
*/
private void appendResultObject(StringBuilder builder, Object o) {
if (o == null) {
builder.append("null");
} else if (o == this) {
builder.append("this future");
} else {
builder
.append(o.getClass().getName())
.append("@")
.append(Integer.toHexString(System.identityHashCode(o)));
}
}
/** Helper for printing user supplied objects into our toString method. */ /** Helper for printing user supplied objects into our toString method. */
private String userObjectToString(Object o) { private String userObjectToString(Object o) {
// This is some basic recursion detection for when people create cycles via set/setFuture // This is some basic recursion detection for when people create cycles via set/setFuture

File diff suppressed because it is too large Load diff

View file

@ -89,7 +89,6 @@ final class CombinedFuture<V> extends AggregateFuture<Object, V> {
private abstract class CombinedFutureInterruptibleTask<T> extends InterruptibleTask<T> { private abstract class CombinedFutureInterruptibleTask<T> extends InterruptibleTask<T> {
private final Executor listenerExecutor; private final Executor listenerExecutor;
boolean thrownByExecute = true;
CombinedFutureInterruptibleTask(Executor listenerExecutor) { CombinedFutureInterruptibleTask(Executor listenerExecutor) {
this.listenerExecutor = checkNotNull(listenerExecutor); this.listenerExecutor = checkNotNull(listenerExecutor);
@ -104,9 +103,7 @@ final class CombinedFuture<V> extends AggregateFuture<Object, V> {
try { try {
listenerExecutor.execute(this); listenerExecutor.execute(this);
} catch (RejectedExecutionException e) { } catch (RejectedExecutionException e) {
if (thrownByExecute) { CombinedFuture.this.setException(e);
CombinedFuture.this.setException(e);
}
} }
} }
@ -153,7 +150,6 @@ final class CombinedFuture<V> extends AggregateFuture<Object, V> {
@Override @Override
ListenableFuture<V> runInterruptibly() throws Exception { ListenableFuture<V> runInterruptibly() throws Exception {
thrownByExecute = false;
ListenableFuture<V> result = callable.call(); ListenableFuture<V> result = callable.call();
return checkNotNull( return checkNotNull(
result, result,
@ -184,7 +180,6 @@ final class CombinedFuture<V> extends AggregateFuture<Object, V> {
@Override @Override
V runInterruptibly() throws Exception { V runInterruptibly() throws Exception {
thrownByExecute = false;
return callable.call(); return callable.call();
} }

View file

@ -15,11 +15,13 @@
package com.google.common.util.concurrent; package com.google.common.util.concurrent;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.util.concurrent.ExecutionSequencer.RunningState.CANCELLED; import static com.google.common.util.concurrent.ExecutionSequencer.RunningState.CANCELLED;
import static com.google.common.util.concurrent.ExecutionSequencer.RunningState.NOT_RUN; import static com.google.common.util.concurrent.ExecutionSequencer.RunningState.NOT_RUN;
import static com.google.common.util.concurrent.ExecutionSequencer.RunningState.STARTED; import static com.google.common.util.concurrent.ExecutionSequencer.RunningState.STARTED;
import static com.google.common.util.concurrent.Futures.immediateCancelledFuture; import static com.google.common.util.concurrent.Futures.immediateCancelledFuture;
import static com.google.common.util.concurrent.Futures.immediateFuture; import static com.google.common.util.concurrent.Futures.immediateFuture;
import static com.google.common.util.concurrent.Futures.immediateVoidFuture;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import com.google.common.annotations.Beta; import com.google.common.annotations.Beta;
@ -28,13 +30,50 @@ import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
/** /**
* Serializes execution of a set of operations. This class guarantees that a submitted callable will * Serializes execution of tasks, somewhat like an "asynchronous {@code synchronized} block." Each
* not be called before previously submitted callables (and any {@code Future}s returned from them) * {@linkplain #submit enqueued} callable will not be submitted to its associated executor until the
* have completed. * previous callable has returned -- and, if the previous callable was an {@link AsyncCallable}, not
* until the {@code Future} it returned is {@linkplain java.util.concurrent.Future#isDone done} (successful, failed, or
* cancelled).
* *
* <p>This class implements a superset of the behavior of {@link * <p>This class has limited support for cancellation and other "early completion":
* MoreExecutors#newSequentialExecutor}. If your tasks all run on the same underlying executor and *
* don't need to wait for {@code Future}s returned from {@code AsyncCallable}s, use it instead. * <ul>
* <li>While calls to {@code submit} and {@code submitAsync} return a {@code Future} that can be
* cancelled, cancellation never propagates to a task that has started to run -- neither to
* the callable itself nor to any {@code Future} returned by an {@code AsyncCallable}.
* (However, cancellation can prevent an <i>unstarted</i> task from running.) Therefore, the
* next task will wait for any running callable (or pending {@code Future} returned by an
* {@code AsyncCallable}) to complete, without interrupting it (and without calling {@code
* cancel} on the {@code Future}). So beware: <i>Even if you cancel every precededing {@code
* Future} returned by this class, the next task may still have to wait.</i>.
* <li>Once an {@code AsyncCallable} returns a {@code Future}, this class considers that task to
* be "done" as soon as <i>that</i> {@code Future} completes in any way. Notably, a {@code
* Future} is "completed" even if it is cancelled while its underlying work continues on a
* thread, an RPC, etc. The {@code Future} is also "completed" if it fails "early" -- for
* example, if the deadline expires on a {@code Future} returned from {@link
* Futures#withTimeout} while the {@code Future} it wraps continues its underlying work. So
* beware: <i>Your {@code AsyncCallable} should not complete its {@code Future} until it is
* safe for the next task to start.</i>
* </ul>
*
* <p>An additional limitation: this class serializes execution of <i>tasks</i> but not any
* <i>listeners</i> of those tasks.
*
* <p>This class is similar to {@link MoreExecutors#newSequentialExecutor}. This class is different
* in a few ways:
*
* <ul>
* <li>Each task may be associated with a different executor.
* <li>Tasks may be of type {@code AsyncCallable}.
* <li>Running tasks <i>cannot</i> be interrupted. (Note that {@code newSequentialExecutor} does
* not return {@code Future} objects, so it doesn't support interruption directly, either.
* However, utilities that <i>use</i> that executor have the ability to interrupt tasks
* running on it. This class, by contrast, does not expose an {@code Executor} API.)
* </ul>
*
* <p>If you don't need the features of this class, you may prefer {@code newSequentialExecutor} for
* its simplicity and ability to accommodate interruption.
* *
* @since 26.0 * @since 26.0
*/ */
@ -48,15 +87,47 @@ public final class ExecutionSequencer {
return new ExecutionSequencer(); return new ExecutionSequencer();
} }
enum RunningState {
NOT_RUN,
CANCELLED,
STARTED,
}
/** This reference acts as a pointer tracking the head of a linked list of ListenableFutures. */ /** This reference acts as a pointer tracking the head of a linked list of ListenableFutures. */
private final AtomicReference<ListenableFuture<Object>> ref = private final AtomicReference<ListenableFuture<Void>> ref =
new AtomicReference<>(immediateFuture(null)); new AtomicReference<>(immediateVoidFuture());
private ThreadConfinedTaskQueue latestTaskQueue = new ThreadConfinedTaskQueue();
/**
* This object is unsafely published, but avoids problematic races by relying exclusively on the
* identity equality of its Thread field so that the task field is only accessed by a single
* thread.
*/
private static final class ThreadConfinedTaskQueue {
/**
* This field is only used for identity comparisons with the current thread. Field assignments
* are atomic, but do not provide happens-before ordering; however:
*
* <ul>
* <li>If this field's value == currentThread, we know that it's up to date, because write
* operations in a thread always happen-before subsequent read operations in the same
* thread
* <li>If this field's value == null because of unsafe publication, we know that it isn't the
* object associated with our thread, because if it was the publication wouldn't have been
* unsafe and we'd have seen our thread as the value. This state is also why a new
* ThreadConfinedTaskQueue object must be created for each inline execution, because
* observing a null thread does not mean the object is safe to reuse.
* <li>If this field's value is some other thread object, we know that it's not our thread.
* <li>If this field's value == null because it originally belonged to another thread and that
* thread cleared it, we still know that it's not associated with our thread
* <li>If this field's value == null because it was associated with our thread and was
* cleared, we know that we're not executing inline any more
* </ul>
*
* All the states where thread != currentThread are identical for our purposes, and so even
* though it's racy, we don't care which of those values we get, so no need to synchronize.
*/
Thread thread;
/** Only used by the thread associated with this object */
Runnable nextTask;
/** Only used by the thread associated with this object */
Executor nextExecutor;
}
/** /**
* Enqueues a task to run when the previous task (if any) completes. * Enqueues a task to run when the previous task (if any) completes.
@ -67,6 +138,7 @@ public final class ExecutionSequencer {
*/ */
public <T> ListenableFuture<T> submit(final Callable<T> callable, Executor executor) { public <T> ListenableFuture<T> submit(final Callable<T> callable, Executor executor) {
checkNotNull(callable); checkNotNull(callable);
checkNotNull(executor);
return submitAsync( return submitAsync(
new AsyncCallable<T>() { new AsyncCallable<T>() {
@Override @Override
@ -92,12 +164,13 @@ public final class ExecutionSequencer {
public <T> ListenableFuture<T> submitAsync( public <T> ListenableFuture<T> submitAsync(
final AsyncCallable<T> callable, final Executor executor) { final AsyncCallable<T> callable, final Executor executor) {
checkNotNull(callable); checkNotNull(callable);
final AtomicReference<RunningState> runningState = new AtomicReference<>(NOT_RUN); checkNotNull(executor);
final TaskNonReentrantExecutor taskExecutor = new TaskNonReentrantExecutor(executor, this);
final AsyncCallable<T> task = final AsyncCallable<T> task =
new AsyncCallable<T>() { new AsyncCallable<T>() {
@Override @Override
public ListenableFuture<T> call() throws Exception { public ListenableFuture<T> call() throws Exception {
if (!runningState.compareAndSet(NOT_RUN, STARTED)) { if (!taskExecutor.trySetStarted()) {
return immediateCancelledFuture(); return immediateCancelledFuture();
} }
return callable.call(); return callable.call();
@ -119,20 +192,13 @@ public final class ExecutionSequencer {
* have completed - namely after oldFuture is done, and taskFuture has either completed or been * have completed - namely after oldFuture is done, and taskFuture has either completed or been
* cancelled before the callable started execution. * cancelled before the callable started execution.
*/ */
final SettableFuture<Object> newFuture = SettableFuture.create(); final SettableFuture<Void> newFuture = SettableFuture.create();
final ListenableFuture<?> oldFuture = ref.getAndSet(newFuture); final ListenableFuture<Void> oldFuture = ref.getAndSet(newFuture);
// Invoke our task once the previous future completes. // Invoke our task once the previous future completes.
final ListenableFuture<T> taskFuture = final TrustedListenableFutureTask<T> taskFuture = TrustedListenableFutureTask.create(task);
Futures.submitAsync( oldFuture.addListener(taskFuture, taskExecutor);
task,
new Executor() {
@Override
public void execute(Runnable runnable) {
oldFuture.addListener(runnable, executor);
}
});
final ListenableFuture<T> outputFuture = Futures.nonCancellationPropagating(taskFuture); final ListenableFuture<T> outputFuture = Futures.nonCancellationPropagating(taskFuture);
@ -144,15 +210,39 @@ public final class ExecutionSequencer {
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {
if (taskFuture.isDone() if (taskFuture.isDone()) {
// If this CAS succeeds, we know that the provided callable will never be invoked,
// so when oldFuture completes it is safe to allow the next submitted task to
// proceed.
|| (outputFuture.isCancelled() && runningState.compareAndSet(NOT_RUN, CANCELLED))) {
// Since the value of oldFuture can only ever be immediateFuture(null) or setFuture of // Since the value of oldFuture can only ever be immediateFuture(null) or setFuture of
// a future that eventually came from immediateFuture(null), this doesn't leak // a future that eventually came from immediateFuture(null), this doesn't leak
// throwables or completion values. // throwables or completion values.
newFuture.setFuture(oldFuture); newFuture.setFuture(oldFuture);
} else if (outputFuture.isCancelled() && taskExecutor.trySetCancelled()) {
// If this CAS succeeds, we know that the provided callable will never be invoked,
// so when oldFuture completes it is safe to allow the next submitted task to
// proceed. Doing this immediately here lets the next task run without waiting for
// the cancelled task's executor to run the noop AsyncCallable.
//
// ---
//
// If the CAS fails, the provided callable already started running (or it is about
// to). Our contract promises:
//
// 1. not to execute a new callable until the old one has returned
//
// If we were to cancel taskFuture, that would let the next task start while the old
// one is still running.
//
// Now, maybe we could tweak our implementation to not start the next task until the
// callable actually completes. (We could detect completion in our wrapper
// `AsyncCallable task`.) However, our contract also promises:
//
// 2. not to cancel any Future the user returned from an AsyncCallable
//
// We promise this because, once we cancel that Future, we would no longer be able to
// tell when any underlying work it is doing is done. Thus, we might start a new task
// while that underlying work is still running.
//
// So that is why we cancel only in the case of CAS success.
taskFuture.cancel(false);
} }
} }
}; };
@ -164,4 +254,163 @@ public final class ExecutionSequencer {
return outputFuture; return outputFuture;
} }
enum RunningState {
NOT_RUN,
CANCELLED,
STARTED,
}
/**
* This class helps avoid a StackOverflowError when large numbers of tasks are submitted with
* {@link MoreExecutors#directExecutor}. Normally, when the first future completes, all the other
* tasks would be called recursively. Here, we detect that the delegate executor is executing
* inline, and maintain a queue to dispatch tasks iteratively. There is one instance of this class
* per call to submit() or submitAsync(), and each instance supports only one call to execute().
*
* <p>This class would certainly be simpler and easier to reason about if it were built with
* ThreadLocal; however, ThreadLocal is not well optimized for the case where the ThreadLocal is
* non-static, and is initialized/removed frequently - this causes churn in the Thread specific
* hashmaps. Using a static ThreadLocal to avoid that overhead would mean that different
* ExecutionSequencer objects interfere with each other, which would be undesirable, in addition
* to increasing the memory footprint of every thread that interacted with it. In order to release
* entries in thread-specific maps when the ThreadLocal object itself is no longer referenced,
* ThreadLocal is usually implemented with a WeakReference, which can have negative performance
* properties; for example, calling WeakReference.get() on Android will block during an
* otherwise-concurrent GC cycle.
*/
@SuppressWarnings("ShouldNotSubclass") // Saving an allocation here is worth it
private static final class TaskNonReentrantExecutor extends AtomicReference<RunningState>
implements Executor, Runnable {
/**
* Used to update and read the latestTaskQueue field. Set to null once the runnable has been run
* or queued.
*/
ExecutionSequencer sequencer;
/**
* Executor the task was set to run on. Set to null when the task has been queued, run, or
* cancelled.
*/
Executor delegate;
/**
* Set before calling delegate.execute(); set to null once run, so that it can be GCed; this
* object may live on after, if submitAsync returns an incomplete future.
*/
Runnable task;
/** Thread that called execute(). Set in execute, cleared when delegate.execute() returns. */
Thread submitting;
private TaskNonReentrantExecutor(Executor delegate, ExecutionSequencer sequencer) {
super(NOT_RUN);
this.delegate = delegate;
this.sequencer = sequencer;
}
@Override
public void execute(Runnable task) {
// If this operation was successfully cancelled already, calling the runnable will be a noop.
// This also avoids a race where if outputFuture is cancelled, it will call taskFuture.cancel,
// which will call newFuture.setFuture(oldFuture), to allow the next task in the queue to run
// without waiting for the user's executor to run our submitted Runnable. However, this can
// interact poorly with the reentrancy-avoiding behavior of this executor - when the operation
// before the cancelled future completes, it will synchronously complete both the newFuture
// from the cancelled operation and its own. This can cause one runnable to queue two tasks,
// breaking the invariant this method relies on to iteratively run the next task after the
// previous one completes.
if (get() == RunningState.CANCELLED) {
delegate = null;
sequencer = null;
return;
}
submitting = Thread.currentThread();
try {
ThreadConfinedTaskQueue submittingTaskQueue = sequencer.latestTaskQueue;
if (submittingTaskQueue.thread == submitting) {
sequencer = null;
// Submit from inside a reentrant submit. We don't know if this one will be reentrant (and
// can't know without submitting something to the executor) so queue to run iteratively.
// Task must be null, since each execution on this executor can only produce one more
// execution.
checkState(submittingTaskQueue.nextTask == null);
submittingTaskQueue.nextTask = task;
submittingTaskQueue.nextExecutor = delegate;
delegate = null;
} else {
Executor localDelegate = delegate;
delegate = null;
this.task = task;
localDelegate.execute(this);
}
} finally {
// Important to null this out here - if we did *not* execute inline, we might still
// run() on the same thread that called execute() - such as in a thread pool, and think
// that it was happening inline. As a side benefit, avoids holding on to the Thread object
// longer than necessary.
submitting = null;
}
}
@SuppressWarnings("ShortCircuitBoolean")
@Override
public void run() {
Thread currentThread = Thread.currentThread();
if (currentThread != submitting) {
Runnable localTask = task;
task = null;
localTask.run();
return;
}
// Executor called reentrantly! Make sure that further calls don't overflow stack. Further
// reentrant calls will see that their current thread is the same as the one set in
// latestTaskQueue, and queue rather than calling execute() directly.
ThreadConfinedTaskQueue executingTaskQueue = new ThreadConfinedTaskQueue();
executingTaskQueue.thread = currentThread;
// Unconditionally set; there is no risk of throwing away a queued task from another thread,
// because in order for the current task to run on this executor the previous task must have
// already started execution. Because each task on a TaskNonReentrantExecutor can only produce
// one execute() call to another instance from the same ExecutionSequencer, we know by
// induction that the task that launched this one must not have added any other runnables to
// that thread's queue, and thus we cannot be replacing a TaskAndThread object that would
// otherwise have another task queued on to it. Note the exception to this, cancellation, is
// specially handled in execute() - execute() calls triggered by cancellation are no-ops, and
// thus don't count.
sequencer.latestTaskQueue = executingTaskQueue;
sequencer = null;
try {
Runnable localTask = task;
task = null;
localTask.run();
// Now check if our task attempted to reentrantly execute the next task.
Runnable queuedTask;
Executor queuedExecutor;
// Intentionally using non-short-circuit operator
while ((queuedTask = executingTaskQueue.nextTask) != null
& (queuedExecutor = executingTaskQueue.nextExecutor) != null) {
executingTaskQueue.nextTask = null;
executingTaskQueue.nextExecutor = null;
queuedExecutor.execute(queuedTask);
}
} finally {
// Null out the thread field, so that we don't leak a reference to Thread, and so that
// future `thread == currentThread()` calls from this thread don't incorrectly queue instead
// of executing. Don't null out the latestTaskQueue field, because the work done here
// may have scheduled more operations on another thread, and if those operations then
// trigger reentrant calls that thread will have updated the latestTaskQueue field, and
// we'd be interfering with their operation.
executingTaskQueue.thread = null;
}
}
private boolean trySetStarted() {
return compareAndSet(NOT_RUN, STARTED);
}
private boolean trySetCancelled() {
return compareAndSet(NOT_RUN, CANCELLED);
}
}
} }

View file

@ -49,7 +49,7 @@ import java.util.concurrent.TimeoutException;
* debugging, and cancellation. Examples of frameworks include: * debugging, and cancellation. Examples of frameworks include:
* *
* <ul> * <ul>
* <li><a href="http://dagger.dev/producers.html">Dagger Producers</a> * <li><a href="https://dagger.dev/producers.html">Dagger Producers</a>
* </ul> * </ul>
* *
* <h4>{@link java.util.concurrent.CompletableFuture} / {@link java.util.concurrent.CompletionStage} * <h4>{@link java.util.concurrent.CompletableFuture} / {@link java.util.concurrent.CompletionStage}

View file

@ -30,7 +30,8 @@ import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.CollectionFuture.ListFuture; import com.google.common.util.concurrent.CollectionFuture.ListFuture;
import com.google.common.util.concurrent.ImmediateFuture.ImmediateCancelledFuture; import com.google.common.util.concurrent.ImmediateFuture.ImmediateCancelledFuture;
import com.google.common.util.concurrent.ImmediateFuture.ImmediateFailedFuture; import com.google.common.util.concurrent.ImmediateFuture.ImmediateFailedFuture;
import com.google.common.util.concurrent.internal.InternalFutureFailureAccess;
import com.google.common.util.concurrent.internal.InternalFutures;
import java.time.Duration; import java.time.Duration;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -45,7 +46,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
/** /**
* Static utility methods pertaining to the {@link Future} interface. * Static utility methods pertaining to the {@link Future} interface.
* *
@ -60,7 +60,7 @@ import java.util.concurrent.atomic.AtomicInteger;
* monitoring, debugging, and cancellation. Examples of frameworks include: * monitoring, debugging, and cancellation. Examples of frameworks include:
* *
* <ul> * <ul>
* <li><a href="http://dagger.dev/producers.html">Dagger Producers</a> * <li><a href="https://dagger.dev/producers.html">Dagger Producers</a>
* </ul> * </ul>
* *
* <p>If you do chain your operations manually, you may want to use {@link FluentFuture}. * <p>If you do chain your operations manually, you may want to use {@link FluentFuture}.
@ -135,6 +135,17 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
return new ImmediateFuture<>(value); return new ImmediateFuture<>(value);
} }
/**
* Returns a successful {@code ListenableFuture<Void>}. This method is equivalent to {@code
* immediateFuture(null)} except that it is restricted to produce futures of type {@code Void}.
*
* @since 29.0
*/
@SuppressWarnings("unchecked")
public static ListenableFuture<Void> immediateVoidFuture() {
return (ListenableFuture<Void>) ImmediateFuture.NULL;
}
/** /**
* Returns a {@code ListenableFuture} which has an exception set immediately upon construction. * Returns a {@code ListenableFuture} which has an exception set immediately upon construction.
* *
@ -161,7 +172,7 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* Executes {@code callable} on the specified {@code executor}, returning a {@code Future}. * Executes {@code callable} on the specified {@code executor}, returning a {@code Future}.
* *
* @throws RejectedExecutionException if the task cannot be scheduled for execution * @throws RejectedExecutionException if the task cannot be scheduled for execution
* @since NEXT * @since 28.2
*/ */
@Beta @Beta
public static <O> ListenableFuture<O> submit(Callable<O> callable, Executor executor) { public static <O> ListenableFuture<O> submit(Callable<O> callable, Executor executor) {
@ -175,7 +186,7 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* will complete after execution. * will complete after execution.
* *
* @throws RejectedExecutionException if the task cannot be scheduled for execution * @throws RejectedExecutionException if the task cannot be scheduled for execution
* @since NEXT * @since 28.2
*/ */
@Beta @Beta
public static ListenableFuture<Void> submit(Runnable runnable, Executor executor) { public static ListenableFuture<Void> submit(Runnable runnable, Executor executor) {
@ -258,9 +269,7 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* }</pre> * }</pre>
* *
* <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See * <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See
* the discussion in the {@link ListenableFuture#addListener ListenableFuture.addListener} * the warnings the {@link MoreExecutors#directExecutor} documentation.
* documentation. All its warnings about heavyweight listeners are also applicable to heavyweight
* functions passed to this method.
* *
* @param input the primary input {@code Future} * @param input the primary input {@code Future}
* @param exceptionType the exception type that triggers use of {@code fallback}. The exception * @param exceptionType the exception type that triggers use of {@code fallback}. The exception
@ -325,11 +334,7 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* }</pre> * }</pre>
* *
* <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See * <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See
* the discussion in the {@link ListenableFuture#addListener ListenableFuture.addListener} * the warnings the {@link MoreExecutors#directExecutor} documentation.
* documentation. All its warnings about heavyweight listeners are also applicable to heavyweight
* functions passed to this method. (Specifically, {@code directExecutor} functions should avoid
* heavyweight operations inside {@code AsyncFunction.apply}. Any heavyweight operations should
* occur in other threads responsible for completing the returned {@code Future}.)
* *
* @param input the primary input {@code Future} * @param input the primary input {@code Future}
* @param exceptionType the exception type that triggers use of {@code fallback}. The exception * @param exceptionType the exception type that triggers use of {@code fallback}. The exception
@ -415,11 +420,7 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* }</pre> * }</pre>
* *
* <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See * <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See
* the discussion in the {@link ListenableFuture#addListener ListenableFuture.addListener} * the warnings the {@link MoreExecutors#directExecutor} documentation.
* documentation. All its warnings about heavyweight listeners are also applicable to heavyweight
* functions passed to this method. (Specifically, {@code directExecutor} functions should avoid
* heavyweight operations inside {@code AsyncFunction.apply}. Any heavyweight operations should
* occur in other threads responsible for completing the returned {@code Future}.)
* *
* <p>The returned {@code Future} attempts to keep its cancellation state in sync with that of the * <p>The returned {@code Future} attempts to keep its cancellation state in sync with that of the
* input future and that of the future returned by the chain function. That is, if the returned * input future and that of the future returned by the chain function. That is, if the returned
@ -455,9 +456,7 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* }</pre> * }</pre>
* *
* <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See * <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See
* the discussion in the {@link ListenableFuture#addListener ListenableFuture.addListener} * the warnings the {@link MoreExecutors#directExecutor} documentation.
* documentation. All its warnings about heavyweight listeners are also applicable to heavyweight
* functions passed to this method.
* *
* <p>The returned {@code Future} attempts to keep its cancellation state in sync with that of the * <p>The returned {@code Future} attempts to keep its cancellation state in sync with that of the
* input future. That is, if the returned {@code Future} is cancelled, it will attempt to cancel * input future. That is, if the returned {@code Future} is cancelled, it will attempt to cancel
@ -669,7 +668,6 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* @since 20.0 * @since 20.0
*/ */
@Beta @Beta
// TODO(cpovirk): Consider removing, especially if we provide run(Runnable)
@GwtCompatible @GwtCompatible
public static final class FutureCombiner<V> { public static final class FutureCombiner<V> {
private final boolean allMustSucceed; private final boolean allMustSucceed;
@ -713,7 +711,6 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* *
* <p>Canceling this future will attempt to cancel all the component futures. * <p>Canceling this future will attempt to cancel all the component futures.
*/ */
// TODO(cpovirk): Remove this
public <C> ListenableFuture<C> call(Callable<C> combiner, Executor executor) { public <C> ListenableFuture<C> call(Callable<C> combiner, Executor executor) {
return new CombinedFuture<C>(futures, allMustSucceed, executor, combiner); return new CombinedFuture<C>(futures, allMustSucceed, executor, combiner);
} }
@ -999,13 +996,18 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
/** /**
* Registers separate success and failure callbacks to be run when the {@code Future}'s * Registers separate success and failure callbacks to be run when the {@code Future}'s
* computation is {@linkplain java.util.concurrent.Future#isDone() complete} or, if the * computation is {@linkplain Future#isDone() complete} or, if the
* computation is already complete, immediately. * computation is already complete, immediately.
* *
* <p>The callback is run on {@code executor}. There is no guaranteed ordering of execution of * <p>The callback is run on {@code executor}. There is no guaranteed ordering of execution of
* callbacks, but any callback added through this method is guaranteed to be called once the * callbacks, but any callback added through this method is guaranteed to be called once the
* computation is complete. * computation is complete.
* *
* <p>Exceptions thrown by a {@code callback} will be propagated up to the executor. Any exception
* thrown during {@code Executor.execute} (e.g., a {@code RejectedExecutionException} or an
* exception thrown by {@linkplain MoreExecutors#directExecutor direct execution}) will be caught
* and logged.
*
* <p>Example: * <p>Example:
* *
* <pre>{@code * <pre>{@code
@ -1023,9 +1025,7 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* }</pre> * }</pre>
* *
* <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See * <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See
* the discussion in the {@link ListenableFuture#addListener ListenableFuture.addListener} * the warnings the {@link MoreExecutors#directExecutor} documentation.
* documentation. All its warnings about heavyweight listeners are also applicable to heavyweight
* callbacks passed to this method.
* *
* <p>For a more general interface to attach a completion listener to a {@code Future}, see {@link * <p>For a more general interface to attach a completion listener to a {@code Future}, see {@link
* ListenableFuture#addListener addListener}. * ListenableFuture#addListener addListener}.
@ -1055,6 +1055,14 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
@Override @Override
public void run() { public void run() {
if (future instanceof InternalFutureFailureAccess) {
Throwable failure =
InternalFutures.tryInternalFastPathGetFailure((InternalFutureFailureAccess) future);
if (failure != null) {
callback.onFailure(failure);
return;
}
}
final V value; final V value;
try { try {
value = getDone(future); value = getDone(future);
@ -1093,7 +1101,6 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* @throws IllegalStateException if the {@code Future} is not done * @throws IllegalStateException if the {@code Future} is not done
* @since 20.0 * @since 20.0
*/ */
// TODO(cpovirk): Consider calling getDone() in our own code. // TODO(cpovirk): Consider calling getDone() in our own code.
public static <V> V getDone(Future<V> future) throws ExecutionException { public static <V> V getDone(Future<V> future) throws ExecutionException {
/* /*
@ -1103,7 +1110,6 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* IllegalArgumentException here, in part to keep its recommendation simple: Static methods * IllegalArgumentException here, in part to keep its recommendation simple: Static methods
* should throw IllegalStateException only when they use static state. * should throw IllegalStateException only when they use static state.
* *
*
* Why do we deviate here? The answer: We want for fluentFuture.getDone() to throw the same * Why do we deviate here? The answer: We want for fluentFuture.getDone() to throw the same
* exception as Futures.getDone(fluentFuture). * exception as Futures.getDone(fluentFuture).
*/ */
@ -1154,7 +1160,6 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* @since 19.0 (in 10.0 as {@code get}) * @since 19.0 (in 10.0 as {@code get})
*/ */
@Beta @Beta
@GwtIncompatible // reflection @GwtIncompatible // reflection
public static <V, X extends Exception> V getChecked(Future<V> future, Class<X> exceptionClass) public static <V, X extends Exception> V getChecked(Future<V> future, Class<X> exceptionClass)
throws X { throws X {
@ -1205,7 +1210,6 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* @since 28.0 * @since 28.0
*/ */
@Beta @Beta
@GwtIncompatible // reflection @GwtIncompatible // reflection
public static <V, X extends Exception> V getChecked( public static <V, X extends Exception> V getChecked(
Future<V> future, Class<X> exceptionClass, Duration timeout) throws X { Future<V> future, Class<X> exceptionClass, Duration timeout) throws X {
@ -1256,7 +1260,6 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* @since 19.0 (in 10.0 as {@code get} and with different parameter order) * @since 19.0 (in 10.0 as {@code get} and with different parameter order)
*/ */
@Beta @Beta
@GwtIncompatible // reflection @GwtIncompatible // reflection
@SuppressWarnings("GoodTime") // should accept a java.time.Duration @SuppressWarnings("GoodTime") // should accept a java.time.Duration
public static <V, X extends Exception> V getChecked( public static <V, X extends Exception> V getChecked(
@ -1298,7 +1301,6 @@ public final class Futures extends GwtFuturesCatchingSpecialization {
* @throws CancellationException if {@code get} throws a {@code CancellationException} * @throws CancellationException if {@code get} throws a {@code CancellationException}
* @since 10.0 * @since 10.0
*/ */
public static <V> V getUnchecked(Future<V> future) { public static <V> V getUnchecked(Future<V> future) {
checkNotNull(future); checkNotNull(future);
try { try {

View file

@ -40,7 +40,7 @@ import java.util.concurrent.RejectedExecutionException;
* frameworks include: * frameworks include:
* *
* <ul> * <ul>
* <li><a href="http://dagger.dev/producers.html">Dagger Producers</a> * <li><a href="https://dagger.dev/producers.html">Dagger Producers</a>
* </ul> * </ul>
* *
* <p>The main purpose of {@link #addListener addListener} is to support this chaining. You will * <p>The main purpose of {@link #addListener addListener} is to support this chaining. You will
@ -112,20 +112,10 @@ public interface ListenableFuture<V> extends Future<V> {
* thrown by {@linkplain MoreExecutors#directExecutor direct execution}) will be caught and * thrown by {@linkplain MoreExecutors#directExecutor direct execution}) will be caught and
* logged. * logged.
* *
* <p>Note: For fast, lightweight listeners that would be safe to execute in any thread, consider * <p>Note: If your listener is lightweight -- and will not cause stack overflow by completing
* {@link MoreExecutors#directExecutor}. Otherwise, avoid it. Heavyweight {@code directExecutor} * more futures or adding more {@code directExecutor()} listeners inline -- consider {@link
* listeners can cause problems, and these problems can be difficult to reproduce because they * MoreExecutors#directExecutor}. Otherwise, avoid it: See the warnings on the docs for {@code
* depend on timing. For example: * directExecutor}.
*
* <ul>
* <li>The listener may be executed by the caller of {@code addListener}. That caller may be a
* UI thread or other latency-sensitive thread. This can harm UI responsiveness.
* <li>The listener may be executed by the thread that completes this {@code Future}. That
* thread may be an internal system thread such as an RPC network thread. Blocking that
* thread may stall progress of the whole system. It may even cause a deadlock.
* <li>The listener may delay other listeners, even listeners that are not themselves {@code
* directExecutor} listeners.
* </ul>
* *
* <p>This is the most general listener interface. For common operations performed using * <p>This is the most general listener interface. For common operations performed using
* listeners, see {@link Futures}. For a simplified but general listener interface, see {@link * listeners, see {@link Futures}. For a simplified but general listener interface, see {@link

View file

@ -14,11 +14,16 @@
package com.google.common.util.concurrent; package com.google.common.util.concurrent;
import static java.lang.Math.min;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.GwtIncompatible;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.FutureTask; import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/** /**
* A {@link FutureTask} that also implements the {@link ListenableFuture} interface. Unlike {@code * A {@link FutureTask} that also implements the {@link ListenableFuture} interface. Unlike {@code
@ -80,6 +85,19 @@ public class ListenableFutureTask<V> extends FutureTask<V> implements Listenable
executionList.add(listener, exec); executionList.add(listener, exec);
} }
@Override
public V get(long timeout, TimeUnit unit)
throws TimeoutException, InterruptedException, ExecutionException {
long timeoutNanos = unit.toNanos(timeout);
if (timeoutNanos <= OverflowAvoidingLockSupport.MAX_NANOSECONDS_THRESHOLD) {
return super.get(timeout, unit);
}
// Waiting 68 years should be enough for any program.
return super.get(
min(timeoutNanos, OverflowAvoidingLockSupport.MAX_NANOSECONDS_THRESHOLD), NANOSECONDS);
}
/** Internal implementation detail used to invoke the listeners. */ /** Internal implementation detail used to invoke the listeners. */
@Override @Override
protected void done() { protected void done() {

View file

@ -27,8 +27,6 @@ import com.google.common.base.Throwables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Queues; import com.google.common.collect.Queues;
import com.google.common.util.concurrent.ForwardingListenableFuture.SimpleForwardingListenableFuture; import com.google.common.util.concurrent.ForwardingListenableFuture.SimpleForwardingListenableFuture;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.time.Duration; import java.time.Duration;
import java.util.Collection; import java.util.Collection;
@ -49,12 +47,11 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
/** /**
* Factory and utility methods for {@link java.util.concurrent.Executor}, {@link ExecutorService}, * Factory and utility methods for {@link Executor}, {@link ExecutorService},
* and {@link ThreadFactory}. * and {@link ThreadFactory}.
* *
* @author Eric Fellheimer * @author Eric Fellheimer
@ -309,10 +306,8 @@ public final class MoreExecutors {
* - Shutdown: runningTasks > 0 and shutdown == true * - Shutdown: runningTasks > 0 and shutdown == true
* - Terminated: runningTasks == 0 and shutdown == true * - Terminated: runningTasks == 0 and shutdown == true
*/ */
private int runningTasks = 0; private int runningTasks = 0;
private boolean shutdown = false; private boolean shutdown = false;
@Override @Override
@ -401,10 +396,11 @@ public final class MoreExecutors {
/** /**
* Creates an executor service that runs each task in the thread that invokes {@code * Creates an executor service that runs each task in the thread that invokes {@code
* execute/submit}, as in {@link CallerRunsPolicy} This applies both to individually submitted * execute/submit}, as in {@code ThreadPoolExecutor.CallerRunsPolicy}. This applies both to
* tasks and to collections of tasks submitted via {@code invokeAll} or {@code invokeAny}. In the * individually submitted tasks and to collections of tasks submitted via {@code invokeAll} or
* latter case, tasks will run serially on the calling thread. Tasks are run to completion before * {@code invokeAny}. In the latter case, tasks will run serially on the calling thread. Tasks are
* a {@code Future} is returned to the caller (unless the executor has been shutdown). * run to completion before a {@code Future} is returned to the caller (unless the executor has
* been shutdown).
* *
* <p>Although all tasks are immediately executed in the thread that submitted the task, this * <p>Although all tasks are immediately executed in the thread that submitted the task, this
* {@code ExecutorService} imposes a small locking overhead on each task submission in order to * {@code ExecutorService} imposes a small locking overhead on each task submission in order to
@ -431,7 +427,33 @@ public final class MoreExecutors {
/** /**
* Returns an {@link Executor} that runs each task in the thread that invokes {@link * Returns an {@link Executor} that runs each task in the thread that invokes {@link
* Executor#execute execute}, as in {@link CallerRunsPolicy}. * Executor#execute execute}, as in {@code ThreadPoolExecutor.CallerRunsPolicy}.
*
* <p>This executor is appropriate for tasks that are lightweight and not deeply chained.
* Inappropriate {@code directExecutor} usage can cause problems, and these problems can be
* difficult to reproduce because they depend on timing. For example:
*
* <ul>
* <li>A call like {@code future.transform(function, directExecutor())} may execute the function
* immediately in the thread that is calling {@code transform}. (This specific case happens
* if the future is already completed.) If {@code transform} call was made from a UI thread
* or other latency-sensitive thread, a heavyweight function can harm responsiveness.
* <li>If the task will be executed later, consider which thread will trigger the execution --
* since that thread will execute the task inline. If the thread is a shared system thread
* like an RPC network thread, a heavyweight task can stall progress of the whole system or
* even deadlock it.
* <li>If many tasks will be triggered by the same event, one heavyweight task may delay other
* tasks -- even tasks that are not themselves {@code directExecutor} tasks.
* <li>If many such tasks are chained together (such as with {@code
* future.transform(...).transform(...).transform(...)....}), they may overflow the stack.
* (In simple cases, callers can avoid this by registering all tasks with the same {@link
* MoreExecutors#newSequentialExecutor} wrapper around {@code directExecutor()}. More
* complex cases may require using thread pools or making deeper changes.)
* </ul>
*
* Additionally, beware of executing tasks with {@code directExecutor} while holding a lock. Since
* the task you submit to the executor (or any other arbitrary work the executor does) may do slow
* work or acquire other locks, you risk deadlocks.
* *
* <p>This instance is equivalent to: * <p>This instance is equivalent to:
* *
@ -446,7 +468,6 @@ public final class MoreExecutors {
* <p>This should be preferred to {@link #newDirectExecutorService()} because implementing the * <p>This should be preferred to {@link #newDirectExecutorService()} because implementing the
* {@link ExecutorService} subinterface necessitates significant performance overhead. * {@link ExecutorService} subinterface necessitates significant performance overhead.
* *
*
* @since 18.0 * @since 18.0
*/ */
public static Executor directExecutor() { public static Executor directExecutor() {
@ -700,7 +721,8 @@ public final class MoreExecutors {
* An implementation of {@link ExecutorService#invokeAny} for {@link ListeningExecutorService} * An implementation of {@link ExecutorService#invokeAny} for {@link ListeningExecutorService}
* implementations. * implementations.
*/ */
@GwtIncompatible static <T> T invokeAnyImpl( @GwtIncompatible
static <T> T invokeAnyImpl(
ListeningExecutorService executorService, ListeningExecutorService executorService,
Collection<? extends Callable<T>> tasks, Collection<? extends Callable<T>> tasks,
boolean timed, boolean timed,
@ -715,7 +737,8 @@ public final class MoreExecutors {
* implementations. * implementations.
*/ */
@SuppressWarnings("GoodTime") // should accept a java.time.Duration @SuppressWarnings("GoodTime") // should accept a java.time.Duration
@GwtIncompatible static <T> T invokeAnyImpl( @GwtIncompatible
static <T> T invokeAnyImpl(
ListeningExecutorService executorService, ListeningExecutorService executorService,
Collection<? extends Callable<T>> tasks, Collection<? extends Callable<T>> tasks,
boolean timed, boolean timed,
@ -907,7 +930,6 @@ public final class MoreExecutors {
* right before each task is run. The renaming is best effort, if a {@link SecurityManager} * right before each task is run. The renaming is best effort, if a {@link SecurityManager}
* prevents the renaming then it will be skipped but the tasks will still execute. * prevents the renaming then it will be skipped but the tasks will still execute.
* *
*
* @param executor The executor to decorate * @param executor The executor to decorate
* @param nameSupplier The source of names for each task * @param nameSupplier The source of names for each task
*/ */
@ -931,7 +953,6 @@ public final class MoreExecutors {
* right before each task is run. The renaming is best effort, if a {@link SecurityManager} * right before each task is run. The renaming is best effort, if a {@link SecurityManager}
* prevents the renaming then it will be skipped but the tasks will still execute. * prevents the renaming then it will be skipped but the tasks will still execute.
* *
*
* @param service The executor to decorate * @param service The executor to decorate
* @param nameSupplier The source of names for each task * @param nameSupplier The source of names for each task
*/ */
@ -961,7 +982,6 @@ public final class MoreExecutors {
* right before each task is run. The renaming is best effort, if a {@link SecurityManager} * right before each task is run. The renaming is best effort, if a {@link SecurityManager}
* prevents the renaming then it will be skipped but the tasks will still execute. * prevents the renaming then it will be skipped but the tasks will still execute.
* *
*
* @param service The executor to decorate * @param service The executor to decorate
* @param nameSupplier The source of names for each task * @param nameSupplier The source of names for each task
*/ */
@ -1007,7 +1027,6 @@ public final class MoreExecutors {
* @since 28.0 * @since 28.0
*/ */
@Beta @Beta
@GwtIncompatible // java.time.Duration @GwtIncompatible // java.time.Duration
public static boolean shutdownAndAwaitTermination(ExecutorService service, Duration timeout) { public static boolean shutdownAndAwaitTermination(ExecutorService service, Duration timeout) {
return shutdownAndAwaitTermination(service, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); return shutdownAndAwaitTermination(service, toNanosSaturated(timeout), TimeUnit.NANOSECONDS);
@ -1038,7 +1057,6 @@ public final class MoreExecutors {
* @since 17.0 * @since 17.0
*/ */
@Beta @Beta
@GwtIncompatible // concurrency @GwtIncompatible // concurrency
@SuppressWarnings("GoodTime") // should accept a java.time.Duration @SuppressWarnings("GoodTime") // should accept a java.time.Duration
public static boolean shutdownAndAwaitTermination( public static boolean shutdownAndAwaitTermination(
@ -1078,26 +1096,12 @@ public final class MoreExecutors {
return delegate; return delegate;
} }
return new Executor() { return new Executor() {
boolean thrownFromDelegate = true;
@Override @Override
public void execute(final Runnable command) { public void execute(Runnable command) {
try { try {
delegate.execute( delegate.execute(command);
new Runnable() {
@Override
public void run() {
thrownFromDelegate = false;
command.run();
}
});
} catch (RejectedExecutionException e) { } catch (RejectedExecutionException e) {
if (thrownFromDelegate) { future.setException(e);
// wrap exception?
future.setException(e);
}
// otherwise it must have been thrown from a transitive call and the delegate runnable
// should have handled it.
} }
} }
}; };

View file

@ -0,0 +1,36 @@
/*
* Copyright (C) 2020 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.util.concurrent;
import static java.lang.Math.min;
import java.util.concurrent.locks.LockSupport;
/**
* Works around an android bug, where parking for more than INT_MAX seconds can produce an abort
* signal on 32 bit devices running Android Q.
*/
final class OverflowAvoidingLockSupport {
// Represents the max nanoseconds representable on a linux timespec with a 32 bit tv_sec
static final long MAX_NANOSECONDS_THRESHOLD = (1L + Integer.MAX_VALUE) * 1_000_000_000L - 1L;
private OverflowAvoidingLockSupport() {}
static void parkNanos(Object blocker, long nanos) {
// Even in the extremely unlikely event that a thread unblocks itself early after only 68 years,
// this is indistinguishable from a spurious wakeup, which LockSupport allows.
LockSupport.parkNanos(blocker, min(nanos, MAX_NANOSECONDS_THRESHOLD));
}
}

View file

@ -19,11 +19,10 @@ import static com.google.common.util.concurrent.SequentialExecutor.WorkerRunning
import static com.google.common.util.concurrent.SequentialExecutor.WorkerRunningState.QUEUED; import static com.google.common.util.concurrent.SequentialExecutor.WorkerRunningState.QUEUED;
import static com.google.common.util.concurrent.SequentialExecutor.WorkerRunningState.QUEUING; import static com.google.common.util.concurrent.SequentialExecutor.WorkerRunningState.QUEUING;
import static com.google.common.util.concurrent.SequentialExecutor.WorkerRunningState.RUNNING; import static com.google.common.util.concurrent.SequentialExecutor.WorkerRunningState.RUNNING;
import static java.lang.System.identityHashCode;
import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Deque; import java.util.Deque;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@ -87,7 +86,7 @@ final class SequentialExecutor implements Executor {
* Adds a task to the queue and makes sure a worker thread is running. * Adds a task to the queue and makes sure a worker thread is running.
* *
* <p>If this method throws, e.g. a {@code RejectedExecutionException} from the delegate executor, * <p>If this method throws, e.g. a {@code RejectedExecutionException} from the delegate executor,
* execution of tasks will stop until a call to this method or to {@link #resume()} is made. * execution of tasks will stop until a call to this method is made.
*/ */
@Override @Override
public void execute(final Runnable task) { public void execute(final Runnable task) {
@ -116,6 +115,11 @@ final class SequentialExecutor implements Executor {
public void run() { public void run() {
task.run(); task.run();
} }
@Override
public String toString() {
return task.toString();
}
}; };
queue.add(submittedTask); queue.add(submittedTask);
workerRunningState = QUEUING; workerRunningState = QUEUING;
@ -160,8 +164,9 @@ final class SequentialExecutor implements Executor {
} }
/** Worker that runs tasks from {@link #queue} until it is empty. */ /** Worker that runs tasks from {@link #queue} until it is empty. */
private final class QueueWorker implements Runnable { private final class QueueWorker implements Runnable {
Runnable task;
@Override @Override
public void run() { public void run() {
try { try {
@ -193,7 +198,6 @@ final class SequentialExecutor implements Executor {
boolean hasSetRunning = false; boolean hasSetRunning = false;
try { try {
while (true) { while (true) {
Runnable task;
synchronized (queue) { synchronized (queue) {
// Choose whether this thread will run or not after acquiring the lock on the first // Choose whether this thread will run or not after acquiring the lock on the first
// iteration // iteration
@ -224,6 +228,8 @@ final class SequentialExecutor implements Executor {
task.run(); task.run();
} catch (RuntimeException e) { } catch (RuntimeException e) {
log.log(Level.SEVERE, "Exception while executing runnable " + task, e); log.log(Level.SEVERE, "Exception while executing runnable " + task, e);
} finally {
task = null;
} }
} }
} finally { } finally {
@ -235,5 +241,20 @@ final class SequentialExecutor implements Executor {
} }
} }
} }
@SuppressWarnings("GuardedBy")
@Override
public String toString() {
Runnable currentlyRunning = task;
if (currentlyRunning != null) {
return "SequentialExecutorWorker{running=" + currentlyRunning + "}";
}
return "SequentialExecutorWorker{state=" + workerRunningState + "}";
}
}
@Override
public String toString() {
return "SequentialExecutor@" + identityHashCode(this) + "{" + executor + "}";
} }
} }

View file

@ -31,7 +31,6 @@ import static com.google.common.util.concurrent.Service.State.STOPPING;
import static com.google.common.util.concurrent.Service.State.TERMINATED; import static com.google.common.util.concurrent.Service.State.TERMINATED;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
@ -40,7 +39,6 @@ import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@ -51,9 +49,6 @@ import com.google.common.collect.Multiset;
import com.google.common.collect.Ordering; import com.google.common.collect.Ordering;
import com.google.common.collect.SetMultimap; import com.google.common.collect.SetMultimap;
import com.google.common.util.concurrent.Service.State; import com.google.common.util.concurrent.Service.State;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.time.Duration; import java.time.Duration;
import java.util.Collections; import java.util.Collections;
@ -121,9 +116,8 @@ import java.util.logging.Logger;
* @author Luke Sandberg * @author Luke Sandberg
* @since 14.0 * @since 14.0
*/ */
@Beta
@GwtIncompatible @GwtIncompatible
public final class ServiceManager { public final class ServiceManager implements ServiceManagerBridge {
private static final Logger logger = Logger.getLogger(ServiceManager.class.getName()); private static final Logger logger = Logger.getLogger(ServiceManager.class.getName());
private static final ListenerCallQueue.Event<Listener> HEALTHY_EVENT = private static final ListenerCallQueue.Event<Listener> HEALTHY_EVENT =
new ListenerCallQueue.Event<Listener>() { new ListenerCallQueue.Event<Listener>() {
@ -159,7 +153,6 @@ public final class ServiceManager {
* @author Luke Sandberg * @author Luke Sandberg
* @since 15.0 (present as an interface in 14.0) * @since 15.0 (present as an interface in 14.0)
*/ */
@Beta // Should come out of Beta when ServiceManager does
public abstract static class Listener { public abstract static class Listener {
/** /**
* Called when the service initially becomes healthy. * Called when the service initially becomes healthy.
@ -245,8 +238,9 @@ public final class ServiceManager {
* during {@code Executor.execute} (e.g., a {@code RejectedExecutionException}) will be caught and * during {@code Executor.execute} (e.g., a {@code RejectedExecutionException}) will be caught and
* logged. * logged.
* *
* <p>For fast, lightweight listeners that would be safe to execute in any thread, consider * <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See
* calling {@link #addListener(Listener)}. * the discussion in the {@link ListenableFuture#addListener ListenableFuture.addListener}
* documentation.
* *
* @param listener the listener to run when the manager changes state * @param listener the listener to run when the manager changes state
* @param executor the executor in which the listeners callback methods will be run. * @param executor the executor in which the listeners callback methods will be run.
@ -255,26 +249,6 @@ public final class ServiceManager {
state.addListener(listener, executor); state.addListener(listener, executor);
} }
/**
* Registers a {@link Listener} to be run when this {@link ServiceManager} changes state. The
* listener will not have previous state changes replayed, so it is suggested that listeners are
* added before any of the managed services are {@linkplain Service#startAsync started}.
*
* <p>{@code addListener} guarantees execution ordering across calls to a given listener but not
* across calls to multiple listeners. Specifically, a given listener will have its callbacks
* invoked in the same order as the underlying service enters those states. Additionally, at most
* one of the listener's callbacks will execute at once. However, multiple listeners' callbacks
* may execute concurrently, and listeners may execute in an order different from the one in which
* they were registered.
*
* <p>RuntimeExceptions thrown by a listener will be caught and logged.
*
* @param listener the listener to run when the manager changes state
*/
public void addListener(Listener listener) {
state.addListener(listener, directExecutor());
}
/** /**
* Initiates service {@linkplain Service#startAsync startup} on all the services being managed. It * Initiates service {@linkplain Service#startAsync startup} on all the services being managed. It
* is only valid to call this method if all of the services are {@linkplain State#NEW new}. * is only valid to call this method if all of the services are {@linkplain State#NEW new}.
@ -283,7 +257,6 @@ public final class ServiceManager {
* @throws IllegalStateException if any of the Services are not {@link State#NEW new} when the * @throws IllegalStateException if any of the Services are not {@link State#NEW new} when the
* method is called. * method is called.
*/ */
public ServiceManager startAsync() { public ServiceManager startAsync() {
for (Service service : services) { for (Service service : services) {
State state = service.state(); State state = service.state();
@ -353,7 +326,6 @@ public final class ServiceManager {
* *
* @return this * @return this
*/ */
public ServiceManager stopAsync() { public ServiceManager stopAsync() {
for (Service service : services) { for (Service service : services) {
service.stopAsync(); service.stopAsync();
@ -363,8 +335,8 @@ public final class ServiceManager {
/** /**
* Waits for the all the services to reach a terminal state. After this method returns all * Waits for the all the services to reach a terminal state. After this method returns all
* services will either be {@linkplain Service.State#TERMINATED terminated} or {@linkplain * services will either be {@linkplain State#TERMINATED terminated} or {@linkplain
* Service.State#FAILED failed}. * State#FAILED failed}.
*/ */
public void awaitStopped() { public void awaitStopped() {
state.awaitStopped(); state.awaitStopped();
@ -372,8 +344,8 @@ public final class ServiceManager {
/** /**
* Waits for the all the services to reach a terminal state for no more than the given time. After * Waits for the all the services to reach a terminal state for no more than the given time. After
* this method returns all services will either be {@linkplain Service.State#TERMINATED * this method returns all services will either be {@linkplain State#TERMINATED
* terminated} or {@linkplain Service.State#FAILED failed}. * terminated} or {@linkplain State#FAILED failed}.
* *
* @param timeout the maximum time to wait * @param timeout the maximum time to wait
* @throws TimeoutException if not all of the services have stopped within the deadline * @throws TimeoutException if not all of the services have stopped within the deadline
@ -385,8 +357,8 @@ public final class ServiceManager {
/** /**
* Waits for the all the services to reach a terminal state for no more than the given time. After * Waits for the all the services to reach a terminal state for no more than the given time. After
* this method returns all services will either be {@linkplain Service.State#TERMINATED * this method returns all services will either be {@linkplain State#TERMINATED
* terminated} or {@linkplain Service.State#FAILED failed}. * terminated} or {@linkplain State#FAILED failed}.
* *
* @param timeout the maximum time to wait * @param timeout the maximum time to wait
* @param unit the time unit of the timeout argument * @param unit the time unit of the timeout argument
@ -417,8 +389,11 @@ public final class ServiceManager {
* *
* <p>N.B. This snapshot is guaranteed to be consistent, i.e. the set of states returned will * <p>N.B. This snapshot is guaranteed to be consistent, i.e. the set of states returned will
* correspond to a point in time view of the services. * correspond to a point in time view of the services.
*
* @since 29.0 (present with return type {@code ImmutableMultimap} since 14.0)
*/ */
public ImmutableMultimap<State, Service> servicesByState() { @Override
public ImmutableSetMultimap<State, Service> servicesByState() {
return state.servicesByState(); return state.servicesByState();
} }
@ -479,7 +454,6 @@ public final class ServiceManager {
*/ */
final Monitor.Guard awaitHealthGuard = new AwaitHealthGuard(); final Monitor.Guard awaitHealthGuard = new AwaitHealthGuard();
final class AwaitHealthGuard extends Monitor.Guard { final class AwaitHealthGuard extends Monitor.Guard {
AwaitHealthGuard() { AwaitHealthGuard() {
super(ServiceManagerState.this.monitor); super(ServiceManagerState.this.monitor);
@ -498,7 +472,6 @@ public final class ServiceManager {
/** Controls how long to wait for all services to reach a terminal state. */ /** Controls how long to wait for all services to reach a terminal state. */
final Monitor.Guard stoppedGuard = new StoppedGuard(); final Monitor.Guard stoppedGuard = new StoppedGuard();
final class StoppedGuard extends Monitor.Guard { final class StoppedGuard extends Monitor.Guard {
StoppedGuard() { StoppedGuard() {
super(ServiceManagerState.this.monitor); super(ServiceManagerState.this.monitor);
@ -615,7 +588,7 @@ public final class ServiceManager {
} }
} }
ImmutableMultimap<State, Service> servicesByState() { ImmutableSetMultimap<State, Service> servicesByState() {
ImmutableSetMultimap.Builder<State, Service> builder = ImmutableSetMultimap.builder(); ImmutableSetMultimap.Builder<State, Service> builder = ImmutableSetMultimap.builder();
monitor.enter(); monitor.enter();
try { try {

View file

@ -0,0 +1,31 @@
/*
* Copyright (C) 2020 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.util.concurrent;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.util.concurrent.Service.State;
/**
* Superinterface of {@link ServiceManager} to introduce a bridge method for {@code
* servicesByState()}, to ensure binary compatibility with older Guava versions that specified
* {@code servicesByState()} to return {@code ImmutableMultimap}.
*/
@GwtIncompatible
interface ServiceManagerBridge {
ImmutableMultimap<State, Service> servicesByState();
}

View file

@ -31,6 +31,11 @@ abstract class SmoothRateLimiter extends RateLimiter {
* compute, for an incoming request, the appropriate throttle time, and make the calling thread * compute, for an incoming request, the appropriate throttle time, and make the calling thread
* wait as much. * wait as much.
* *
* The primary feature of a RateLimiter is its "stable rate", the maximum rate that it should
* allow in normal conditions. This is enforced by "throttling" incoming requests as needed. For
* example, we could compute the appropriate throttle time for an incoming request, and make the
* calling thread wait for that time.
*
* The simplest way to maintain a rate of QPS is to keep the timestamp of the last granted * The simplest way to maintain a rate of QPS is to keep the timestamp of the last granted
* request, and ensure that (1/QPS) seconds have elapsed since then. For example, for a rate of * request, and ensure that (1/QPS) seconds have elapsed since then. For example, for a rate of
* QPS=5 (5 tokens per second), if we ensure that a request isn't granted earlier than 200ms after * QPS=5 (5 tokens per second), if we ensure that a request isn't granted earlier than 200ms after

View file

@ -65,7 +65,6 @@ public final class UncaughtExceptionHandlers {
@Override @Override
public void uncaughtException(Thread t, Throwable e) { public void uncaughtException(Thread t, Throwable e) {
try { try {
// cannot use FormattingLogger due to a dependency loop
logger.log( logger.log(
SEVERE, String.format(Locale.ROOT, "Caught an exception in %s. Shutting down.", t), e); SEVERE, String.format(Locale.ROOT, "Caught an exception in %s. Shutting down.", t), e);
} catch (Throwable errorInLogging) { } catch (Throwable errorInLogging) {

View file

@ -14,6 +14,7 @@
package com.google.common.util.concurrent; package com.google.common.util.concurrent;
import static com.google.common.base.Verify.verify;
import static com.google.common.util.concurrent.Internal.toNanosSaturated; import static com.google.common.util.concurrent.Internal.toNanosSaturated;
import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS;
@ -21,17 +22,18 @@ import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import java.time.Duration; import java.time.Duration;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.Semaphore; import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/** /**
* Utilities for treating interruptible operations as uninterruptible. In all cases, if a thread is * Utilities for treating interruptible operations as uninterruptible. In all cases, if a thread is
@ -73,7 +75,6 @@ public final class Uninterruptibles {
* *
* @since 28.0 * @since 28.0
*/ */
// TODO(cpovirk): Consider being more strict.
@GwtIncompatible // concurrency @GwtIncompatible // concurrency
@Beta @Beta
public static boolean awaitUninterruptibly(CountDownLatch latch, Duration timeout) { public static boolean awaitUninterruptibly(CountDownLatch latch, Duration timeout) {
@ -84,7 +85,6 @@ public final class Uninterruptibles {
* Invokes {@code latch.}{@link CountDownLatch#await(long, TimeUnit) await(timeout, unit)} * Invokes {@code latch.}{@link CountDownLatch#await(long, TimeUnit) await(timeout, unit)}
* uninterruptibly. * uninterruptibly.
*/ */
// TODO(cpovirk): Consider being more strict.
@GwtIncompatible // concurrency @GwtIncompatible // concurrency
@SuppressWarnings("GoodTime") // should accept a java.time.Duration @SuppressWarnings("GoodTime") // should accept a java.time.Duration
public static boolean awaitUninterruptibly(CountDownLatch latch, long timeout, TimeUnit unit) { public static boolean awaitUninterruptibly(CountDownLatch latch, long timeout, TimeUnit unit) {
@ -228,7 +228,6 @@ public final class Uninterruptibles {
* @throws ExecutionException if the computation threw an exception * @throws ExecutionException if the computation threw an exception
* @throws CancellationException if the computation was cancelled * @throws CancellationException if the computation was cancelled
*/ */
public static <V> V getUninterruptibly(Future<V> future) throws ExecutionException { public static <V> V getUninterruptibly(Future<V> future) throws ExecutionException {
boolean interrupted = false; boolean interrupted = false;
try { try {
@ -265,7 +264,6 @@ public final class Uninterruptibles {
* @throws TimeoutException if the wait timed out * @throws TimeoutException if the wait timed out
* @since 28.0 * @since 28.0
*/ */
@GwtIncompatible // java.time.Duration @GwtIncompatible // java.time.Duration
@Beta @Beta
public static <V> V getUninterruptibly(Future<V> future, Duration timeout) public static <V> V getUninterruptibly(Future<V> future, Duration timeout)
@ -291,7 +289,6 @@ public final class Uninterruptibles {
* @throws CancellationException if the computation was cancelled * @throws CancellationException if the computation was cancelled
* @throws TimeoutException if the wait timed out * @throws TimeoutException if the wait timed out
*/ */
@GwtIncompatible // TODO @GwtIncompatible // TODO
@SuppressWarnings("GoodTime") // should accept a java.time.Duration @SuppressWarnings("GoodTime") // should accept a java.time.Duration
public static <V> V getUninterruptibly(Future<V> future, long timeout, TimeUnit unit) public static <V> V getUninterruptibly(Future<V> future, long timeout, TimeUnit unit)
@ -471,6 +468,104 @@ public final class Uninterruptibles {
} }
} }
/**
* Invokes {@code lock.}{@link Lock#tryLock(long, TimeUnit) tryLock(timeout, unit)}
* uninterruptibly.
*
* @since 30.0
*/
@GwtIncompatible // concurrency
@Beta
public static boolean tryLockUninterruptibly(Lock lock, Duration timeout) {
return tryLockUninterruptibly(lock, toNanosSaturated(timeout), TimeUnit.NANOSECONDS);
}
/**
* Invokes {@code lock.}{@link Lock#tryLock(long, TimeUnit) tryLock(timeout, unit)}
* uninterruptibly.
*
* @since 30.0
*/
@GwtIncompatible // concurrency
@SuppressWarnings("GoodTime") // should accept a java.time.Duration
public static boolean tryLockUninterruptibly(Lock lock, long timeout, TimeUnit unit) {
boolean interrupted = false;
try {
long remainingNanos = unit.toNanos(timeout);
long end = System.nanoTime() + remainingNanos;
while (true) {
try {
return lock.tryLock(remainingNanos, NANOSECONDS);
} catch (InterruptedException e) {
interrupted = true;
remainingNanos = end - System.nanoTime();
}
}
} finally {
if (interrupted) {
Thread.currentThread().interrupt();
}
}
}
/**
* Invokes {@code executor.}{@link ExecutorService#awaitTermination(long, TimeUnit)
* awaitTermination(long, TimeUnit)} uninterruptibly with no timeout.
*
* @since 30.0
*/
@Beta
@GwtIncompatible // concurrency
public static void awaitTerminationUninterruptibly(ExecutorService executor) {
// TODO(cpovirk): We could optimize this to avoid calling nanoTime() at all.
verify(awaitTerminationUninterruptibly(executor, Long.MAX_VALUE, NANOSECONDS));
}
/**
* Invokes {@code executor.}{@link ExecutorService#awaitTermination(long, TimeUnit)
* awaitTermination(long, TimeUnit)} uninterruptibly.
*
* @since 30.0
*/
@Beta
@GwtIncompatible // concurrency
public static boolean awaitTerminationUninterruptibly(
ExecutorService executor, Duration timeout) {
return awaitTerminationUninterruptibly(executor, toNanosSaturated(timeout), NANOSECONDS);
}
/**
* Invokes {@code executor.}{@link ExecutorService#awaitTermination(long, TimeUnit)
* awaitTermination(long, TimeUnit)} uninterruptibly.
*
* @since 30.0
*/
@Beta
@GwtIncompatible // concurrency
@SuppressWarnings("GoodTime")
public static boolean awaitTerminationUninterruptibly(
ExecutorService executor, long timeout, TimeUnit unit) {
boolean interrupted = false;
try {
long remainingNanos = unit.toNanos(timeout);
long end = System.nanoTime() + remainingNanos;
while (true) {
try {
return executor.awaitTermination(remainingNanos, NANOSECONDS);
} catch (InterruptedException e) {
interrupted = true;
remainingNanos = end - System.nanoTime();
}
}
} finally {
if (interrupted) {
Thread.currentThread().interrupt();
}
}
}
// TODO(user): Add support for waitUninterruptibly. // TODO(user): Add support for waitUninterruptibly.
private Uninterruptibles() {} private Uninterruptibles() {}