• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package com.google.common.cache;
16 
17 import com.google.common.base.Function;
18 import com.google.common.base.MoreObjects;
19 import com.google.common.base.Objects;
20 import com.google.common.base.Optional;
21 import com.google.common.base.Preconditions;
22 import com.google.common.cache.LocalCache.Strength;
23 import com.google.common.collect.Iterables;
24 import com.google.common.collect.Lists;
25 import com.google.common.collect.Sets;
26 import java.util.List;
27 import java.util.Set;
28 import java.util.concurrent.TimeUnit;
29 import org.checkerframework.checker.nullness.qual.Nullable;
30 
31 /**
32  * Helper class for creating {@link CacheBuilder} instances with all combinations of several sets of
33  * parameters.
34  *
35  * @author mike nonemacher
36  */
37 class CacheBuilderFactory {
38   // Default values contain only 'null', which means don't call the CacheBuilder method (just give
39   // the CacheBuilder default).
40   private Set<Integer> concurrencyLevels = Sets.newHashSet((Integer) null);
41   private Set<Integer> initialCapacities = Sets.newHashSet((Integer) null);
42   private Set<Integer> maximumSizes = Sets.newHashSet((Integer) null);
43   private Set<DurationSpec> expireAfterWrites = Sets.newHashSet((DurationSpec) null);
44   private Set<DurationSpec> expireAfterAccesses = Sets.newHashSet((DurationSpec) null);
45   private Set<DurationSpec> refreshes = Sets.newHashSet((DurationSpec) null);
46   private Set<Strength> keyStrengths = Sets.newHashSet((Strength) null);
47   private Set<Strength> valueStrengths = Sets.newHashSet((Strength) null);
48 
withConcurrencyLevels(Set<Integer> concurrencyLevels)49   CacheBuilderFactory withConcurrencyLevels(Set<Integer> concurrencyLevels) {
50     this.concurrencyLevels = Sets.newLinkedHashSet(concurrencyLevels);
51     return this;
52   }
53 
withInitialCapacities(Set<Integer> initialCapacities)54   CacheBuilderFactory withInitialCapacities(Set<Integer> initialCapacities) {
55     this.initialCapacities = Sets.newLinkedHashSet(initialCapacities);
56     return this;
57   }
58 
withMaximumSizes(Set<Integer> maximumSizes)59   CacheBuilderFactory withMaximumSizes(Set<Integer> maximumSizes) {
60     this.maximumSizes = Sets.newLinkedHashSet(maximumSizes);
61     return this;
62   }
63 
withExpireAfterWrites(Set<DurationSpec> durations)64   CacheBuilderFactory withExpireAfterWrites(Set<DurationSpec> durations) {
65     this.expireAfterWrites = Sets.newLinkedHashSet(durations);
66     return this;
67   }
68 
withExpireAfterAccesses(Set<DurationSpec> durations)69   CacheBuilderFactory withExpireAfterAccesses(Set<DurationSpec> durations) {
70     this.expireAfterAccesses = Sets.newLinkedHashSet(durations);
71     return this;
72   }
73 
withRefreshes(Set<DurationSpec> durations)74   CacheBuilderFactory withRefreshes(Set<DurationSpec> durations) {
75     this.refreshes = Sets.newLinkedHashSet(durations);
76     return this;
77   }
78 
withKeyStrengths(Set<Strength> keyStrengths)79   CacheBuilderFactory withKeyStrengths(Set<Strength> keyStrengths) {
80     this.keyStrengths = Sets.newLinkedHashSet(keyStrengths);
81     Preconditions.checkArgument(!this.keyStrengths.contains(Strength.SOFT));
82     return this;
83   }
84 
withValueStrengths(Set<Strength> valueStrengths)85   CacheBuilderFactory withValueStrengths(Set<Strength> valueStrengths) {
86     this.valueStrengths = Sets.newLinkedHashSet(valueStrengths);
87     return this;
88   }
89 
buildAllPermutations()90   Iterable<CacheBuilder<Object, Object>> buildAllPermutations() {
91     Iterable<List<Object>> combinations =
92         buildCartesianProduct(
93             concurrencyLevels,
94             initialCapacities,
95             maximumSizes,
96             expireAfterWrites,
97             expireAfterAccesses,
98             refreshes,
99             keyStrengths,
100             valueStrengths);
101     return Iterables.transform(
102         combinations,
103         new Function<List<Object>, CacheBuilder<Object, Object>>() {
104           @Override
105           public CacheBuilder<Object, Object> apply(List<Object> combination) {
106             return createCacheBuilder(
107                 (Integer) combination.get(0),
108                 (Integer) combination.get(1),
109                 (Integer) combination.get(2),
110                 (DurationSpec) combination.get(3),
111                 (DurationSpec) combination.get(4),
112                 (DurationSpec) combination.get(5),
113                 (Strength) combination.get(6),
114                 (Strength) combination.get(7));
115           }
116         });
117   }
118 
119   private static final Function<Object, Optional<?>> NULLABLE_TO_OPTIONAL =
120       new Function<Object, Optional<?>>() {
121         @Override
122         public Optional<?> apply(@Nullable Object obj) {
123           return Optional.fromNullable(obj);
124         }
125       };
126 
127   private static final Function<Optional<?>, @Nullable Object> OPTIONAL_TO_NULLABLE =
128       new Function<Optional<?>, @Nullable Object>() {
129         @Override
130         public @Nullable Object apply(Optional<?> optional) {
131           return optional.orNull();
132         }
133       };
134 
135   /**
136    * Sets.cartesianProduct doesn't allow sets that contain null, but we want null to mean "don't
137    * call the associated CacheBuilder method" - that is, get the default CacheBuilder behavior. This
138    * method wraps the elements in the input sets (which may contain null) as Optionals, calls
139    * Sets.cartesianProduct with those, then transforms the result to unwrap the Optionals.
140    */
141   private Iterable<List<Object>> buildCartesianProduct(Set<?>... sets) {
142     List<Set<Optional<?>>> optionalSets = Lists.newArrayListWithExpectedSize(sets.length);
143     for (Set<?> set : sets) {
144       Set<Optional<?>> optionalSet =
145           Sets.newLinkedHashSet(Iterables.transform(set, NULLABLE_TO_OPTIONAL));
146       optionalSets.add(optionalSet);
147     }
148     Set<List<Optional<?>>> cartesianProduct = Sets.cartesianProduct(optionalSets);
149     return Iterables.transform(
150         cartesianProduct,
151         new Function<List<Optional<?>>, List<Object>>() {
152           @Override
153           public List<Object> apply(List<Optional<?>> objs) {
154             return Lists.transform(objs, OPTIONAL_TO_NULLABLE);
155           }
156         });
157   }
158 
159   private CacheBuilder<Object, Object> createCacheBuilder(
160       @Nullable Integer concurrencyLevel,
161       @Nullable Integer initialCapacity,
162       @Nullable Integer maximumSize,
163       @Nullable DurationSpec expireAfterWrite,
164       @Nullable DurationSpec expireAfterAccess,
165       @Nullable DurationSpec refresh,
166       @Nullable Strength keyStrength,
167       @Nullable Strength valueStrength) {
168 
169     CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder();
170     if (concurrencyLevel != null) {
171       builder.concurrencyLevel(concurrencyLevel);
172     }
173     if (initialCapacity != null) {
174       builder.initialCapacity(initialCapacity);
175     }
176     if (maximumSize != null) {
177       builder.maximumSize(maximumSize);
178     }
179     if (expireAfterWrite != null) {
180       builder.expireAfterWrite(expireAfterWrite.duration, expireAfterWrite.unit);
181     }
182     if (expireAfterAccess != null) {
183       builder.expireAfterAccess(expireAfterAccess.duration, expireAfterAccess.unit);
184     }
185     if (refresh != null) {
186       builder.refreshAfterWrite(refresh.duration, refresh.unit);
187     }
188     if (keyStrength != null) {
189       builder.setKeyStrength(keyStrength);
190     }
191     if (valueStrength != null) {
192       builder.setValueStrength(valueStrength);
193     }
194     return builder;
195   }
196 
197   static class DurationSpec {
198     private final long duration;
199     private final TimeUnit unit;
200 
201     private DurationSpec(long duration, TimeUnit unit) {
202       this.duration = duration;
203       this.unit = unit;
204     }
205 
206     public static DurationSpec of(long duration, TimeUnit unit) {
207       return new DurationSpec(duration, unit);
208     }
209 
210     @Override
211     public int hashCode() {
212       return Objects.hashCode(duration, unit);
213     }
214 
215     @Override
216     public boolean equals(@Nullable Object o) {
217       if (o instanceof DurationSpec) {
218         DurationSpec that = (DurationSpec) o;
219         return unit.toNanos(duration) == that.unit.toNanos(that.duration);
220       }
221       return false;
222     }
223 
224     @Override
225     public String toString() {
226       return MoreObjects.toStringHelper(this)
227           .add("duration", duration)
228           .add("unit", unit)
229           .toString();
230     }
231   }
232 }
233