Home | History | Annotate | Download | only in cache
      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