diff --git a/android/guava-tests/test/com/google/common/cache/CacheBuilderTest.java b/android/guava-tests/test/com/google/common/cache/CacheBuilderTest.java index 6730c48c3dbf..3c39cf30857c 100644 --- a/android/guava-tests/test/com/google/common/cache/CacheBuilderTest.java +++ b/android/guava-tests/test/com/google/common/cache/CacheBuilderTest.java @@ -36,6 +36,7 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.testing.NullPointerTester; +import java.time.Duration; import java.util.Map; import java.util.Random; import java.util.Set; @@ -228,6 +229,18 @@ public void testValueStrengthSetTwice() { assertThrows(IllegalStateException.class, () -> builder2.weakValues()); } + @GwtIncompatible // Duration + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // No more dangerous than wherever the caller got the Duration from + public void testLargeDurationsAreOk() { + Duration threeHundredYears = Duration.ofDays(365 * 300); + CacheBuilder unused = + CacheBuilder.newBuilder() + .expireAfterWrite(threeHundredYears) + .expireAfterAccess(threeHundredYears) + .refreshAfterWrite(threeHundredYears); + } + public void testTimeToLive_negative() { CacheBuilder builder = CacheBuilder.newBuilder(); try { @@ -237,6 +250,14 @@ public void testTimeToLive_negative() { } } + @GwtIncompatible // Duration + @SuppressWarnings("Java7ApiChecker") + public void testTimeToLive_negative_duration() { + CacheBuilder builder = CacheBuilder.newBuilder(); + assertThrows( + IllegalArgumentException.class, () -> builder.expireAfterWrite(Duration.ofSeconds(-1))); + } + @SuppressWarnings("ReturnValueIgnored") public void testTimeToLive_small() { CacheBuilder.newBuilder().expireAfterWrite(1, NANOSECONDS).build(identityLoader()); @@ -254,6 +275,14 @@ public void testTimeToLive_setTwice() { } } + @GwtIncompatible // Duration + @SuppressWarnings("Java7ApiChecker") + public void testTimeToLive_setTwice_duration() { + CacheBuilder builder = + CacheBuilder.newBuilder().expireAfterWrite(Duration.ofHours(1)); + assertThrows(IllegalStateException.class, () -> builder.expireAfterWrite(Duration.ofHours(1))); + } + public void testTimeToIdle_negative() { CacheBuilder builder = CacheBuilder.newBuilder(); try { @@ -263,6 +292,14 @@ public void testTimeToIdle_negative() { } } + @GwtIncompatible // Duration + @SuppressWarnings("Java7ApiChecker") + public void testTimeToIdle_negative_duration() { + CacheBuilder builder = CacheBuilder.newBuilder(); + assertThrows( + IllegalArgumentException.class, () -> builder.expireAfterAccess(Duration.ofSeconds(-1))); + } + @SuppressWarnings("ReturnValueIgnored") public void testTimeToIdle_small() { CacheBuilder.newBuilder().expireAfterAccess(1, NANOSECONDS).build(identityLoader()); @@ -280,12 +317,21 @@ public void testTimeToIdle_setTwice() { } } - @SuppressWarnings("ReturnValueIgnored") + @GwtIncompatible // Duration + @SuppressWarnings("Java7ApiChecker") + public void testTimeToIdle_setTwice_duration() { + CacheBuilder builder = + CacheBuilder.newBuilder().expireAfterAccess(Duration.ofHours(1)); + assertThrows(IllegalStateException.class, () -> builder.expireAfterAccess(Duration.ofHours(1))); + } + + @SuppressWarnings("Java7ApiChecker") public void testTimeToIdleAndToLive() { - CacheBuilder.newBuilder() - .expireAfterWrite(1, NANOSECONDS) - .expireAfterAccess(1, NANOSECONDS) - .build(identityLoader()); + LoadingCache unused = + CacheBuilder.newBuilder() + .expireAfterWrite(1, NANOSECONDS) + .expireAfterAccess(1, NANOSECONDS) + .build(identityLoader()); // well, it didn't blow up. } @@ -295,6 +341,13 @@ public void testRefresh_zero() { assertThrows(IllegalArgumentException.class, () -> builder.refreshAfterWrite(0, SECONDS)); } + @GwtIncompatible // Duration + @SuppressWarnings("Java7ApiChecker") + public void testRefresh_zero_duration() { + CacheBuilder builder = CacheBuilder.newBuilder(); + assertThrows(IllegalArgumentException.class, () -> builder.refreshAfterWrite(Duration.ZERO)); + } + @GwtIncompatible // refreshAfterWrite public void testRefresh_setTwice() { CacheBuilder builder = @@ -302,6 +355,14 @@ public void testRefresh_setTwice() { assertThrows(IllegalStateException.class, () -> builder.refreshAfterWrite(3600, SECONDS)); } + @GwtIncompatible // Duration + @SuppressWarnings("Java7ApiChecker") + public void testRefresh_setTwice_duration() { + CacheBuilder builder = + CacheBuilder.newBuilder().refreshAfterWrite(Duration.ofHours(1)); + assertThrows(IllegalStateException.class, () -> builder.refreshAfterWrite(Duration.ofHours(1))); + } + public void testTicker_setTwice() { Ticker testTicker = Ticker.systemTicker(); CacheBuilder builder = CacheBuilder.newBuilder().ticker(testTicker); @@ -508,6 +569,7 @@ public void testRemovalNotification_get_basher() throws InterruptedException { final AtomicInteger computeCount = new AtomicInteger(); final AtomicInteger exceptionCount = new AtomicInteger(); final AtomicInteger computeNullCount = new AtomicInteger(); + @SuppressWarnings("CacheLoaderNull") // test of handling of erroneous implementation CacheLoader countingIdentityLoader = new CacheLoader() { @Override diff --git a/android/guava/src/com/google/common/cache/CacheBuilder.java b/android/guava/src/com/google/common/cache/CacheBuilder.java index c3fbdac732cb..3796ee94a429 100644 --- a/android/guava/src/com/google/common/cache/CacheBuilder.java +++ b/android/guava/src/com/google/common/cache/CacheBuilder.java @@ -30,8 +30,10 @@ import com.google.common.cache.AbstractCache.StatsCounter; import com.google.common.cache.LocalCache.Strength; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.j2objc.annotations.J2ObjCIncompatible; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; +import java.time.Duration; import java.util.ConcurrentModificationException; import java.util.IdentityHashMap; import java.util.Map; @@ -104,7 +106,7 @@ *
{@code
  * LoadingCache graphs = CacheBuilder.newBuilder()
  *     .maximumSize(10000)
- *     .expireAfterWrite(10, TimeUnit.MINUTES)
+ *     .expireAfterWrite(Duration.ofMinutes(10))
  *     .removalListener(MY_LISTENER)
  *     .build(
  *         new CacheLoader() {
@@ -194,10 +196,10 @@ public final class CacheBuilder {
   private static final int DEFAULT_INITIAL_CAPACITY = 16;
   private static final int DEFAULT_CONCURRENCY_LEVEL = 4;
 
-  @SuppressWarnings("GoodTime") // should be a java.time.Duration
+  @SuppressWarnings("GoodTime") // should be a Duration
   private static final int DEFAULT_EXPIRATION_NANOS = 0;
 
-  @SuppressWarnings("GoodTime") // should be a java.time.Duration
+  @SuppressWarnings("GoodTime") // should be a Duration
   private static final int DEFAULT_REFRESH_NANOS = 0;
 
   static final Supplier NULL_STATS_COUNTER =
@@ -289,13 +291,13 @@ private static final class LoggerHolder {
   @CheckForNull Strength keyStrength;
   @CheckForNull Strength valueStrength;
 
-  @SuppressWarnings("GoodTime") // should be a java.time.Duration
+  @SuppressWarnings("GoodTime") // should be a Duration
   long expireAfterWriteNanos = UNSET_INT;
 
-  @SuppressWarnings("GoodTime") // should be a java.time.Duration
+  @SuppressWarnings("GoodTime") // should be a Duration
   long expireAfterAccessNanos = UNSET_INT;
 
-  @SuppressWarnings("GoodTime") // should be a java.time.Duration
+  @SuppressWarnings("GoodTime") // should be a Duration
   long refreshNanos = UNSET_INT;
 
   @CheckForNull Equivalence keyEquivalence;
@@ -711,12 +713,48 @@ Strength getValueStrength() {
    *
    * @param duration the length of time after an entry is created that it should be automatically
    *     removed
+   * @return this {@code CacheBuilder} instance (for chaining)
+   * @throws IllegalArgumentException if {@code duration} is negative
+   * @throws IllegalStateException if {@link #expireAfterWrite} was already set
+   * @throws ArithmeticException for durations greater than +/- approximately 292 years
+   * @since NEXT (but since 25.0 in the JRE flavor)
+   */
+  @J2ObjCIncompatible
+  @GwtIncompatible // Duration
+  @SuppressWarnings({
+    "GoodTime", // Duration decomposition
+    "Java7ApiChecker",
+  })
+  @IgnoreJRERequirement // No more dangerous than wherever the caller got the Duration from
+  @CanIgnoreReturnValue
+  public CacheBuilder expireAfterWrite(Duration duration) {
+    return expireAfterWrite(toNanosSaturated(duration), TimeUnit.NANOSECONDS);
+  }
+
+  /**
+   * Specifies that each entry should be automatically removed from the cache once a fixed duration
+   * has elapsed after the entry's creation, or the most recent replacement of its value.
+   *
+   * 

When {@code duration} is zero, this method hands off to {@link #maximumSize(long) + * maximumSize}{@code (0)}, ignoring any otherwise-specified maximum size or weight. This can be + * useful in testing, or to disable caching temporarily without a code change. + * + *

Expired entries may be counted in {@link Cache#size}, but will never be visible to read or + * write operations. Expired entries are cleaned up as part of the routine maintenance described + * in the class javadoc. + * + *

If you can represent the duration as a {@link Duration} (which should be preferred when + * feasible), use {@link #expireAfterWrite(Duration)} instead. + * + * @param duration the length of time after an entry is created that it should be automatically + * removed * @param unit the unit that {@code duration} is expressed in * @return this {@code CacheBuilder} instance (for chaining) * @throws IllegalArgumentException if {@code duration} is negative * @throws IllegalStateException if {@link #expireAfterWrite} was already set */ - @SuppressWarnings("GoodTime") // should accept a java.time.Duration + @SuppressWarnings("GoodTime") // should accept a Duration @CanIgnoreReturnValue public CacheBuilder expireAfterWrite(long duration, TimeUnit unit) { checkState( @@ -733,6 +771,44 @@ long getExpireAfterWriteNanos() { return (expireAfterWriteNanos == UNSET_INT) ? DEFAULT_EXPIRATION_NANOS : expireAfterWriteNanos; } + /** + * Specifies that each entry should be automatically removed from the cache once a fixed duration + * has elapsed after the entry's creation, the most recent replacement of its value, or its last + * access. Access time is reset by all cache read and write operations (including {@code + * Cache.asMap().get(Object)} and {@code Cache.asMap().put(K, V)}), but not by {@code + * containsKey(Object)}, nor by operations on the collection-views of {@link Cache#asMap}}. So, + * for example, iterating through {@code Cache.asMap().entrySet()} does not reset access time for + * the entries you retrieve. + * + *

When {@code duration} is zero, this method hands off to {@link #maximumSize(long) + * maximumSize}{@code (0)}, ignoring any otherwise-specified maximum size or weight. This can be + * useful in testing, or to disable caching temporarily without a code change. + * + *

Expired entries may be counted in {@link Cache#size}, but will never be visible to read or + * write operations. Expired entries are cleaned up as part of the routine maintenance described + * in the class javadoc. + * + * @param duration the length of time after an entry is last accessed that it should be + * automatically removed + * @return this {@code CacheBuilder} instance (for chaining) + * @throws IllegalArgumentException if {@code duration} is negative + * @throws IllegalStateException if {@link #expireAfterAccess} was already set + * @throws ArithmeticException for durations greater than +/- approximately 292 years + * @since NEXT (but since 25.0 in the JRE flavor) + */ + @J2ObjCIncompatible + @GwtIncompatible // Duration + @SuppressWarnings({ + "GoodTime", // Duration decomposition + "Java7ApiChecker", + }) + @IgnoreJRERequirement // No more dangerous than wherever the caller got the Duration from + @CanIgnoreReturnValue + public CacheBuilder expireAfterAccess(Duration duration) { + return expireAfterAccess(toNanosSaturated(duration), TimeUnit.NANOSECONDS); + } + /** * Specifies that each entry should be automatically removed from the cache once a fixed duration * has elapsed after the entry's creation, the most recent replacement of its value, or its last @@ -750,6 +826,9 @@ long getExpireAfterWriteNanos() { * write operations. Expired entries are cleaned up as part of the routine maintenance described * in the class javadoc. * + *

If you can represent the duration as a {@link Duration} (which should be preferred when + * feasible), use {@link #expireAfterAccess(Duration)} instead. + * * @param duration the length of time after an entry is last accessed that it should be * automatically removed * @param unit the unit that {@code duration} is expressed in @@ -757,7 +836,7 @@ long getExpireAfterWriteNanos() { * @throws IllegalArgumentException if {@code duration} is negative * @throws IllegalStateException if {@link #expireAfterAccess} was already set */ - @SuppressWarnings("GoodTime") // should accept a java.time.Duration + @SuppressWarnings("GoodTime") // should accept a Duration @CanIgnoreReturnValue public CacheBuilder expireAfterAccess(long duration, TimeUnit unit) { checkState( @@ -776,6 +855,46 @@ long getExpireAfterAccessNanos() { : expireAfterAccessNanos; } + /** + * Specifies that active entries are eligible for automatic refresh once a fixed duration has + * elapsed after the entry's creation, or the most recent replacement of its value. The semantics + * of refreshes are specified in {@link LoadingCache#refresh}, and are performed by calling {@link + * CacheLoader#reload}. + * + *

As the default implementation of {@link CacheLoader#reload} is synchronous, it is + * recommended that users of this method override {@link CacheLoader#reload} with an asynchronous + * implementation; otherwise refreshes will be performed during unrelated cache read and write + * operations. + * + *

Currently automatic refreshes are performed when the first stale request for an entry + * occurs. The request triggering refresh will make a synchronous call to {@link + * CacheLoader#reload} + * to obtain a future of the new value. If the returned future is already complete, it is returned + * immediately. Otherwise, the old value is returned. + * + *

Note: all exceptions thrown during refresh will be logged and then swallowed. + * + * @param duration the length of time after an entry is created that it should be considered + * stale, and thus eligible for refresh + * @return this {@code CacheBuilder} instance (for chaining) + * @throws IllegalArgumentException if {@code duration} is negative + * @throws IllegalStateException if {@link #refreshAfterWrite} was already set + * @throws ArithmeticException for durations greater than +/- approximately 292 years + * @since NEXT (but since 25.0 in the JRE flavor) + */ + @J2ObjCIncompatible + @GwtIncompatible // Duration + @SuppressWarnings({ + "GoodTime", // Duration decomposition + "Java7ApiChecker", + }) + @IgnoreJRERequirement // No more dangerous than wherever the caller got the Duration from + @CanIgnoreReturnValue + public CacheBuilder refreshAfterWrite(Duration duration) { + return refreshAfterWrite(toNanosSaturated(duration), TimeUnit.NANOSECONDS); + } + /** * Specifies that active entries are eligible for automatic refresh once a fixed duration has * elapsed after the entry's creation, or the most recent replacement of its value. The semantics @@ -795,6 +914,9 @@ long getExpireAfterAccessNanos() { * *

Note: all exceptions thrown during refresh will be logged and then swallowed. * + *

If you can represent the duration as a {@link Duration} (which should be preferred when + * feasible), use {@link #refreshAfterWrite(Duration)} instead. + * * @param duration the length of time after an entry is created that it should be considered * stale, and thus eligible for refresh * @param unit the unit that {@code duration} is expressed in @@ -804,7 +926,7 @@ long getExpireAfterAccessNanos() { * @since 11.0 */ @GwtIncompatible // To be supported (synchronously). - @SuppressWarnings("GoodTime") // should accept a java.time.Duration + @SuppressWarnings("GoodTime") // should accept a Duration @CanIgnoreReturnValue public CacheBuilder refreshAfterWrite(long duration, TimeUnit unit) { checkNotNull(unit); @@ -1002,4 +1124,27 @@ public String toString() { } return s.toString(); } + + /** + * Returns the number of nanoseconds of the given duration without throwing or overflowing. + * + *

Instead of throwing {@link ArithmeticException}, this method silently saturates to either + * {@link Long#MAX_VALUE} or {@link Long#MIN_VALUE}. This behavior can be useful when decomposing + * a duration in order to call a legacy API which requires a {@code long, TimeUnit} pair. + */ + @GwtIncompatible // Duration + @SuppressWarnings({ + "GoodTime", // Duration decomposition + "Java7ApiChecker", + }) + @IgnoreJRERequirement // No more dangerous than wherever the caller got the Duration from + private static long toNanosSaturated(Duration duration) { + // Using a try/catch seems lazy, but the catch block will rarely get invoked (except for + // durations longer than approximately +/- 292 years). + try { + return duration.toNanos(); + } catch (ArithmeticException tooBig) { + return duration.isNegative() ? Long.MIN_VALUE : Long.MAX_VALUE; + } + } } diff --git a/android/guava/src/com/google/common/cache/IgnoreJRERequirement.java b/android/guava/src/com/google/common/cache/IgnoreJRERequirement.java new file mode 100644 index 000000000000..c80ab07ea20a --- /dev/null +++ b/android/guava/src/com/google/common/cache/IgnoreJRERequirement.java @@ -0,0 +1,30 @@ +/* + * Copyright 2019 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.cache; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Target; + +/** + * Disables Animal Sniffer's checking of compatibility with older versions of Java/Android. + * + *

Each package's copy of this annotation needs to be listed in our {@code pom.xml}. + */ +@Target({METHOD, CONSTRUCTOR, TYPE}) +@ElementTypesAreNonnullByDefault +@interface IgnoreJRERequirement {} diff --git a/android/pom.xml b/android/pom.xml index c520031e7d78..9cb072447af7 100644 --- a/android/pom.xml +++ b/android/pom.xml @@ -185,7 +185,7 @@ - com.google.common.base.IgnoreJRERequirement,com.google.common.collect.IgnoreJRERequirement,com.google.common.hash.IgnoreJRERequirement,com.google.common.io.IgnoreJRERequirement,com.google.common.reflect.IgnoreJRERequirement,com.google.common.testing.IgnoreJRERequirement + com.google.common.base.IgnoreJRERequirement,com.google.common.cache.IgnoreJRERequirement,com.google.common.collect.IgnoreJRERequirement,com.google.common.hash.IgnoreJRERequirement,com.google.common.io.IgnoreJRERequirement,com.google.common.reflect.IgnoreJRERequirement,com.google.common.testing.IgnoreJRERequirement true com.toasttab.android diff --git a/guava-tests/test/com/google/common/cache/CacheBuilderTest.java b/guava-tests/test/com/google/common/cache/CacheBuilderTest.java index 7bb266aeaf04..691b46bdd96f 100644 --- a/guava-tests/test/com/google/common/cache/CacheBuilderTest.java +++ b/guava-tests/test/com/google/common/cache/CacheBuilderTest.java @@ -36,6 +36,7 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.testing.NullPointerTester; +import java.time.Duration; import java.util.Map; import java.util.Random; import java.util.Set; @@ -228,10 +229,10 @@ public void testValueStrengthSetTwice() { assertThrows(IllegalStateException.class, () -> builder2.weakValues()); } - @GwtIncompatible // java.time.Duration + @GwtIncompatible // Duration public void testLargeDurationsAreOk() { - java.time.Duration threeHundredYears = java.time.Duration.ofDays(365 * 300); - CacheBuilder builder = + Duration threeHundredYears = Duration.ofDays(365 * 300); + CacheBuilder unused = CacheBuilder.newBuilder() .expireAfterWrite(threeHundredYears) .expireAfterAccess(threeHundredYears) @@ -247,12 +248,11 @@ public void testTimeToLive_negative() { } } - @GwtIncompatible // java.time.Duration + @GwtIncompatible // Duration public void testTimeToLive_negative_duration() { CacheBuilder builder = CacheBuilder.newBuilder(); assertThrows( - IllegalArgumentException.class, - () -> builder.expireAfterWrite(java.time.Duration.ofSeconds(-1))); + IllegalArgumentException.class, () -> builder.expireAfterWrite(Duration.ofSeconds(-1))); } @SuppressWarnings("ReturnValueIgnored") @@ -272,12 +272,11 @@ public void testTimeToLive_setTwice() { } } - @GwtIncompatible // java.time.Duration + @GwtIncompatible // Duration public void testTimeToLive_setTwice_duration() { CacheBuilder builder = - CacheBuilder.newBuilder().expireAfterWrite(java.time.Duration.ofHours(1)); - assertThrows( - IllegalStateException.class, () -> builder.expireAfterWrite(java.time.Duration.ofHours(1))); + CacheBuilder.newBuilder().expireAfterWrite(Duration.ofHours(1)); + assertThrows(IllegalStateException.class, () -> builder.expireAfterWrite(Duration.ofHours(1))); } public void testTimeToIdle_negative() { @@ -289,12 +288,11 @@ public void testTimeToIdle_negative() { } } - @GwtIncompatible // java.time.Duration + @GwtIncompatible // Duration public void testTimeToIdle_negative_duration() { CacheBuilder builder = CacheBuilder.newBuilder(); assertThrows( - IllegalArgumentException.class, - () -> builder.expireAfterAccess(java.time.Duration.ofSeconds(-1))); + IllegalArgumentException.class, () -> builder.expireAfterAccess(Duration.ofSeconds(-1))); } @SuppressWarnings("ReturnValueIgnored") @@ -314,21 +312,19 @@ public void testTimeToIdle_setTwice() { } } - @GwtIncompatible // java.time.Duration + @GwtIncompatible // Duration public void testTimeToIdle_setTwice_duration() { CacheBuilder builder = - CacheBuilder.newBuilder().expireAfterAccess(java.time.Duration.ofHours(1)); - assertThrows( - IllegalStateException.class, - () -> builder.expireAfterAccess(java.time.Duration.ofHours(1))); + CacheBuilder.newBuilder().expireAfterAccess(Duration.ofHours(1)); + assertThrows(IllegalStateException.class, () -> builder.expireAfterAccess(Duration.ofHours(1))); } - @SuppressWarnings("ReturnValueIgnored") public void testTimeToIdleAndToLive() { - CacheBuilder.newBuilder() - .expireAfterWrite(1, NANOSECONDS) - .expireAfterAccess(1, NANOSECONDS) - .build(identityLoader()); + LoadingCache unused = + CacheBuilder.newBuilder() + .expireAfterWrite(1, NANOSECONDS) + .expireAfterAccess(1, NANOSECONDS) + .build(identityLoader()); // well, it didn't blow up. } @@ -338,11 +334,10 @@ public void testRefresh_zero() { assertThrows(IllegalArgumentException.class, () -> builder.refreshAfterWrite(0, SECONDS)); } - @GwtIncompatible // java.time.Duration + @GwtIncompatible // Duration public void testRefresh_zero_duration() { CacheBuilder builder = CacheBuilder.newBuilder(); - assertThrows( - IllegalArgumentException.class, () -> builder.refreshAfterWrite(java.time.Duration.ZERO)); + assertThrows(IllegalArgumentException.class, () -> builder.refreshAfterWrite(Duration.ZERO)); } @GwtIncompatible // refreshAfterWrite @@ -352,13 +347,11 @@ public void testRefresh_setTwice() { assertThrows(IllegalStateException.class, () -> builder.refreshAfterWrite(3600, SECONDS)); } - @GwtIncompatible // java.time.Duration + @GwtIncompatible // Duration public void testRefresh_setTwice_duration() { CacheBuilder builder = - CacheBuilder.newBuilder().refreshAfterWrite(java.time.Duration.ofHours(1)); - assertThrows( - IllegalStateException.class, - () -> builder.refreshAfterWrite(java.time.Duration.ofHours(1))); + CacheBuilder.newBuilder().refreshAfterWrite(Duration.ofHours(1)); + assertThrows(IllegalStateException.class, () -> builder.refreshAfterWrite(Duration.ofHours(1))); } public void testTicker_setTwice() { @@ -567,6 +560,7 @@ public void testRemovalNotification_get_basher() throws InterruptedException { final AtomicInteger computeCount = new AtomicInteger(); final AtomicInteger exceptionCount = new AtomicInteger(); final AtomicInteger computeNullCount = new AtomicInteger(); + @SuppressWarnings("CacheLoaderNull") // test of handling of erroneous implementation CacheLoader countingIdentityLoader = new CacheLoader() { @Override diff --git a/guava/src/com/google/common/cache/CacheBuilder.java b/guava/src/com/google/common/cache/CacheBuilder.java index ca0754c83f92..d571d57c28ef 100644 --- a/guava/src/com/google/common/cache/CacheBuilder.java +++ b/guava/src/com/google/common/cache/CacheBuilder.java @@ -33,6 +33,7 @@ import com.google.j2objc.annotations.J2ObjCIncompatible; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; +import java.time.Duration; import java.util.ConcurrentModificationException; import java.util.IdentityHashMap; import java.util.Map; @@ -195,10 +196,10 @@ public final class CacheBuilder { private static final int DEFAULT_INITIAL_CAPACITY = 16; private static final int DEFAULT_CONCURRENCY_LEVEL = 4; - @SuppressWarnings("GoodTime") // should be a java.time.Duration + @SuppressWarnings("GoodTime") // should be a Duration private static final int DEFAULT_EXPIRATION_NANOS = 0; - @SuppressWarnings("GoodTime") // should be a java.time.Duration + @SuppressWarnings("GoodTime") // should be a Duration private static final int DEFAULT_REFRESH_NANOS = 0; static final Supplier NULL_STATS_COUNTER = @@ -290,13 +291,13 @@ private static final class LoggerHolder { @CheckForNull Strength keyStrength; @CheckForNull Strength valueStrength; - @SuppressWarnings("GoodTime") // should be a java.time.Duration + @SuppressWarnings("GoodTime") // should be a Duration long expireAfterWriteNanos = UNSET_INT; - @SuppressWarnings("GoodTime") // should be a java.time.Duration + @SuppressWarnings("GoodTime") // should be a Duration long expireAfterAccessNanos = UNSET_INT; - @SuppressWarnings("GoodTime") // should be a java.time.Duration + @SuppressWarnings("GoodTime") // should be a Duration long refreshNanos = UNSET_INT; @CheckForNull Equivalence keyEquivalence; @@ -569,7 +570,7 @@ public CacheBuilder maximumWeight(long maximumWeight) { * * @param weigher the weigher to use in calculating the weight of cache entries * @return this {@code CacheBuilder} instance (for chaining) - * @throws IllegalStateException if a weigher was already set or {@link #maximumSize} was + * @throws IllegalStateException if a weigher was already set or {@link #maximumSize(long)} was * previously called * @since 11.0 */ @@ -716,13 +717,14 @@ Strength getValueStrength() { * @throws IllegalArgumentException if {@code duration} is negative * @throws IllegalStateException if {@link #expireAfterWrite} was already set * @throws ArithmeticException for durations greater than +/- approximately 292 years - * @since 25.0 + * @since 25.0 (but only since 33.3.0 in the Android flavor) */ @J2ObjCIncompatible - @GwtIncompatible // java.time.Duration - @SuppressWarnings("GoodTime") // java.time.Duration decomposition + @GwtIncompatible // Duration + @SuppressWarnings("GoodTime") // Duration decomposition @CanIgnoreReturnValue - public CacheBuilder expireAfterWrite(java.time.Duration duration) { + public CacheBuilder expireAfterWrite(Duration duration) { return expireAfterWrite(toNanosSaturated(duration), TimeUnit.NANOSECONDS); } @@ -738,8 +740,8 @@ public CacheBuilder expireAfterWrite(java.time.Duration duration) { * write operations. Expired entries are cleaned up as part of the routine maintenance described * in the class javadoc. * - *

If you can represent the duration as a {@link java.time.Duration} (which should be preferred - * when feasible), use {@link #expireAfterWrite(Duration)} instead. + *

If you can represent the duration as a {@link Duration} (which should be preferred when + * feasible), use {@link #expireAfterWrite(Duration)} instead. * * @param duration the length of time after an entry is created that it should be automatically * removed @@ -748,7 +750,7 @@ public CacheBuilder expireAfterWrite(java.time.Duration duration) { * @throws IllegalArgumentException if {@code duration} is negative * @throws IllegalStateException if {@link #expireAfterWrite} was already set */ - @SuppressWarnings("GoodTime") // should accept a java.time.Duration + @SuppressWarnings("GoodTime") // should accept a Duration @CanIgnoreReturnValue public CacheBuilder expireAfterWrite(long duration, TimeUnit unit) { checkState( @@ -788,13 +790,14 @@ long getExpireAfterWriteNanos() { * @throws IllegalArgumentException if {@code duration} is negative * @throws IllegalStateException if {@link #expireAfterAccess} was already set * @throws ArithmeticException for durations greater than +/- approximately 292 years - * @since 25.0 + * @since 25.0 (but only since 33.3.0 in the Android flavor) */ @J2ObjCIncompatible - @GwtIncompatible // java.time.Duration - @SuppressWarnings("GoodTime") // java.time.Duration decomposition + @GwtIncompatible // Duration + @SuppressWarnings("GoodTime") // Duration decomposition @CanIgnoreReturnValue - public CacheBuilder expireAfterAccess(java.time.Duration duration) { + public CacheBuilder expireAfterAccess(Duration duration) { return expireAfterAccess(toNanosSaturated(duration), TimeUnit.NANOSECONDS); } @@ -815,8 +818,8 @@ public CacheBuilder expireAfterAccess(java.time.Duration duration) { * write operations. Expired entries are cleaned up as part of the routine maintenance described * in the class javadoc. * - *

If you can represent the duration as a {@link java.time.Duration} (which should be preferred - * when feasible), use {@link #expireAfterAccess(Duration)} instead. + *

If you can represent the duration as a {@link Duration} (which should be preferred when + * feasible), use {@link #expireAfterAccess(Duration)} instead. * * @param duration the length of time after an entry is last accessed that it should be * automatically removed @@ -825,7 +828,7 @@ public CacheBuilder expireAfterAccess(java.time.Duration duration) { * @throws IllegalArgumentException if {@code duration} is negative * @throws IllegalStateException if {@link #expireAfterAccess} was already set */ - @SuppressWarnings("GoodTime") // should accept a java.time.Duration + @SuppressWarnings("GoodTime") // should accept a Duration @CanIgnoreReturnValue public CacheBuilder expireAfterAccess(long duration, TimeUnit unit) { checkState( @@ -869,13 +872,14 @@ long getExpireAfterAccessNanos() { * @throws IllegalArgumentException if {@code duration} is negative * @throws IllegalStateException if {@link #refreshAfterWrite} was already set * @throws ArithmeticException for durations greater than +/- approximately 292 years - * @since 25.0 + * @since 25.0 (but only since 33.3.0 in the Android flavor) */ @J2ObjCIncompatible - @GwtIncompatible // java.time.Duration - @SuppressWarnings("GoodTime") // java.time.Duration decomposition + @GwtIncompatible // Duration + @SuppressWarnings("GoodTime") // Duration decomposition @CanIgnoreReturnValue - public CacheBuilder refreshAfterWrite(java.time.Duration duration) { + public CacheBuilder refreshAfterWrite(Duration duration) { return refreshAfterWrite(toNanosSaturated(duration), TimeUnit.NANOSECONDS); } @@ -898,8 +902,8 @@ public CacheBuilder refreshAfterWrite(java.time.Duration duration) { * *

Note: all exceptions thrown during refresh will be logged and then swallowed. * - *

If you can represent the duration as a {@link java.time.Duration} (which should be preferred - * when feasible), use {@link #refreshAfterWrite(Duration)} instead. + *

If you can represent the duration as a {@link Duration} (which should be preferred when + * feasible), use {@link #refreshAfterWrite(Duration)} instead. * * @param duration the length of time after an entry is created that it should be considered * stale, and thus eligible for refresh @@ -910,7 +914,7 @@ public CacheBuilder refreshAfterWrite(java.time.Duration duration) { * @since 11.0 */ @GwtIncompatible // To be supported (synchronously). - @SuppressWarnings("GoodTime") // should accept a java.time.Duration + @SuppressWarnings("GoodTime") // should accept a Duration @CanIgnoreReturnValue public CacheBuilder refreshAfterWrite(long duration, TimeUnit unit) { checkNotNull(unit); @@ -1116,9 +1120,9 @@ public String toString() { * {@link Long#MAX_VALUE} or {@link Long#MIN_VALUE}. This behavior can be useful when decomposing * a duration in order to call a legacy API which requires a {@code long, TimeUnit} pair. */ - @GwtIncompatible // java.time.Duration + @GwtIncompatible // Duration @SuppressWarnings("GoodTime") // duration decomposition - private static long toNanosSaturated(java.time.Duration duration) { + private static long toNanosSaturated(Duration duration) { // Using a try/catch seems lazy, but the catch block will rarely get invoked (except for // durations longer than approximately +/- 292 years). try { diff --git a/guava/src/com/google/common/cache/IgnoreJRERequirement.java b/guava/src/com/google/common/cache/IgnoreJRERequirement.java new file mode 100644 index 000000000000..c80ab07ea20a --- /dev/null +++ b/guava/src/com/google/common/cache/IgnoreJRERequirement.java @@ -0,0 +1,30 @@ +/* + * Copyright 2019 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.cache; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Target; + +/** + * Disables Animal Sniffer's checking of compatibility with older versions of Java/Android. + * + *

Each package's copy of this annotation needs to be listed in our {@code pom.xml}. + */ +@Target({METHOD, CONSTRUCTOR, TYPE}) +@ElementTypesAreNonnullByDefault +@interface IgnoreJRERequirement {} diff --git a/pom.xml b/pom.xml index 7b1a3ccac12b..10d72519193e 100644 --- a/pom.xml +++ b/pom.xml @@ -186,7 +186,7 @@ - com.google.common.base.IgnoreJRERequirement,com.google.common.collect.IgnoreJRERequirement,com.google.common.hash.IgnoreJRERequirement,com.google.common.io.IgnoreJRERequirement,com.google.common.reflect.IgnoreJRERequirement,com.google.common.testing.IgnoreJRERequirement + com.google.common.base.IgnoreJRERequirement,com.google.common.cache.IgnoreJRERequirement,com.google.common.collect.IgnoreJRERequirement,com.google.common.hash.IgnoreJRERequirement,com.google.common.io.IgnoreJRERequirement,com.google.common.reflect.IgnoreJRERequirement,com.google.common.testing.IgnoreJRERequirement true org.codehaus.mojo.signature