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 51 CacheBuilderFactory withConcurrencyLevels(Set<Integer> concurrencyLevels) { 52 this.concurrencyLevels = Sets.newLinkedHashSet(concurrencyLevels); 53 return this; 54 } 55 56 CacheBuilderFactory withInitialCapacities(Set<Integer> initialCapacities) { 57 this.initialCapacities = Sets.newLinkedHashSet(initialCapacities); 58 return this; 59 } 60 61 CacheBuilderFactory withMaximumSizes(Set<Integer> maximumSizes) { 62 this.maximumSizes = Sets.newLinkedHashSet(maximumSizes); 63 return this; 64 } 65 66 CacheBuilderFactory withExpireAfterWrites(Set<DurationSpec> durations) { 67 this.expireAfterWrites = Sets.newLinkedHashSet(durations); 68 return this; 69 } 70 71 CacheBuilderFactory withExpireAfterAccesses(Set<DurationSpec> durations) { 72 this.expireAfterAccesses = Sets.newLinkedHashSet(durations); 73 return this; 74 } 75 76 CacheBuilderFactory withRefreshes(Set<DurationSpec> durations) { 77 this.refreshes = Sets.newLinkedHashSet(durations); 78 return this; 79 } 80 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 87 CacheBuilderFactory withValueStrengths(Set<Strength> valueStrengths) { 88 this.valueStrengths = Sets.newLinkedHashSet(valueStrengths); 89 return this; 90 } 91 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