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.compatqual.NullableDecl; 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 @SuppressWarnings("unchecked") 92 Iterable<List<Object>> combinations = 93 buildCartesianProduct( 94 concurrencyLevels, 95 initialCapacities, 96 maximumSizes, 97 expireAfterWrites, 98 expireAfterAccesses, 99 refreshes, 100 keyStrengths, 101 valueStrengths); 102 return Iterables.transform( 103 combinations, 104 new Function<List<Object>, CacheBuilder<Object, Object>>() { 105 @Override 106 public CacheBuilder<Object, Object> apply(List<Object> combination) { 107 return createCacheBuilder( 108 (Integer) combination.get(0), 109 (Integer) combination.get(1), 110 (Integer) combination.get(2), 111 (DurationSpec) combination.get(3), 112 (DurationSpec) combination.get(4), 113 (DurationSpec) combination.get(5), 114 (Strength) combination.get(6), 115 (Strength) combination.get(7)); 116 } 117 }); 118 } 119 120 private static final Function<Object, Optional<?>> NULLABLE_TO_OPTIONAL = 121 new Function<Object, Optional<?>>() { 122 @Override 123 public Optional<?> apply(@NullableDecl Object obj) { 124 return Optional.fromNullable(obj); 125 } 126 }; 127 128 private static final Function<Optional<?>, Object> OPTIONAL_TO_NULLABLE = 129 new Function<Optional<?>, Object>() { 130 @Override 131 public Object apply(Optional<?> optional) { 132 return optional.orNull(); 133 } 134 }; 135 136 /** 137 * Sets.cartesianProduct doesn't allow sets that contain null, but we want null to mean "don't 138 * call the associated CacheBuilder method" - that is, get the default CacheBuilder behavior. This 139 * method wraps the elements in the input sets (which may contain null) as Optionals, calls 140 * Sets.cartesianProduct with those, then transforms the result to unwrap the Optionals. 141 */ 142 private Iterable<List<Object>> buildCartesianProduct(Set<?>... sets) { 143 List<Set<Optional<?>>> optionalSets = Lists.newArrayListWithExpectedSize(sets.length); 144 for (Set<?> set : sets) { 145 Set<Optional<?>> optionalSet = 146 Sets.newLinkedHashSet(Iterables.transform(set, NULLABLE_TO_OPTIONAL)); 147 optionalSets.add(optionalSet); 148 } 149 Set<List<Optional<?>>> cartesianProduct = Sets.cartesianProduct(optionalSets); 150 return Iterables.transform( 151 cartesianProduct, 152 new Function<List<Optional<?>>, List<Object>>() { 153 @Override 154 public List<Object> apply(List<Optional<?>> objs) { 155 return Lists.transform(objs, OPTIONAL_TO_NULLABLE); 156 } 157 }); 158 } 159 160 private CacheBuilder<Object, Object> createCacheBuilder( 161 Integer concurrencyLevel, 162 Integer initialCapacity, 163 Integer maximumSize, 164 DurationSpec expireAfterWrite, 165 DurationSpec expireAfterAccess, 166 DurationSpec refresh, 167 Strength keyStrength, 168 Strength valueStrength) { 169 170 CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder(); 171 if (concurrencyLevel != null) { 172 builder.concurrencyLevel(concurrencyLevel); 173 } 174 if (initialCapacity != null) { 175 builder.initialCapacity(initialCapacity); 176 } 177 if (maximumSize != null) { 178 builder.maximumSize(maximumSize); 179 } 180 if (expireAfterWrite != null) { 181 builder.expireAfterWrite(expireAfterWrite.duration, expireAfterWrite.unit); 182 } 183 if (expireAfterAccess != null) { 184 builder.expireAfterAccess(expireAfterAccess.duration, expireAfterAccess.unit); 185 } 186 if (refresh != null) { 187 builder.refreshAfterWrite(refresh.duration, refresh.unit); 188 } 189 if (keyStrength != null) { 190 builder.setKeyStrength(keyStrength); 191 } 192 if (valueStrength != null) { 193 builder.setValueStrength(valueStrength); 194 } 195 return builder; 196 } 197 198 static class DurationSpec { 199 private final long duration; 200 private final TimeUnit unit; 201 202 private DurationSpec(long duration, TimeUnit unit) { 203 this.duration = duration; 204 this.unit = unit; 205 } 206 207 public static DurationSpec of(long duration, TimeUnit unit) { 208 return new DurationSpec(duration, unit); 209 } 210 211 @Override 212 public int hashCode() { 213 return Objects.hashCode(duration, unit); 214 } 215 216 @Override 217 public boolean equals(Object o) { 218 if (o instanceof DurationSpec) { 219 DurationSpec that = (DurationSpec) o; 220 return unit.toNanos(duration) == that.unit.toNanos(that.duration); 221 } 222 return false; 223 } 224 225 @Override 226 public String toString() { 227 return MoreObjects.toStringHelper(this) 228 .add("duration", duration) 229 .add("unit", unit) 230 .toString(); 231 } 232 } 233 } 234