• 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 
27 import java.util.List;
28 import java.util.Set;
29 import java.util.concurrent.TimeUnit;
30 
31 import javax.annotation.Nullable;
32 
33 /**
34  * Helper class for creating {@link CacheBuilder} instances with all combinations of several sets of
35  * parameters.
36  *
37  * @author mike nonemacher
38  */
39 class CacheBuilderFactory {
40   // Default values contain only 'null', which means don't call the CacheBuilder method (just give
41   // the CacheBuilder default).
42   private Set<Integer> concurrencyLevels = Sets.newHashSet((Integer) null);
43   private Set<Integer> initialCapacities = Sets.newHashSet((Integer) null);
44   private Set<Integer> maximumSizes = Sets.newHashSet((Integer) null);
45   private Set<DurationSpec> expireAfterWrites = Sets.newHashSet((DurationSpec) null);
46   private Set<DurationSpec> expireAfterAccesses = Sets.newHashSet((DurationSpec) null);
47   private Set<DurationSpec> refreshes = Sets.newHashSet((DurationSpec) null);
48   private Set<Strength> keyStrengths = Sets.newHashSet((Strength) null);
49   private Set<Strength> valueStrengths = Sets.newHashSet((Strength) null);
50 
withConcurrencyLevels(Set<Integer> concurrencyLevels)51   CacheBuilderFactory withConcurrencyLevels(Set<Integer> concurrencyLevels) {
52     this.concurrencyLevels = Sets.newLinkedHashSet(concurrencyLevels);
53     return this;
54   }
55 
withInitialCapacities(Set<Integer> initialCapacities)56   CacheBuilderFactory withInitialCapacities(Set<Integer> initialCapacities) {
57     this.initialCapacities = Sets.newLinkedHashSet(initialCapacities);
58     return this;
59   }
60 
withMaximumSizes(Set<Integer> maximumSizes)61   CacheBuilderFactory withMaximumSizes(Set<Integer> maximumSizes) {
62     this.maximumSizes = Sets.newLinkedHashSet(maximumSizes);
63     return this;
64   }
65 
withExpireAfterWrites(Set<DurationSpec> durations)66   CacheBuilderFactory withExpireAfterWrites(Set<DurationSpec> durations) {
67     this.expireAfterWrites = Sets.newLinkedHashSet(durations);
68     return this;
69   }
70 
withExpireAfterAccesses(Set<DurationSpec> durations)71   CacheBuilderFactory withExpireAfterAccesses(Set<DurationSpec> durations) {
72     this.expireAfterAccesses = Sets.newLinkedHashSet(durations);
73     return this;
74   }
75 
withRefreshes(Set<DurationSpec> durations)76   CacheBuilderFactory withRefreshes(Set<DurationSpec> durations) {
77     this.refreshes = Sets.newLinkedHashSet(durations);
78     return this;
79   }
80 
withKeyStrengths(Set<Strength> keyStrengths)81   CacheBuilderFactory withKeyStrengths(Set<Strength> keyStrengths) {
82     this.keyStrengths = Sets.newLinkedHashSet(keyStrengths);
83     Preconditions.checkArgument(!this.keyStrengths.contains(Strength.SOFT));
84     return this;
85   }
86 
withValueStrengths(Set<Strength> valueStrengths)87   CacheBuilderFactory withValueStrengths(Set<Strength> valueStrengths) {
88     this.valueStrengths = Sets.newLinkedHashSet(valueStrengths);
89     return this;
90   }
91 
buildAllPermutations()92   Iterable<CacheBuilder<Object, Object>> buildAllPermutations() {
93     @SuppressWarnings("unchecked")
94     Iterable<List<Object>> combinations = buildCartesianProduct(concurrencyLevels,
95         initialCapacities, maximumSizes, expireAfterWrites, expireAfterAccesses, refreshes,
96         keyStrengths, valueStrengths);
97     return Iterables.transform(combinations,
98         new Function<List<Object>, CacheBuilder<Object, Object>>() {
99           @Override public CacheBuilder<Object, Object> apply(List<Object> combination) {
100             return createCacheBuilder(
101                 (Integer) combination.get(0),
102                 (Integer) combination.get(1),
103                 (Integer) combination.get(2),
104                 (DurationSpec) combination.get(3),
105                 (DurationSpec) combination.get(4),
106                 (DurationSpec) combination.get(5),
107                 (Strength) combination.get(6),
108                 (Strength) combination.get(7));
109           }
110         });
111   }
112 
113   private static final Function<Object, Optional<?>> NULLABLE_TO_OPTIONAL =
114       new Function<Object, Optional<?>>() {
115         @Override public Optional<?> apply(@Nullable Object obj) {
116           return Optional.fromNullable(obj);
117         }
118       };
119 
120   private static final Function<Optional<?>, Object> OPTIONAL_TO_NULLABLE =
121       new Function<Optional<?>, Object>() {
122         @Override public Object apply(Optional<?> optional) {
123           return optional.orNull();
124         }
125       };
126 
127   /**
128    * Sets.cartesianProduct doesn't allow sets that contain null, but we want null to mean
129    * "don't call the associated CacheBuilder method" - that is, get the default CacheBuilder
130    * behavior. This method wraps the elements in the input sets (which may contain null) as
131    * Optionals, calls Sets.cartesianProduct with those, then transforms the result to unwrap
132    * the Optionals.
133    */
134   private Iterable<List<Object>> buildCartesianProduct(Set<?>... sets) {
135     List<Set<Optional<?>>> optionalSets = Lists.newArrayListWithExpectedSize(sets.length);
136     for (Set<?> set : sets) {
137       Set<Optional<?>> optionalSet =
138           Sets.newLinkedHashSet(Iterables.transform(set, NULLABLE_TO_OPTIONAL));
139       optionalSets.add(optionalSet);
140     }
141     Set<List<Optional<?>>> cartesianProduct = Sets.cartesianProduct(optionalSets);
142     return Iterables.transform(cartesianProduct,
143         new Function<List<Optional<?>>, List<Object>>() {
144           @Override public List<Object> apply(List<Optional<?>> objs) {
145             return Lists.transform(objs, OPTIONAL_TO_NULLABLE);
146           }
147         });
148   }
149 
150   private CacheBuilder<Object, Object> createCacheBuilder(
151       Integer concurrencyLevel, Integer initialCapacity, Integer maximumSize,
152       DurationSpec expireAfterWrite, DurationSpec expireAfterAccess, DurationSpec refresh,
153       Strength keyStrength, Strength valueStrength) {
154 
155     CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder();
156     if (concurrencyLevel != null) {
157       builder.concurrencyLevel(concurrencyLevel);
158     }
159     if (initialCapacity != null) {
160       builder.initialCapacity(initialCapacity);
161     }
162     if (maximumSize != null) {
163       builder.maximumSize(maximumSize);
164     }
165     if (expireAfterWrite != null) {
166       builder.expireAfterWrite(expireAfterWrite.duration, expireAfterWrite.unit);
167     }
168     if (expireAfterAccess != null) {
169       builder.expireAfterAccess(expireAfterAccess.duration, expireAfterAccess.unit);
170     }
171     if (refresh != null) {
172       builder.refreshAfterWrite(refresh.duration, refresh.unit);
173     }
174     if (keyStrength != null) {
175       builder.setKeyStrength(keyStrength);
176     }
177     if (valueStrength != null) {
178       builder.setValueStrength(valueStrength);
179     }
180     return builder;
181   }
182 
183   static class DurationSpec {
184     private final long duration;
185     private final TimeUnit unit;
186 
187     private DurationSpec(long duration, TimeUnit unit) {
188       this.duration = duration;
189       this.unit = unit;
190     }
191 
192     public static DurationSpec of(long duration, TimeUnit unit) {
193       return new DurationSpec(duration, unit);
194     }
195 
196     @Override
197     public int hashCode() {
198       return Objects.hashCode(duration, unit);
199     }
200 
201     @Override
202     public boolean equals(Object o) {
203       if (o instanceof DurationSpec) {
204         DurationSpec that = (DurationSpec) o;
205         return unit.toNanos(duration) == that.unit.toNanos(that.duration);
206       }
207       return false;
208     }
209 
210     @Override
211     public String toString() {
212       return MoreObjects.toStringHelper(this)
213           .add("duration", duration)
214           .add("unit", unit)
215           .toString();
216     }
217   }
218 }
219