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