17dd252788645e940eada959bdde927426e2531c9Paul Duffin/*
27dd252788645e940eada959bdde927426e2531c9Paul Duffin * Copyright (C) 2011 The Guava Authors
37dd252788645e940eada959bdde927426e2531c9Paul Duffin *
47dd252788645e940eada959bdde927426e2531c9Paul Duffin * Licensed under the Apache License, Version 2.0 (the "License");
57dd252788645e940eada959bdde927426e2531c9Paul Duffin * you may not use this file except in compliance with the License.
67dd252788645e940eada959bdde927426e2531c9Paul Duffin * You may obtain a copy of the License at
77dd252788645e940eada959bdde927426e2531c9Paul Duffin *
87dd252788645e940eada959bdde927426e2531c9Paul Duffin * http://www.apache.org/licenses/LICENSE-2.0
97dd252788645e940eada959bdde927426e2531c9Paul Duffin *
107dd252788645e940eada959bdde927426e2531c9Paul Duffin * Unless required by applicable law or agreed to in writing, software
117dd252788645e940eada959bdde927426e2531c9Paul Duffin * distributed under the License is distributed on an "AS IS" BASIS,
127dd252788645e940eada959bdde927426e2531c9Paul Duffin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137dd252788645e940eada959bdde927426e2531c9Paul Duffin * See the License for the specific language governing permissions and
147dd252788645e940eada959bdde927426e2531c9Paul Duffin * limitations under the License.
157dd252788645e940eada959bdde927426e2531c9Paul Duffin */
167dd252788645e940eada959bdde927426e2531c9Paul Duffin
177dd252788645e940eada959bdde927426e2531c9Paul Duffinpackage com.google.common.cache;
187dd252788645e940eada959bdde927426e2531c9Paul Duffin
197dd252788645e940eada959bdde927426e2531c9Paul Duffinimport static com.google.common.base.Preconditions.checkArgument;
207dd252788645e940eada959bdde927426e2531c9Paul Duffin
217dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.annotations.Beta;
227dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.annotations.VisibleForTesting;
233ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffinimport com.google.common.base.MoreObjects;
247dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.base.Objects;
257dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.base.Splitter;
267dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.cache.LocalCache.Strength;
277dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.collect.ImmutableList;
287dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.collect.ImmutableMap;
297dd252788645e940eada959bdde927426e2531c9Paul Duffin
307dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.List;
317dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.concurrent.TimeUnit;
327dd252788645e940eada959bdde927426e2531c9Paul Duffin
337dd252788645e940eada959bdde927426e2531c9Paul Duffinimport javax.annotation.Nullable;
347dd252788645e940eada959bdde927426e2531c9Paul Duffin
357dd252788645e940eada959bdde927426e2531c9Paul Duffin/**
367dd252788645e940eada959bdde927426e2531c9Paul Duffin * A specification of a {@link CacheBuilder} configuration.
377dd252788645e940eada959bdde927426e2531c9Paul Duffin *
387dd252788645e940eada959bdde927426e2531c9Paul Duffin * <p>{@code CacheBuilderSpec} supports parsing configuration off of a string, which
397dd252788645e940eada959bdde927426e2531c9Paul Duffin * makes it especially useful for command-line configuration of a {@code CacheBuilder}.
407dd252788645e940eada959bdde927426e2531c9Paul Duffin *
417dd252788645e940eada959bdde927426e2531c9Paul Duffin * <p>The string syntax is a series of comma-separated keys or key-value pairs,
427dd252788645e940eada959bdde927426e2531c9Paul Duffin * each corresponding to a {@code CacheBuilder} method.
437dd252788645e940eada959bdde927426e2531c9Paul Duffin * <ul>
447dd252788645e940eada959bdde927426e2531c9Paul Duffin * <li>{@code concurrencyLevel=[integer]}: sets {@link CacheBuilder#concurrencyLevel}.
457dd252788645e940eada959bdde927426e2531c9Paul Duffin * <li>{@code initialCapacity=[integer]}: sets {@link CacheBuilder#initialCapacity}.
467dd252788645e940eada959bdde927426e2531c9Paul Duffin * <li>{@code maximumSize=[long]}: sets {@link CacheBuilder#maximumSize}.
477dd252788645e940eada959bdde927426e2531c9Paul Duffin * <li>{@code maximumWeight=[long]}: sets {@link CacheBuilder#maximumWeight}.
487dd252788645e940eada959bdde927426e2531c9Paul Duffin * <li>{@code expireAfterAccess=[duration]}: sets {@link CacheBuilder#expireAfterAccess}.
497dd252788645e940eada959bdde927426e2531c9Paul Duffin * <li>{@code expireAfterWrite=[duration]}: sets {@link CacheBuilder#expireAfterWrite}.
507dd252788645e940eada959bdde927426e2531c9Paul Duffin * <li>{@code refreshAfterWrite=[duration]}: sets {@link CacheBuilder#refreshAfterWrite}.
517dd252788645e940eada959bdde927426e2531c9Paul Duffin * <li>{@code weakKeys}: sets {@link CacheBuilder#weakKeys}.
527dd252788645e940eada959bdde927426e2531c9Paul Duffin * <li>{@code softValues}: sets {@link CacheBuilder#softValues}.
537dd252788645e940eada959bdde927426e2531c9Paul Duffin * <li>{@code weakValues}: sets {@link CacheBuilder#weakValues}.
540888a09821a98ac0680fad765217302858e70fa4Paul Duffin * <li>{@code recordStats}: sets {@link CacheBuilder#recordStats}.
557dd252788645e940eada959bdde927426e2531c9Paul Duffin * </ul>
567dd252788645e940eada959bdde927426e2531c9Paul Duffin *
570888a09821a98ac0680fad765217302858e70fa4Paul Duffin * <p>The set of supported keys will grow as {@code CacheBuilder} evolves, but existing keys
587dd252788645e940eada959bdde927426e2531c9Paul Duffin * will never be removed.
597dd252788645e940eada959bdde927426e2531c9Paul Duffin *
607dd252788645e940eada959bdde927426e2531c9Paul Duffin * <p>Durations are represented by an integer, followed by one of "d", "h", "m",
617dd252788645e940eada959bdde927426e2531c9Paul Duffin * or "s", representing days, hours, minutes, or seconds respectively.  (There
627dd252788645e940eada959bdde927426e2531c9Paul Duffin * is currently no syntax to request expiration in milliseconds, microseconds,
637dd252788645e940eada959bdde927426e2531c9Paul Duffin * or nanoseconds.)
647dd252788645e940eada959bdde927426e2531c9Paul Duffin *
657dd252788645e940eada959bdde927426e2531c9Paul Duffin * <p>Whitespace before and after commas and equal signs is ignored.  Keys may
667dd252788645e940eada959bdde927426e2531c9Paul Duffin * not be repeated;  it is also illegal to use the following pairs of keys in
677dd252788645e940eada959bdde927426e2531c9Paul Duffin * a single value:
687dd252788645e940eada959bdde927426e2531c9Paul Duffin * <ul>
697dd252788645e940eada959bdde927426e2531c9Paul Duffin * <li>{@code maximumSize} and {@code maximumWeight}
707dd252788645e940eada959bdde927426e2531c9Paul Duffin * <li>{@code softValues} and {@code weakValues}
717dd252788645e940eada959bdde927426e2531c9Paul Duffin * </ul>
727dd252788645e940eada959bdde927426e2531c9Paul Duffin *
737dd252788645e940eada959bdde927426e2531c9Paul Duffin * <p>{@code CacheBuilderSpec} does not support configuring {@code CacheBuilder} methods
747dd252788645e940eada959bdde927426e2531c9Paul Duffin * with non-value parameters.  These must be configured in code.
757dd252788645e940eada959bdde927426e2531c9Paul Duffin *
767dd252788645e940eada959bdde927426e2531c9Paul Duffin * <p>A new {@code CacheBuilder} can be instantiated from a {@code CacheBuilderSpec} using
777dd252788645e940eada959bdde927426e2531c9Paul Duffin * {@link CacheBuilder#from(CacheBuilderSpec)} or {@link CacheBuilder#from(String)}.
787dd252788645e940eada959bdde927426e2531c9Paul Duffin *
797dd252788645e940eada959bdde927426e2531c9Paul Duffin * @author Adam Winer
807dd252788645e940eada959bdde927426e2531c9Paul Duffin * @since 12.0
817dd252788645e940eada959bdde927426e2531c9Paul Duffin */
827dd252788645e940eada959bdde927426e2531c9Paul Duffin@Beta
837dd252788645e940eada959bdde927426e2531c9Paul Duffinpublic final class CacheBuilderSpec {
847dd252788645e940eada959bdde927426e2531c9Paul Duffin  /** Parses a single value. */
857dd252788645e940eada959bdde927426e2531c9Paul Duffin  private interface ValueParser {
867dd252788645e940eada959bdde927426e2531c9Paul Duffin    void parse(CacheBuilderSpec spec, String key, @Nullable String value);
877dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
887dd252788645e940eada959bdde927426e2531c9Paul Duffin
897dd252788645e940eada959bdde927426e2531c9Paul Duffin  /** Splits each key-value pair. */
907dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static final Splitter KEYS_SPLITTER = Splitter.on(',').trimResults();
917dd252788645e940eada959bdde927426e2531c9Paul Duffin
927dd252788645e940eada959bdde927426e2531c9Paul Duffin  /** Splits the key from the value. */
937dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static final Splitter KEY_VALUE_SPLITTER = Splitter.on('=').trimResults();
947dd252788645e940eada959bdde927426e2531c9Paul Duffin
957dd252788645e940eada959bdde927426e2531c9Paul Duffin  /** Map of names to ValueParser. */
960888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private static final ImmutableMap<String, ValueParser> VALUE_PARSERS =
970888a09821a98ac0680fad765217302858e70fa4Paul Duffin      ImmutableMap.<String, ValueParser>builder()
980888a09821a98ac0680fad765217302858e70fa4Paul Duffin          .put("initialCapacity", new InitialCapacityParser())
990888a09821a98ac0680fad765217302858e70fa4Paul Duffin          .put("maximumSize", new MaximumSizeParser())
1000888a09821a98ac0680fad765217302858e70fa4Paul Duffin          .put("maximumWeight", new MaximumWeightParser())
1010888a09821a98ac0680fad765217302858e70fa4Paul Duffin          .put("concurrencyLevel", new ConcurrencyLevelParser())
1020888a09821a98ac0680fad765217302858e70fa4Paul Duffin          .put("weakKeys", new KeyStrengthParser(Strength.WEAK))
1030888a09821a98ac0680fad765217302858e70fa4Paul Duffin          .put("softValues", new ValueStrengthParser(Strength.SOFT))
1040888a09821a98ac0680fad765217302858e70fa4Paul Duffin          .put("weakValues", new ValueStrengthParser(Strength.WEAK))
1050888a09821a98ac0680fad765217302858e70fa4Paul Duffin          .put("recordStats", new RecordStatsParser())
1060888a09821a98ac0680fad765217302858e70fa4Paul Duffin          .put("expireAfterAccess", new AccessDurationParser())
1070888a09821a98ac0680fad765217302858e70fa4Paul Duffin          .put("expireAfterWrite", new WriteDurationParser())
1080888a09821a98ac0680fad765217302858e70fa4Paul Duffin          .put("refreshAfterWrite", new RefreshDurationParser())
1090888a09821a98ac0680fad765217302858e70fa4Paul Duffin          .put("refreshInterval", new RefreshDurationParser())
1100888a09821a98ac0680fad765217302858e70fa4Paul Duffin          .build();
1110888a09821a98ac0680fad765217302858e70fa4Paul Duffin
1120888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @VisibleForTesting Integer initialCapacity;
1130888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @VisibleForTesting Long maximumSize;
1140888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @VisibleForTesting Long maximumWeight;
1150888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @VisibleForTesting Integer concurrencyLevel;
1160888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @VisibleForTesting Strength keyStrength;
1170888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @VisibleForTesting Strength valueStrength;
1180888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @VisibleForTesting Boolean recordStats;
1190888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @VisibleForTesting long writeExpirationDuration;
1200888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @VisibleForTesting TimeUnit writeExpirationTimeUnit;
1210888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @VisibleForTesting long accessExpirationDuration;
1220888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @VisibleForTesting TimeUnit accessExpirationTimeUnit;
1230888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @VisibleForTesting long refreshDuration;
1240888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @VisibleForTesting TimeUnit refreshTimeUnit;
1257dd252788645e940eada959bdde927426e2531c9Paul Duffin  /** Specification;  used for toParseableString(). */
1267dd252788645e940eada959bdde927426e2531c9Paul Duffin  private final String specification;
1277dd252788645e940eada959bdde927426e2531c9Paul Duffin
1287dd252788645e940eada959bdde927426e2531c9Paul Duffin  private CacheBuilderSpec(String specification) {
1297dd252788645e940eada959bdde927426e2531c9Paul Duffin    this.specification = specification;
1307dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
1317dd252788645e940eada959bdde927426e2531c9Paul Duffin
1327dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
1337dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Creates a CacheBuilderSpec from a string.
1347dd252788645e940eada959bdde927426e2531c9Paul Duffin   *
1357dd252788645e940eada959bdde927426e2531c9Paul Duffin   * @param cacheBuilderSpecification the string form
1367dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
1377dd252788645e940eada959bdde927426e2531c9Paul Duffin  public static CacheBuilderSpec parse(String cacheBuilderSpecification) {
1387dd252788645e940eada959bdde927426e2531c9Paul Duffin    CacheBuilderSpec spec = new CacheBuilderSpec(cacheBuilderSpecification);
1393ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin    if (!cacheBuilderSpecification.isEmpty()) {
1407dd252788645e940eada959bdde927426e2531c9Paul Duffin      for (String keyValuePair : KEYS_SPLITTER.split(cacheBuilderSpecification)) {
1417dd252788645e940eada959bdde927426e2531c9Paul Duffin        List<String> keyAndValue = ImmutableList.copyOf(KEY_VALUE_SPLITTER.split(keyValuePair));
1427dd252788645e940eada959bdde927426e2531c9Paul Duffin        checkArgument(!keyAndValue.isEmpty(), "blank key-value pair");
1430888a09821a98ac0680fad765217302858e70fa4Paul Duffin        checkArgument(keyAndValue.size() <= 2,
1440888a09821a98ac0680fad765217302858e70fa4Paul Duffin            "key-value pair %s with more than one equals sign", keyValuePair);
1457dd252788645e940eada959bdde927426e2531c9Paul Duffin
1467dd252788645e940eada959bdde927426e2531c9Paul Duffin        // Find the ValueParser for the current key.
1477dd252788645e940eada959bdde927426e2531c9Paul Duffin        String key = keyAndValue.get(0);
1487dd252788645e940eada959bdde927426e2531c9Paul Duffin        ValueParser valueParser = VALUE_PARSERS.get(key);
1497dd252788645e940eada959bdde927426e2531c9Paul Duffin        checkArgument(valueParser != null, "unknown key %s", key);
1507dd252788645e940eada959bdde927426e2531c9Paul Duffin
1517dd252788645e940eada959bdde927426e2531c9Paul Duffin        String value = keyAndValue.size() == 1 ? null : keyAndValue.get(1);
1527dd252788645e940eada959bdde927426e2531c9Paul Duffin        valueParser.parse(spec, key, value);
1537dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
1547dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
1557dd252788645e940eada959bdde927426e2531c9Paul Duffin
1567dd252788645e940eada959bdde927426e2531c9Paul Duffin    return spec;
1577dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
1587dd252788645e940eada959bdde927426e2531c9Paul Duffin
1597dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
1607dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Returns a CacheBuilderSpec that will prevent caching.
1617dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
1627dd252788645e940eada959bdde927426e2531c9Paul Duffin  public static CacheBuilderSpec disableCaching() {
1637dd252788645e940eada959bdde927426e2531c9Paul Duffin    // Maximum size of zero is one way to block caching
1647dd252788645e940eada959bdde927426e2531c9Paul Duffin    return CacheBuilderSpec.parse("maximumSize=0");
1657dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
1667dd252788645e940eada959bdde927426e2531c9Paul Duffin
1677dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
1687dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Returns a CacheBuilder configured according to this instance's specification.
1697dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
1707dd252788645e940eada959bdde927426e2531c9Paul Duffin  CacheBuilder<Object, Object> toCacheBuilder() {
1717dd252788645e940eada959bdde927426e2531c9Paul Duffin    CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder();
1727dd252788645e940eada959bdde927426e2531c9Paul Duffin    if (initialCapacity != null) {
1737dd252788645e940eada959bdde927426e2531c9Paul Duffin      builder.initialCapacity(initialCapacity);
1747dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
1757dd252788645e940eada959bdde927426e2531c9Paul Duffin    if (maximumSize != null) {
1767dd252788645e940eada959bdde927426e2531c9Paul Duffin      builder.maximumSize(maximumSize);
1777dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
1787dd252788645e940eada959bdde927426e2531c9Paul Duffin    if (maximumWeight != null) {
1797dd252788645e940eada959bdde927426e2531c9Paul Duffin      builder.maximumWeight(maximumWeight);
1807dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
1817dd252788645e940eada959bdde927426e2531c9Paul Duffin    if (concurrencyLevel != null) {
1827dd252788645e940eada959bdde927426e2531c9Paul Duffin      builder.concurrencyLevel(concurrencyLevel);
1837dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
1847dd252788645e940eada959bdde927426e2531c9Paul Duffin    if (keyStrength != null) {
1857dd252788645e940eada959bdde927426e2531c9Paul Duffin      switch (keyStrength) {
1867dd252788645e940eada959bdde927426e2531c9Paul Duffin        case WEAK:
1877dd252788645e940eada959bdde927426e2531c9Paul Duffin          builder.weakKeys();
1887dd252788645e940eada959bdde927426e2531c9Paul Duffin          break;
1897dd252788645e940eada959bdde927426e2531c9Paul Duffin        default:
1907dd252788645e940eada959bdde927426e2531c9Paul Duffin          throw new AssertionError();
1917dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
1927dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
1937dd252788645e940eada959bdde927426e2531c9Paul Duffin    if (valueStrength != null) {
1947dd252788645e940eada959bdde927426e2531c9Paul Duffin      switch (valueStrength) {
1957dd252788645e940eada959bdde927426e2531c9Paul Duffin        case SOFT:
1967dd252788645e940eada959bdde927426e2531c9Paul Duffin          builder.softValues();
1977dd252788645e940eada959bdde927426e2531c9Paul Duffin          break;
1987dd252788645e940eada959bdde927426e2531c9Paul Duffin        case WEAK:
1997dd252788645e940eada959bdde927426e2531c9Paul Duffin          builder.weakValues();
2007dd252788645e940eada959bdde927426e2531c9Paul Duffin          break;
2017dd252788645e940eada959bdde927426e2531c9Paul Duffin        default:
2027dd252788645e940eada959bdde927426e2531c9Paul Duffin          throw new AssertionError();
2037dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
2047dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2050888a09821a98ac0680fad765217302858e70fa4Paul Duffin    if (recordStats != null && recordStats) {
2060888a09821a98ac0680fad765217302858e70fa4Paul Duffin      builder.recordStats();
2070888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
2087dd252788645e940eada959bdde927426e2531c9Paul Duffin    if (writeExpirationTimeUnit != null) {
2097dd252788645e940eada959bdde927426e2531c9Paul Duffin      builder.expireAfterWrite(writeExpirationDuration, writeExpirationTimeUnit);
2107dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2117dd252788645e940eada959bdde927426e2531c9Paul Duffin    if (accessExpirationTimeUnit != null) {
2127dd252788645e940eada959bdde927426e2531c9Paul Duffin      builder.expireAfterAccess(accessExpirationDuration, accessExpirationTimeUnit);
2137dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2147dd252788645e940eada959bdde927426e2531c9Paul Duffin    if (refreshTimeUnit != null) {
2157dd252788645e940eada959bdde927426e2531c9Paul Duffin      builder.refreshAfterWrite(refreshDuration, refreshTimeUnit);
2167dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2177dd252788645e940eada959bdde927426e2531c9Paul Duffin
2187dd252788645e940eada959bdde927426e2531c9Paul Duffin    return builder;
2197dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2207dd252788645e940eada959bdde927426e2531c9Paul Duffin
2217dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
2227dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Returns a string that can be used to parse an equivalent
2237dd252788645e940eada959bdde927426e2531c9Paul Duffin   * {@code CacheBuilderSpec}.  The order and form of this representation is
2247dd252788645e940eada959bdde927426e2531c9Paul Duffin   * not guaranteed, except that reparsing its output will produce
2257dd252788645e940eada959bdde927426e2531c9Paul Duffin   * a {@code CacheBuilderSpec} equal to this instance.
2267dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
2277dd252788645e940eada959bdde927426e2531c9Paul Duffin  public String toParsableString() {
2287dd252788645e940eada959bdde927426e2531c9Paul Duffin    return specification;
2297dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2307dd252788645e940eada959bdde927426e2531c9Paul Duffin
2317dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
2327dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Returns a string representation for this CacheBuilderSpec instance.
2337dd252788645e940eada959bdde927426e2531c9Paul Duffin   * The form of this representation is not guaranteed.
2347dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
2357dd252788645e940eada959bdde927426e2531c9Paul Duffin  @Override
2367dd252788645e940eada959bdde927426e2531c9Paul Duffin  public String toString() {
2373ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin    return MoreObjects.toStringHelper(this).addValue(toParsableString()).toString();
2387dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2397dd252788645e940eada959bdde927426e2531c9Paul Duffin
2407dd252788645e940eada959bdde927426e2531c9Paul Duffin  @Override
2417dd252788645e940eada959bdde927426e2531c9Paul Duffin  public int hashCode() {
2420888a09821a98ac0680fad765217302858e70fa4Paul Duffin    return Objects.hashCode(
2430888a09821a98ac0680fad765217302858e70fa4Paul Duffin        initialCapacity,
2440888a09821a98ac0680fad765217302858e70fa4Paul Duffin        maximumSize,
2450888a09821a98ac0680fad765217302858e70fa4Paul Duffin        maximumWeight,
2460888a09821a98ac0680fad765217302858e70fa4Paul Duffin        concurrencyLevel,
2470888a09821a98ac0680fad765217302858e70fa4Paul Duffin        keyStrength,
2480888a09821a98ac0680fad765217302858e70fa4Paul Duffin        valueStrength,
2490888a09821a98ac0680fad765217302858e70fa4Paul Duffin        recordStats,
2507dd252788645e940eada959bdde927426e2531c9Paul Duffin        durationInNanos(writeExpirationDuration, writeExpirationTimeUnit),
2517dd252788645e940eada959bdde927426e2531c9Paul Duffin        durationInNanos(accessExpirationDuration, accessExpirationTimeUnit),
2527dd252788645e940eada959bdde927426e2531c9Paul Duffin        durationInNanos(refreshDuration, refreshTimeUnit));
2537dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2547dd252788645e940eada959bdde927426e2531c9Paul Duffin
2557dd252788645e940eada959bdde927426e2531c9Paul Duffin  @Override
2567dd252788645e940eada959bdde927426e2531c9Paul Duffin  public boolean equals(@Nullable Object obj) {
2577dd252788645e940eada959bdde927426e2531c9Paul Duffin    if (this == obj) {
2587dd252788645e940eada959bdde927426e2531c9Paul Duffin      return true;
2597dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2607dd252788645e940eada959bdde927426e2531c9Paul Duffin    if (!(obj instanceof CacheBuilderSpec)) {
2617dd252788645e940eada959bdde927426e2531c9Paul Duffin      return false;
2627dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2637dd252788645e940eada959bdde927426e2531c9Paul Duffin    CacheBuilderSpec that = (CacheBuilderSpec) obj;
2647dd252788645e940eada959bdde927426e2531c9Paul Duffin    return Objects.equal(initialCapacity, that.initialCapacity)
2657dd252788645e940eada959bdde927426e2531c9Paul Duffin        && Objects.equal(maximumSize, that.maximumSize)
2667dd252788645e940eada959bdde927426e2531c9Paul Duffin        && Objects.equal(maximumWeight, that.maximumWeight)
2677dd252788645e940eada959bdde927426e2531c9Paul Duffin        && Objects.equal(concurrencyLevel, that.concurrencyLevel)
2687dd252788645e940eada959bdde927426e2531c9Paul Duffin        && Objects.equal(keyStrength, that.keyStrength)
2697dd252788645e940eada959bdde927426e2531c9Paul Duffin        && Objects.equal(valueStrength, that.valueStrength)
2700888a09821a98ac0680fad765217302858e70fa4Paul Duffin        && Objects.equal(recordStats, that.recordStats)
2717dd252788645e940eada959bdde927426e2531c9Paul Duffin        && Objects.equal(durationInNanos(writeExpirationDuration, writeExpirationTimeUnit),
2727dd252788645e940eada959bdde927426e2531c9Paul Duffin            durationInNanos(that.writeExpirationDuration, that.writeExpirationTimeUnit))
2737dd252788645e940eada959bdde927426e2531c9Paul Duffin        && Objects.equal(durationInNanos(accessExpirationDuration, accessExpirationTimeUnit),
2747dd252788645e940eada959bdde927426e2531c9Paul Duffin            durationInNanos(that.accessExpirationDuration, that.accessExpirationTimeUnit))
2757dd252788645e940eada959bdde927426e2531c9Paul Duffin        && Objects.equal(durationInNanos(refreshDuration, refreshTimeUnit),
2767dd252788645e940eada959bdde927426e2531c9Paul Duffin            durationInNanos(that.refreshDuration, that.refreshTimeUnit));
2777dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2787dd252788645e940eada959bdde927426e2531c9Paul Duffin
2797dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
2807dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Converts an expiration duration/unit pair into a single Long for hashing and equality.
2817dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Uses nanos to match CacheBuilder implementation.
2827dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
2830888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @Nullable private static Long durationInNanos(long duration, @Nullable TimeUnit unit) {
2847dd252788645e940eada959bdde927426e2531c9Paul Duffin    return (unit == null) ? null : unit.toNanos(duration);
2857dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2867dd252788645e940eada959bdde927426e2531c9Paul Duffin
2877dd252788645e940eada959bdde927426e2531c9Paul Duffin  /** Base class for parsing integers. */
2887dd252788645e940eada959bdde927426e2531c9Paul Duffin  abstract static class IntegerParser implements ValueParser {
2897dd252788645e940eada959bdde927426e2531c9Paul Duffin    protected abstract void parseInteger(CacheBuilderSpec spec, int value);
2907dd252788645e940eada959bdde927426e2531c9Paul Duffin
2910888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @Override
2927dd252788645e940eada959bdde927426e2531c9Paul Duffin    public void parse(CacheBuilderSpec spec, String key, String value) {
2933ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin      checkArgument(value != null && !value.isEmpty(), "value of key %s omitted", key);
2947dd252788645e940eada959bdde927426e2531c9Paul Duffin      try {
2957dd252788645e940eada959bdde927426e2531c9Paul Duffin        parseInteger(spec, Integer.parseInt(value));
2967dd252788645e940eada959bdde927426e2531c9Paul Duffin      } catch (NumberFormatException e) {
2970888a09821a98ac0680fad765217302858e70fa4Paul Duffin        throw new IllegalArgumentException(
2980888a09821a98ac0680fad765217302858e70fa4Paul Duffin            String.format("key %s value set to %s, must be integer", key, value), e);
2997dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
3007dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
3017dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
3027dd252788645e940eada959bdde927426e2531c9Paul Duffin
3037dd252788645e940eada959bdde927426e2531c9Paul Duffin  /** Base class for parsing integers. */
3047dd252788645e940eada959bdde927426e2531c9Paul Duffin  abstract static class LongParser implements ValueParser {
3057dd252788645e940eada959bdde927426e2531c9Paul Duffin    protected abstract void parseLong(CacheBuilderSpec spec, long value);
3067dd252788645e940eada959bdde927426e2531c9Paul Duffin
3070888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @Override
3087dd252788645e940eada959bdde927426e2531c9Paul Duffin    public void parse(CacheBuilderSpec spec, String key, String value) {
3093ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin      checkArgument(value != null && !value.isEmpty(), "value of key %s omitted", key);
3107dd252788645e940eada959bdde927426e2531c9Paul Duffin      try {
3117dd252788645e940eada959bdde927426e2531c9Paul Duffin        parseLong(spec, Long.parseLong(value));
3127dd252788645e940eada959bdde927426e2531c9Paul Duffin      } catch (NumberFormatException e) {
3130888a09821a98ac0680fad765217302858e70fa4Paul Duffin        throw new IllegalArgumentException(
3140888a09821a98ac0680fad765217302858e70fa4Paul Duffin            String.format("key %s value set to %s, must be integer", key, value), e);
3157dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
3167dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
3177dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
3187dd252788645e940eada959bdde927426e2531c9Paul Duffin
3197dd252788645e940eada959bdde927426e2531c9Paul Duffin  /** Parse initialCapacity */
3207dd252788645e940eada959bdde927426e2531c9Paul Duffin  static class InitialCapacityParser extends IntegerParser {
3217dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override
3227dd252788645e940eada959bdde927426e2531c9Paul Duffin    protected void parseInteger(CacheBuilderSpec spec, int value) {
3230888a09821a98ac0680fad765217302858e70fa4Paul Duffin      checkArgument(spec.initialCapacity == null,
3240888a09821a98ac0680fad765217302858e70fa4Paul Duffin          "initial capacity was already set to ", spec.initialCapacity);
3257dd252788645e940eada959bdde927426e2531c9Paul Duffin      spec.initialCapacity = value;
3267dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
3277dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
3287dd252788645e940eada959bdde927426e2531c9Paul Duffin
3297dd252788645e940eada959bdde927426e2531c9Paul Duffin  /** Parse maximumSize */
3307dd252788645e940eada959bdde927426e2531c9Paul Duffin  static class MaximumSizeParser extends LongParser {
3317dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override
3327dd252788645e940eada959bdde927426e2531c9Paul Duffin    protected void parseLong(CacheBuilderSpec spec, long value) {
3330888a09821a98ac0680fad765217302858e70fa4Paul Duffin      checkArgument(spec.maximumSize == null,
3340888a09821a98ac0680fad765217302858e70fa4Paul Duffin          "maximum size was already set to ", spec.maximumSize);
3350888a09821a98ac0680fad765217302858e70fa4Paul Duffin      checkArgument(spec.maximumWeight == null,
3360888a09821a98ac0680fad765217302858e70fa4Paul Duffin          "maximum weight was already set to ", spec.maximumWeight);
3377dd252788645e940eada959bdde927426e2531c9Paul Duffin      spec.maximumSize = value;
3387dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
3397dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
3407dd252788645e940eada959bdde927426e2531c9Paul Duffin
3417dd252788645e940eada959bdde927426e2531c9Paul Duffin  /** Parse maximumWeight */
3427dd252788645e940eada959bdde927426e2531c9Paul Duffin  static class MaximumWeightParser extends LongParser {
3437dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override
3447dd252788645e940eada959bdde927426e2531c9Paul Duffin    protected void parseLong(CacheBuilderSpec spec, long value) {
3450888a09821a98ac0680fad765217302858e70fa4Paul Duffin      checkArgument(spec.maximumWeight == null,
3460888a09821a98ac0680fad765217302858e70fa4Paul Duffin          "maximum weight was already set to ", spec.maximumWeight);
3470888a09821a98ac0680fad765217302858e70fa4Paul Duffin      checkArgument(spec.maximumSize == null,
3480888a09821a98ac0680fad765217302858e70fa4Paul Duffin          "maximum size was already set to ", spec.maximumSize);
3497dd252788645e940eada959bdde927426e2531c9Paul Duffin      spec.maximumWeight = value;
3507dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
3517dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
3527dd252788645e940eada959bdde927426e2531c9Paul Duffin
3537dd252788645e940eada959bdde927426e2531c9Paul Duffin  /** Parse concurrencyLevel */
3547dd252788645e940eada959bdde927426e2531c9Paul Duffin  static class ConcurrencyLevelParser extends IntegerParser {
3557dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override
3567dd252788645e940eada959bdde927426e2531c9Paul Duffin    protected void parseInteger(CacheBuilderSpec spec, int value) {
3570888a09821a98ac0680fad765217302858e70fa4Paul Duffin      checkArgument(spec.concurrencyLevel == null,
3580888a09821a98ac0680fad765217302858e70fa4Paul Duffin          "concurrency level was already set to ", spec.concurrencyLevel);
3597dd252788645e940eada959bdde927426e2531c9Paul Duffin      spec.concurrencyLevel = value;
3607dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
3617dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
3627dd252788645e940eada959bdde927426e2531c9Paul Duffin
3637dd252788645e940eada959bdde927426e2531c9Paul Duffin  /** Parse weakKeys */
3647dd252788645e940eada959bdde927426e2531c9Paul Duffin  static class KeyStrengthParser implements ValueParser {
3657dd252788645e940eada959bdde927426e2531c9Paul Duffin    private final Strength strength;
3667dd252788645e940eada959bdde927426e2531c9Paul Duffin
3677dd252788645e940eada959bdde927426e2531c9Paul Duffin    public KeyStrengthParser(Strength strength) {
3687dd252788645e940eada959bdde927426e2531c9Paul Duffin      this.strength = strength;
3697dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
3707dd252788645e940eada959bdde927426e2531c9Paul Duffin
3710888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @Override
3727dd252788645e940eada959bdde927426e2531c9Paul Duffin    public void parse(CacheBuilderSpec spec, String key, @Nullable String value) {
3737dd252788645e940eada959bdde927426e2531c9Paul Duffin      checkArgument(value == null, "key %s does not take values", key);
3747dd252788645e940eada959bdde927426e2531c9Paul Duffin      checkArgument(spec.keyStrength == null, "%s was already set to %s", key, spec.keyStrength);
3757dd252788645e940eada959bdde927426e2531c9Paul Duffin      spec.keyStrength = strength;
3767dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
3777dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
3787dd252788645e940eada959bdde927426e2531c9Paul Duffin
3797dd252788645e940eada959bdde927426e2531c9Paul Duffin  /** Parse weakValues and softValues */
3807dd252788645e940eada959bdde927426e2531c9Paul Duffin  static class ValueStrengthParser implements ValueParser {
3817dd252788645e940eada959bdde927426e2531c9Paul Duffin    private final Strength strength;
3827dd252788645e940eada959bdde927426e2531c9Paul Duffin
3837dd252788645e940eada959bdde927426e2531c9Paul Duffin    public ValueStrengthParser(Strength strength) {
3847dd252788645e940eada959bdde927426e2531c9Paul Duffin      this.strength = strength;
3857dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
3867dd252788645e940eada959bdde927426e2531c9Paul Duffin
3870888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @Override
3887dd252788645e940eada959bdde927426e2531c9Paul Duffin    public void parse(CacheBuilderSpec spec, String key, @Nullable String value) {
3897dd252788645e940eada959bdde927426e2531c9Paul Duffin      checkArgument(value == null, "key %s does not take values", key);
3900888a09821a98ac0680fad765217302858e70fa4Paul Duffin      checkArgument(spec.valueStrength == null,
3910888a09821a98ac0680fad765217302858e70fa4Paul Duffin        "%s was already set to %s", key, spec.valueStrength);
3927dd252788645e940eada959bdde927426e2531c9Paul Duffin
3937dd252788645e940eada959bdde927426e2531c9Paul Duffin      spec.valueStrength = strength;
3947dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
3957dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
3967dd252788645e940eada959bdde927426e2531c9Paul Duffin
3970888a09821a98ac0680fad765217302858e70fa4Paul Duffin  /** Parse recordStats */
3980888a09821a98ac0680fad765217302858e70fa4Paul Duffin  static class RecordStatsParser implements ValueParser {
3990888a09821a98ac0680fad765217302858e70fa4Paul Duffin
4000888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @Override
4010888a09821a98ac0680fad765217302858e70fa4Paul Duffin    public void parse(CacheBuilderSpec spec, String key, @Nullable String value) {
4020888a09821a98ac0680fad765217302858e70fa4Paul Duffin      checkArgument(value == null, "recordStats does not take values");
4030888a09821a98ac0680fad765217302858e70fa4Paul Duffin      checkArgument(spec.recordStats == null, "recordStats already set");
4040888a09821a98ac0680fad765217302858e70fa4Paul Duffin      spec.recordStats = true;
4050888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
4060888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
4070888a09821a98ac0680fad765217302858e70fa4Paul Duffin
4087dd252788645e940eada959bdde927426e2531c9Paul Duffin  /** Base class for parsing times with durations */
4097dd252788645e940eada959bdde927426e2531c9Paul Duffin  abstract static class DurationParser implements ValueParser {
4100888a09821a98ac0680fad765217302858e70fa4Paul Duffin    protected abstract void parseDuration(
4110888a09821a98ac0680fad765217302858e70fa4Paul Duffin        CacheBuilderSpec spec,
4120888a09821a98ac0680fad765217302858e70fa4Paul Duffin        long duration,
4130888a09821a98ac0680fad765217302858e70fa4Paul Duffin        TimeUnit unit);
4147dd252788645e940eada959bdde927426e2531c9Paul Duffin
4150888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @Override
4167dd252788645e940eada959bdde927426e2531c9Paul Duffin    public void parse(CacheBuilderSpec spec, String key, String value) {
4173ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin      checkArgument(value != null && !value.isEmpty(), "value of key %s omitted", key);
4187dd252788645e940eada959bdde927426e2531c9Paul Duffin      try {
4197dd252788645e940eada959bdde927426e2531c9Paul Duffin        char lastChar = value.charAt(value.length() - 1);
4203ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin        TimeUnit timeUnit;
4217dd252788645e940eada959bdde927426e2531c9Paul Duffin        switch (lastChar) {
4227dd252788645e940eada959bdde927426e2531c9Paul Duffin          case 'd':
4233ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin            timeUnit = TimeUnit.DAYS;
4243ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin            break;
4257dd252788645e940eada959bdde927426e2531c9Paul Duffin          case 'h':
4263ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin            timeUnit = TimeUnit.HOURS;
4273ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin            break;
4287dd252788645e940eada959bdde927426e2531c9Paul Duffin          case 'm':
4293ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin            timeUnit = TimeUnit.MINUTES;
4303ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin            break;
4317dd252788645e940eada959bdde927426e2531c9Paul Duffin          case 's':
4323ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin            timeUnit = TimeUnit.SECONDS;
4337dd252788645e940eada959bdde927426e2531c9Paul Duffin            break;
4347dd252788645e940eada959bdde927426e2531c9Paul Duffin          default:
4350888a09821a98ac0680fad765217302858e70fa4Paul Duffin            throw new IllegalArgumentException(
4360888a09821a98ac0680fad765217302858e70fa4Paul Duffin                String.format("key %s invalid format.  was %s, must end with one of [dDhHmMsS]",
4370888a09821a98ac0680fad765217302858e70fa4Paul Duffin                    key, value));
4387dd252788645e940eada959bdde927426e2531c9Paul Duffin        }
4397dd252788645e940eada959bdde927426e2531c9Paul Duffin
4407dd252788645e940eada959bdde927426e2531c9Paul Duffin        long duration = Long.parseLong(value.substring(0, value.length() - 1));
4413ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin        parseDuration(spec, duration, timeUnit);
4427dd252788645e940eada959bdde927426e2531c9Paul Duffin      } catch (NumberFormatException e) {
4430888a09821a98ac0680fad765217302858e70fa4Paul Duffin        throw new IllegalArgumentException(
4440888a09821a98ac0680fad765217302858e70fa4Paul Duffin            String.format("key %s value set to %s, must be integer", key, value));
4457dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
4467dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
4477dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
4487dd252788645e940eada959bdde927426e2531c9Paul Duffin
4497dd252788645e940eada959bdde927426e2531c9Paul Duffin  /** Parse expireAfterAccess */
4507dd252788645e940eada959bdde927426e2531c9Paul Duffin  static class AccessDurationParser extends DurationParser {
4510888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @Override protected void parseDuration(CacheBuilderSpec spec, long duration, TimeUnit unit) {
4527dd252788645e940eada959bdde927426e2531c9Paul Duffin      checkArgument(spec.accessExpirationTimeUnit == null, "expireAfterAccess already set");
4537dd252788645e940eada959bdde927426e2531c9Paul Duffin      spec.accessExpirationDuration = duration;
4547dd252788645e940eada959bdde927426e2531c9Paul Duffin      spec.accessExpirationTimeUnit = unit;
4557dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
4567dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
4577dd252788645e940eada959bdde927426e2531c9Paul Duffin
4587dd252788645e940eada959bdde927426e2531c9Paul Duffin  /** Parse expireAfterWrite */
4597dd252788645e940eada959bdde927426e2531c9Paul Duffin  static class WriteDurationParser extends DurationParser {
4600888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @Override protected void parseDuration(CacheBuilderSpec spec, long duration, TimeUnit unit) {
4617dd252788645e940eada959bdde927426e2531c9Paul Duffin      checkArgument(spec.writeExpirationTimeUnit == null, "expireAfterWrite already set");
4627dd252788645e940eada959bdde927426e2531c9Paul Duffin      spec.writeExpirationDuration = duration;
4637dd252788645e940eada959bdde927426e2531c9Paul Duffin      spec.writeExpirationTimeUnit = unit;
4647dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
4657dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
4667dd252788645e940eada959bdde927426e2531c9Paul Duffin
4677dd252788645e940eada959bdde927426e2531c9Paul Duffin  /** Parse refreshAfterWrite */
4687dd252788645e940eada959bdde927426e2531c9Paul Duffin  static class RefreshDurationParser extends DurationParser {
4690888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @Override protected void parseDuration(CacheBuilderSpec spec, long duration, TimeUnit unit) {
4707dd252788645e940eada959bdde927426e2531c9Paul Duffin      checkArgument(spec.refreshTimeUnit == null, "refreshAfterWrite already set");
4717dd252788645e940eada959bdde927426e2531c9Paul Duffin      spec.refreshDuration = duration;
4727dd252788645e940eada959bdde927426e2531c9Paul Duffin      spec.refreshTimeUnit = unit;
4737dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
4747dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
4757dd252788645e940eada959bdde927426e2531c9Paul Duffin}
476