/*
 * Copyright (C) 2012 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.common.collect;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Predicates.compose;
import static com.google.common.base.Predicates.in;
import static com.google.common.base.Predicates.not;

import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.MoreObjects;
import com.google.common.base.Predicate;
import com.google.common.collect.Maps.IteratorBasedAbstractMap;
import java.util.AbstractMap;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.NoSuchElementException;
import java.util.Set;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;

/**
 * An implementation of {@code RangeMap} based on a {@code TreeMap}, supporting all optional
 * operations.
 *
 * <p>Like all {@code RangeMap} implementations, this supports neither null keys nor null values.
 *
 * @author Louis Wasserman
 * @since 14.0
 */
@Beta
@GwtIncompatible // NavigableMap
public final class TreeRangeMap<K extends Comparable, V> implements RangeMap<K, V> {

  private final NavigableMap<Cut<K>, RangeMapEntry<K, V>> entriesByLowerBound;

  public static <K extends Comparable, V> TreeRangeMap<K, V> create() {
    return new TreeRangeMap<>();
  }

  private TreeRangeMap() {
    this.entriesByLowerBound = Maps.newTreeMap();
  }

  private static final class RangeMapEntry<K extends Comparable, V>
      extends AbstractMapEntry<Range<K>, V> {
    private final Range<K> range;
    private final V value;

    RangeMapEntry(Cut<K> lowerBound, Cut<K> upperBound, V value) {
      this(Range.create(lowerBound, upperBound), value);
    }

    RangeMapEntry(Range<K> range, V value) {
      this.range = range;
      this.value = value;
    }

    @Override
    public Range<K> getKey() {
      return range;
    }

    @Override
    public V getValue() {
      return value;
    }

    public boolean contains(K value) {
      return range.contains(value);
    }

    Cut<K> getLowerBound() {
      return range.lowerBound;
    }

    Cut<K> getUpperBound() {
      return range.upperBound;
    }
  }

  @Override
  @NullableDecl
  public V get(K key) {
    Entry<Range<K>, V> entry = getEntry(key);
    return (entry == null) ? null : entry.getValue();
  }

  @Override
  @NullableDecl
  public Entry<Range<K>, V> getEntry(K key) {
    Entry<Cut<K>, RangeMapEntry<K, V>> mapEntry =
        entriesByLowerBound.floorEntry(Cut.belowValue(key));
    if (mapEntry != null && mapEntry.getValue().contains(key)) {
      return mapEntry.getValue();
    } else {
      return null;
    }
  }

  @Override
  public void put(Range<K> range, V value) {
    // don't short-circuit if the range is empty - it may be between two ranges we can coalesce.
    if (!range.isEmpty()) {
      checkNotNull(value);
      remove(range);
      entriesByLowerBound.put(range.lowerBound, new RangeMapEntry<K, V>(range, value));
    }
  }

  @Override
  public void putCoalescing(Range<K> range, V value) {
    if (entriesByLowerBound.isEmpty()) {
      put(range, value);
      return;
    }

    Range<K> coalescedRange = coalescedRange(range, checkNotNull(value));
    put(coalescedRange, value);
  }

  /** Computes the coalesced range for the given range+value - does not mutate the map. */
  private Range<K> coalescedRange(Range<K> range, V value) {
    Range<K> coalescedRange = range;
    Entry<Cut<K>, RangeMapEntry<K, V>> lowerEntry =
        entriesByLowerBound.lowerEntry(range.lowerBound);
    coalescedRange = coalesce(coalescedRange, value, lowerEntry);

    Entry<Cut<K>, RangeMapEntry<K, V>> higherEntry =
        entriesByLowerBound.floorEntry(range.upperBound);
    coalescedRange = coalesce(coalescedRange, value, higherEntry);

    return coalescedRange;
  }

  /** Returns the range that spans the given range and entry, if the entry can be coalesced. */
  private static <K extends Comparable, V> Range<K> coalesce(
      Range<K> range, V value, @NullableDecl Entry<Cut<K>, RangeMapEntry<K, V>> entry) {
    if (entry != null
        && entry.getValue().getKey().isConnected(range)
        && entry.getValue().getValue().equals(value)) {
      return range.span(entry.getValue().getKey());
    }
    return range;
  }

  @Override
  public void putAll(RangeMap<K, V> rangeMap) {
    for (Entry<Range<K>, V> entry : rangeMap.asMapOfRanges().entrySet()) {
      put(entry.getKey(), entry.getValue());
    }
  }

  @Override
  public void clear() {
    entriesByLowerBound.clear();
  }

  @Override
  public Range<K> span() {
    Entry<Cut<K>, RangeMapEntry<K, V>> firstEntry = entriesByLowerBound.firstEntry();
    Entry<Cut<K>, RangeMapEntry<K, V>> lastEntry = entriesByLowerBound.lastEntry();
    if (firstEntry == null) {
      throw new NoSuchElementException();
    }
    return Range.create(
        firstEntry.getValue().getKey().lowerBound, lastEntry.getValue().getKey().upperBound);
  }

  private void putRangeMapEntry(Cut<K> lowerBound, Cut<K> upperBound, V value) {
    entriesByLowerBound.put(lowerBound, new RangeMapEntry<K, V>(lowerBound, upperBound, value));
  }

  @Override
  public void remove(Range<K> rangeToRemove) {
    if (rangeToRemove.isEmpty()) {
      return;
    }

    /*
     * The comments for this method will use [ ] to indicate the bounds of rangeToRemove and ( ) to
     * indicate the bounds of ranges in the range map.
     */
    Entry<Cut<K>, RangeMapEntry<K, V>> mapEntryBelowToTruncate =
        entriesByLowerBound.lowerEntry(rangeToRemove.lowerBound);
    if (mapEntryBelowToTruncate != null) {
      // we know ( [
      RangeMapEntry<K, V> rangeMapEntry = mapEntryBelowToTruncate.getValue();
      if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.lowerBound) > 0) {
        // we know ( [ )
        if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) {
          // we know ( [ ] ), so insert the range ] ) back into the map --
          // it's being split apart
          putRangeMapEntry(
              rangeToRemove.upperBound,
              rangeMapEntry.getUpperBound(),
              mapEntryBelowToTruncate.getValue().getValue());
        }
        // overwrite mapEntryToTruncateBelow with a truncated range
        putRangeMapEntry(
            rangeMapEntry.getLowerBound(),
            rangeToRemove.lowerBound,
            mapEntryBelowToTruncate.getValue().getValue());
      }
    }

    Entry<Cut<K>, RangeMapEntry<K, V>> mapEntryAboveToTruncate =
        entriesByLowerBound.lowerEntry(rangeToRemove.upperBound);
    if (mapEntryAboveToTruncate != null) {
      // we know ( ]
      RangeMapEntry<K, V> rangeMapEntry = mapEntryAboveToTruncate.getValue();
      if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) {
        // we know ( ] ), and since we dealt with truncating below already,
        // we know [ ( ] )
        putRangeMapEntry(
            rangeToRemove.upperBound,
            rangeMapEntry.getUpperBound(),
            mapEntryAboveToTruncate.getValue().getValue());
      }
    }
    entriesByLowerBound.subMap(rangeToRemove.lowerBound, rangeToRemove.upperBound).clear();
  }

  @Override
  public Map<Range<K>, V> asMapOfRanges() {
    return new AsMapOfRanges(entriesByLowerBound.values());
  }

  @Override
  public Map<Range<K>, V> asDescendingMapOfRanges() {
    return new AsMapOfRanges(entriesByLowerBound.descendingMap().values());
  }

  private final class AsMapOfRanges extends IteratorBasedAbstractMap<Range<K>, V> {

    final Iterable<Entry<Range<K>, V>> entryIterable;

    @SuppressWarnings("unchecked") // it's safe to upcast iterables
    AsMapOfRanges(Iterable<RangeMapEntry<K, V>> entryIterable) {
      this.entryIterable = (Iterable) entryIterable;
    }

    @Override
    public boolean containsKey(@NullableDecl Object key) {
      return get(key) != null;
    }

    @Override
    public V get(@NullableDecl Object key) {
      if (key instanceof Range) {
        Range<?> range = (Range<?>) key;
        RangeMapEntry<K, V> rangeMapEntry = entriesByLowerBound.get(range.lowerBound);
        if (rangeMapEntry != null && rangeMapEntry.getKey().equals(range)) {
          return rangeMapEntry.getValue();
        }
      }
      return null;
    }

    @Override
    public int size() {
      return entriesByLowerBound.size();
    }

    @Override
    Iterator<Entry<Range<K>, V>> entryIterator() {
      return entryIterable.iterator();
    }
  }

  @Override
  public RangeMap<K, V> subRangeMap(Range<K> subRange) {
    if (subRange.equals(Range.all())) {
      return this;
    } else {
      return new SubRangeMap(subRange);
    }
  }

  @SuppressWarnings("unchecked")
  private RangeMap<K, V> emptySubRangeMap() {
    return EMPTY_SUB_RANGE_MAP;
  }

  private static final RangeMap EMPTY_SUB_RANGE_MAP =
      new RangeMap() {
        @Override
        @NullableDecl
        public Object get(Comparable key) {
          return null;
        }

        @Override
        @NullableDecl
        public Entry<Range, Object> getEntry(Comparable key) {
          return null;
        }

        @Override
        public Range span() {
          throw new NoSuchElementException();
        }

        @Override
        public void put(Range range, Object value) {
          checkNotNull(range);
          throw new IllegalArgumentException(
              "Cannot insert range " + range + " into an empty subRangeMap");
        }

        @Override
        public void putCoalescing(Range range, Object value) {
          checkNotNull(range);
          throw new IllegalArgumentException(
              "Cannot insert range " + range + " into an empty subRangeMap");
        }

        @Override
        public void putAll(RangeMap rangeMap) {
          if (!rangeMap.asMapOfRanges().isEmpty()) {
            throw new IllegalArgumentException(
                "Cannot putAll(nonEmptyRangeMap) into an empty subRangeMap");
          }
        }

        @Override
        public void clear() {}

        @Override
        public void remove(Range range) {
          checkNotNull(range);
        }

        @Override
        public Map<Range, Object> asMapOfRanges() {
          return Collections.emptyMap();
        }

        @Override
        public Map<Range, Object> asDescendingMapOfRanges() {
          return Collections.emptyMap();
        }

        @Override
        public RangeMap subRangeMap(Range range) {
          checkNotNull(range);
          return this;
        }
      };

  private class SubRangeMap implements RangeMap<K, V> {

    private final Range<K> subRange;

    SubRangeMap(Range<K> subRange) {
      this.subRange = subRange;
    }

    @Override
    @NullableDecl
    public V get(K key) {
      return subRange.contains(key) ? TreeRangeMap.this.get(key) : null;
    }

    @Override
    @NullableDecl
    public Entry<Range<K>, V> getEntry(K key) {
      if (subRange.contains(key)) {
        Entry<Range<K>, V> entry = TreeRangeMap.this.getEntry(key);
        if (entry != null) {
          return Maps.immutableEntry(entry.getKey().intersection(subRange), entry.getValue());
        }
      }
      return null;
    }

    @Override
    public Range<K> span() {
      Cut<K> lowerBound;
      Entry<Cut<K>, RangeMapEntry<K, V>> lowerEntry =
          entriesByLowerBound.floorEntry(subRange.lowerBound);
      if (lowerEntry != null
          && lowerEntry.getValue().getUpperBound().compareTo(subRange.lowerBound) > 0) {
        lowerBound = subRange.lowerBound;
      } else {
        lowerBound = entriesByLowerBound.ceilingKey(subRange.lowerBound);
        if (lowerBound == null || lowerBound.compareTo(subRange.upperBound) >= 0) {
          throw new NoSuchElementException();
        }
      }

      Cut<K> upperBound;
      Entry<Cut<K>, RangeMapEntry<K, V>> upperEntry =
          entriesByLowerBound.lowerEntry(subRange.upperBound);
      if (upperEntry == null) {
        throw new NoSuchElementException();
      } else if (upperEntry.getValue().getUpperBound().compareTo(subRange.upperBound) >= 0) {
        upperBound = subRange.upperBound;
      } else {
        upperBound = upperEntry.getValue().getUpperBound();
      }
      return Range.create(lowerBound, upperBound);
    }

    @Override
    public void put(Range<K> range, V value) {
      checkArgument(
          subRange.encloses(range), "Cannot put range %s into a subRangeMap(%s)", range, subRange);
      TreeRangeMap.this.put(range, value);
    }

    @Override
    public void putCoalescing(Range<K> range, V value) {
      if (entriesByLowerBound.isEmpty() || range.isEmpty() || !subRange.encloses(range)) {
        put(range, value);
        return;
      }

      Range<K> coalescedRange = coalescedRange(range, checkNotNull(value));
      // only coalesce ranges within the subRange
      put(coalescedRange.intersection(subRange), value);
    }

    @Override
    public void putAll(RangeMap<K, V> rangeMap) {
      if (rangeMap.asMapOfRanges().isEmpty()) {
        return;
      }
      Range<K> span = rangeMap.span();
      checkArgument(
          subRange.encloses(span),
          "Cannot putAll rangeMap with span %s into a subRangeMap(%s)",
          span,
          subRange);
      TreeRangeMap.this.putAll(rangeMap);
    }

    @Override
    public void clear() {
      TreeRangeMap.this.remove(subRange);
    }

    @Override
    public void remove(Range<K> range) {
      if (range.isConnected(subRange)) {
        TreeRangeMap.this.remove(range.intersection(subRange));
      }
    }

    @Override
    public RangeMap<K, V> subRangeMap(Range<K> range) {
      if (!range.isConnected(subRange)) {
        return emptySubRangeMap();
      } else {
        return TreeRangeMap.this.subRangeMap(range.intersection(subRange));
      }
    }

    @Override
    public Map<Range<K>, V> asMapOfRanges() {
      return new SubRangeMapAsMap();
    }

    @Override
    public Map<Range<K>, V> asDescendingMapOfRanges() {
      return new SubRangeMapAsMap() {

        @Override
        Iterator<Entry<Range<K>, V>> entryIterator() {
          if (subRange.isEmpty()) {
            return Iterators.emptyIterator();
          }
          final Iterator<RangeMapEntry<K, V>> backingItr =
              entriesByLowerBound
                  .headMap(subRange.upperBound, false)
                  .descendingMap()
                  .values()
                  .iterator();
          return new AbstractIterator<Entry<Range<K>, V>>() {

            @Override
            protected Entry<Range<K>, V> computeNext() {
              if (backingItr.hasNext()) {
                RangeMapEntry<K, V> entry = backingItr.next();
                if (entry.getUpperBound().compareTo(subRange.lowerBound) <= 0) {
                  return endOfData();
                }
                return Maps.immutableEntry(entry.getKey().intersection(subRange), entry.getValue());
              }
              return endOfData();
            }
          };
        }
      };
    }

    @Override
    public boolean equals(@NullableDecl Object o) {
      if (o instanceof RangeMap) {
        RangeMap<?, ?> rangeMap = (RangeMap<?, ?>) o;
        return asMapOfRanges().equals(rangeMap.asMapOfRanges());
      }
      return false;
    }

    @Override
    public int hashCode() {
      return asMapOfRanges().hashCode();
    }

    @Override
    public String toString() {
      return asMapOfRanges().toString();
    }

    class SubRangeMapAsMap extends AbstractMap<Range<K>, V> {

      @Override
      public boolean containsKey(Object key) {
        return get(key) != null;
      }

      @Override
      public V get(Object key) {
        try {
          if (key instanceof Range) {
            @SuppressWarnings("unchecked") // we catch ClassCastExceptions
            Range<K> r = (Range<K>) key;
            if (!subRange.encloses(r) || r.isEmpty()) {
              return null;
            }
            RangeMapEntry<K, V> candidate = null;
            if (r.lowerBound.compareTo(subRange.lowerBound) == 0) {
              // r could be truncated on the left
              Entry<Cut<K>, RangeMapEntry<K, V>> entry =
                  entriesByLowerBound.floorEntry(r.lowerBound);
              if (entry != null) {
                candidate = entry.getValue();
              }
            } else {
              candidate = entriesByLowerBound.get(r.lowerBound);
            }

            if (candidate != null
                && candidate.getKey().isConnected(subRange)
                && candidate.getKey().intersection(subRange).equals(r)) {
              return candidate.getValue();
            }
          }
        } catch (ClassCastException e) {
          return null;
        }
        return null;
      }

      @Override
      public V remove(Object key) {
        V value = get(key);
        if (value != null) {
          @SuppressWarnings("unchecked") // it's definitely in the map, so safe
          Range<K> range = (Range<K>) key;
          TreeRangeMap.this.remove(range);
          return value;
        }
        return null;
      }

      @Override
      public void clear() {
        SubRangeMap.this.clear();
      }

      private boolean removeEntryIf(Predicate<? super Entry<Range<K>, V>> predicate) {
        List<Range<K>> toRemove = Lists.newArrayList();
        for (Entry<Range<K>, V> entry : entrySet()) {
          if (predicate.apply(entry)) {
            toRemove.add(entry.getKey());
          }
        }
        for (Range<K> range : toRemove) {
          TreeRangeMap.this.remove(range);
        }
        return !toRemove.isEmpty();
      }

      @Override
      public Set<Range<K>> keySet() {
        return new Maps.KeySet<Range<K>, V>(SubRangeMapAsMap.this) {
          @Override
          public boolean remove(@NullableDecl Object o) {
            return SubRangeMapAsMap.this.remove(o) != null;
          }

          @Override
          public boolean retainAll(Collection<?> c) {
            return removeEntryIf(compose(not(in(c)), Maps.<Range<K>>keyFunction()));
          }
        };
      }

      @Override
      public Set<Entry<Range<K>, V>> entrySet() {
        return new Maps.EntrySet<Range<K>, V>() {
          @Override
          Map<Range<K>, V> map() {
            return SubRangeMapAsMap.this;
          }

          @Override
          public Iterator<Entry<Range<K>, V>> iterator() {
            return entryIterator();
          }

          @Override
          public boolean retainAll(Collection<?> c) {
            return removeEntryIf(not(in(c)));
          }

          @Override
          public int size() {
            return Iterators.size(iterator());
          }

          @Override
          public boolean isEmpty() {
            return !iterator().hasNext();
          }
        };
      }

      Iterator<Entry<Range<K>, V>> entryIterator() {
        if (subRange.isEmpty()) {
          return Iterators.emptyIterator();
        }
        Cut<K> cutToStart =
            MoreObjects.firstNonNull(
                entriesByLowerBound.floorKey(subRange.lowerBound), subRange.lowerBound);
        final Iterator<RangeMapEntry<K, V>> backingItr =
            entriesByLowerBound.tailMap(cutToStart, true).values().iterator();
        return new AbstractIterator<Entry<Range<K>, V>>() {

          @Override
          protected Entry<Range<K>, V> computeNext() {
            while (backingItr.hasNext()) {
              RangeMapEntry<K, V> entry = backingItr.next();
              if (entry.getLowerBound().compareTo(subRange.upperBound) >= 0) {
                return endOfData();
              } else if (entry.getUpperBound().compareTo(subRange.lowerBound) > 0) {
                // this might not be true e.g. at the start of the iteration
                return Maps.immutableEntry(entry.getKey().intersection(subRange), entry.getValue());
              }
            }
            return endOfData();
          }
        };
      }

      @Override
      public Collection<V> values() {
        return new Maps.Values<Range<K>, V>(this) {
          @Override
          public boolean removeAll(Collection<?> c) {
            return removeEntryIf(compose(in(c), Maps.<V>valueFunction()));
          }

          @Override
          public boolean retainAll(Collection<?> c) {
            return removeEntryIf(compose(not(in(c)), Maps.<V>valueFunction()));
          }
        };
      }
    }
  }

  @Override
  public boolean equals(@NullableDecl Object o) {
    if (o instanceof RangeMap) {
      RangeMap<?, ?> rangeMap = (RangeMap<?, ?>) o;
      return asMapOfRanges().equals(rangeMap.asMapOfRanges());
    }
    return false;
  }

  @Override
  public int hashCode() {
    return asMapOfRanges().hashCode();
  }

  @Override
  public String toString() {
    return entriesByLowerBound.values().toString();
  }
}


/*
 * Copyright (C) 2010 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.common.base;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.logging.Level.WARNING;

import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.VisibleForTesting;
import java.util.logging.Logger;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;

/**
 * Static utility methods pertaining to {@code String} or {@code CharSequence} instances.
 *
 * @author Kevin Bourrillion
 * @since 3.0
 */
@GwtCompatible
public final class Strings {
  private Strings() {}

  /**
   * Returns the given string if it is non-null; the empty string otherwise.
   *
   * @param string the string to test and possibly return
   * @return {@code string} itself if it is non-null; {@code ""} if it is null
   */
  public static String nullToEmpty(@NullableDecl String string) {
    return Platform.nullToEmpty(string);
  }

  /**
   * Returns the given string if it is nonempty; {@code null} otherwise.
   *
   * @param string the string to test and possibly return
   * @return {@code string} itself if it is nonempty; {@code null} if it is empty or null
   */
  @NullableDecl
  public static String emptyToNull(@NullableDecl String string) {
    return Platform.emptyToNull(string);
  }

  /**
   * Returns {@code true} if the given string is null or is the empty string.
   *
   *
Consider normalizing your string references with {@link #nullToEmpty}. If you do, you can
   * use {@link String#isEmpty()} instead of this method, and you won't need special null-safe forms
   * of methods like {@link String#toUpperCase} either. Or, if you'd like to normalize "in the other
   * direction," converting empty strings to {@code null}, you can use {@link #emptyToNull}.
   *
   * @param string a string reference to check
   * @return {@code true} if the string is null or is the empty string
   */
  public static boolean isNullOrEmpty(@NullableDecl String string) {
    return Platform.stringIsNullOrEmpty(string);
  }

  /**
   * Returns a string, of length at least {@code minLength}, consisting of {@code string} prepended
   * with as many copies of {@code padChar} as are necessary to reach that length. For example,
   *
   *

   *
{@code padStart("7", 3, '0')} returns {@code "007"}
   *
{@code padStart("2010", 3, '0')} returns {@code "2010"}
   *

   *
   *
See {@link java.util.Formatter} for a richer set of formatting capabilities.
   *
   * @param string the string which should appear at the end of the result
   * @param minLength the minimum length the resulting string must have. Can be zero or negative, in
   *     which case the input string is always returned.
   * @param padChar the character to insert at the beginning of the result until the minimum length
   *     is reached
   * @return the padded string
   */
  public static String padStart(String string, int minLength, char padChar) {
    checkNotNull(string); // eager for GWT.
    if (string.length() >= minLength) {
      return string;
    }
    StringBuilder sb = new StringBuilder(minLength);
    for (int i = string.length(); i < minLength; i++) {
      sb.append(padChar);
    }
    sb.append(string);
    return sb.toString();
  }

  /**
   * Returns a string, of length at least {@code minLength}, consisting of {@code string} appended
   * with as many copies of {@code padChar} as are necessary to reach that length. For example,
   *
   *

   *
{@code padEnd("4.", 5, '0')} returns {@code "4.000"}
   *
{@code padEnd("2010", 3, '!')} returns {@code "2010"}
   *

   *
   *
See {@link java.util.Formatter} for a richer set of formatting capabilities.
   *
   * @param string the string which should appear at the beginning of the result
   * @param minLength the minimum length the resulting string must have. Can be zero or negative, in
   *     which case the input string is always returned.
   * @param padChar the character to append to the end of the result until the minimum length is
   *     reached
   * @return the padded string
   */
  public static String padEnd(String string, int minLength, char padChar) {
    checkNotNull(string); // eager for GWT.
    if (string.length() >= minLength) {
      return string;
    }
    StringBuilder sb = new StringBuilder(minLength);
    sb.append(string);
    for (int i = string.length(); i < minLength; i++) {
      sb.append(padChar);
    }
    return sb.toString();
  }

  /**
   * Returns a string consisting of a specific number of concatenated copies of an input string. For
   * example, {@code repeat("hey", 3)} returns the string {@code "heyheyhey"}.
   *
   * @param string any non-null string
   * @param count the number of times to repeat it; a nonnegative integer
   * @return a string containing {@code string} repeated {@code count} times (the empty string if
   *     {@code count} is zero)
   * @throws IllegalArgumentException if {@code count} is negative
   */
  public static String repeat(String string, int count) {
    checkNotNull(string); // eager for GWT.

    if (count <= 1) {
      checkArgument(count >= 0, "invalid count: %s", count);
      return (count == 0) ? "" : string;
    }

    // IF YOU MODIFY THE CODE HERE, you must update StringsRepeatBenchmark
    final int len = string.length();
    final long longSize = (long) len * (long) count;
    final int size = (int) longSize;
    if (size != longSize) {
      throw new ArrayIndexOutOfBoundsException("Required array size too large: " + longSize);
    }

    final char[] array = new char[size];
    string.getChars(0, len, array, 0);
    int n;
    for (n = len; n < size - n; n <<= 1) {
      System.arraycopy(array, 0, array, n, n);
    }
    System.arraycopy(array, 0, array, n, size - n);
    return new String(array);
  }

  /**
   * Returns the longest string {@code prefix} such that {@code a.toString().startsWith(prefix) &&
   * b.toString().startsWith(prefix)}, taking care not to split surrogate pairs. If {@code a} and
   * {@code b} have no common prefix, returns the empty string.
   *
   * @since 11.0
   */
  public static String commonPrefix(CharSequence a, CharSequence b) {
    checkNotNull(a);
    checkNotNull(b);

    int maxPrefixLength = Math.min(a.length(), b.length());
    int p = 0;
    while (p < maxPrefixLength && a.charAt(p) == b.charAt(p)) {
      p++;
    }
    if (validSurrogatePairAt(a, p - 1) || validSurrogatePairAt(b, p - 1)) {
      p--;
    }
    return a.subSequence(0, p).toString();
  }

  /**
   * Returns the longest string {@code suffix} such that {@code a.toString().endsWith(suffix) &&
   * b.toString().endsWith(suffix)}, taking care not to split surrogate pairs. If {@code a} and
   * {@code b} have no common suffix, returns the empty string.
   *
   * @since 11.0
   */
  public static String commonSuffix(CharSequence a, CharSequence b) {
    checkNotNull(a);
    checkNotNull(b);

    int maxSuffixLength = Math.min(a.length(), b.length());
    int s = 0;
    while (s < maxSuffixLength && a.charAt(a.length() - s - 1) == b.charAt(b.length() - s - 1)) {
      s++;
    }
    if (validSurrogatePairAt(a, a.length() - s - 1)
        || validSurrogatePairAt(b, b.length() - s - 1)) {
      s--;
    }
    return a.subSequence(a.length() - s, a.length()).toString();
  }

  /**
   * True when a valid surrogate pair starts at the given {@code index} in the given {@code string}.
   * Out-of-range indexes return false.
   */
  @VisibleForTesting
  static boolean validSurrogatePairAt(CharSequence string, int index) {
    return index >= 0
        && index <= (string.length() - 2)
        && Character.isHighSurrogate(string.charAt(index))
        && Character.isLowSurrogate(string.charAt(index + 1));
  }

  /**
   * Returns the given {@code template} string with each occurrence of {@code "%s"} replaced with
   * the corresponding argument value from {@code args}; or, if the placeholder and argument counts
   * do not match, returns a best-effort form of that string. Will not throw an exception under
   * normal conditions.
   *
   *
Note: For most string-formatting needs, use {@link String#format String.format},
   * {@link java.io.PrintWriter#format PrintWriter.format}, and related methods. These support the
   * full range of format
   * specifiers, and alert you to usage errors by throwing {@link
   * java.util.IllegalFormatException}.
   *
   *
In certain cases, such as outputting debugging information or constructing a message to be
   * used for another unchecked exception, an exception during string formatting would serve little
   * purpose except to supplant the real information you were trying to provide. These are the cases
   * this method is made for; it instead generates a best-effort string with all supplied argument
   * values present. This method is also useful in environments such as GWT where {@code
   * String.format} is not available. As an example, method implementations of the {@link
   * Preconditions} class use this formatter, for both of the reasons just discussed.
   *
   *
Warning: Only the exact two-character placeholder sequence {@code "%s"} is
   * recognized.
   *
   * @param template a string containing zero or more {@code "%s"} placeholder sequences. {@code
   *     null} is treated as the four-character string {@code "null"}.
   * @param args the arguments to be substituted into the message template. The first argument
   *     specified is substituted for the first occurrence of {@code "%s"} in the template, and so
   *     forth. A {@code null} argument is converted to the four-character string {@code "null"};
   *     non-null values are converted to strings using {@link Object#toString()}.
   * @since 25.1
   */
  // TODO(diamondm) consider using Arrays.toString() for array parameters
  public static String lenientFormat(@NullableDecl String template, @NullableDecl Object... args) {
    template = String.valueOf(template); // null -> "null"

    if (args == null) {
      args = new Object[] {"(Object[])null"};
    } else {
      for (int i = 0; i < args.length; i++) {
        args[i] = lenientToString(args[i]);
      }
    }

    // start substituting the arguments into the '%s' placeholders
    StringBuilder builder = new StringBuilder(template.length() + 16 * args.length);
    int templateStart = 0;
    int i = 0;
    while (i < args.length) {
      int placeholderStart = template.indexOf("%s", templateStart);
      if (placeholderStart == -1) {
        break;
      }
      builder.append(template, templateStart, placeholderStart);
      builder.append(args[i++]);
      templateStart = placeholderStart + 2;
    }
    builder.append(template, templateStart, template.length());

    // if we run out of placeholders, append the extra args in square braces
    if (i < args.length) {
      builder.append(" [");
      builder.append(args[i++]);
      while (i < args.length) {
        builder.append(", ");
        builder.append(args[i++]);
      }
      builder.append(']');
    }

    return builder.toString();
  }

  private static String lenientToString(@NullableDecl Object o) {
    try {
      return String.valueOf(o);
    } catch (Exception e) {
      // Default toString() behavior - see Object.toString()
      String objectToString =
          o.getClass().getName() + '@' + Integer.toHexString(System.identityHashCode(o));
      // Logger is created inline with fixed name to avoid forcing Proguard to create another class.
      Logger.getLogger("com.google.common.base.Strings")
          .log(WARNING, "Exception during lenientFormat for " + objectToString, e);
      return "<" + objectToString + " threw " + e.getClass().getName() + ">";
    }
  }
}


/*
 * Copyright (C) 2011 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.common.reflect;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.transform;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import java.io.Serializable;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.security.AccessControlException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicReference;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
 * Utilities for working with {@link Type}.
 *
 * @author Ben Yu
 */
final class Types {

  /** Class#toString without the "class " and "interface " prefixes */
  private static final Function<Type, String> TYPE_NAME =
      new Function<Type, String>() {
        @Override
        public String apply(Type from) {
          return JavaVersion.CURRENT.typeName(from);
        }
      };

  private static final Joiner COMMA_JOINER = Joiner.on(", ").useForNull("null");

  /** Returns the array type of {@code componentType}. */
  static Type newArrayType(Type componentType) {
    if (componentType instanceof WildcardType) {
      WildcardType wildcard = (WildcardType) componentType;
      Type[] lowerBounds = wildcard.getLowerBounds();
      checkArgument(lowerBounds.length <= 1, "Wildcard cannot have more than one lower bounds.");
      if (lowerBounds.length == 1) {
        return supertypeOf(newArrayType(lowerBounds[0]));
      } else {
        Type[] upperBounds = wildcard.getUpperBounds();
        checkArgument(upperBounds.length == 1, "Wildcard should have only one upper bound.");
        return subtypeOf(newArrayType(upperBounds[0]));
      }
    }
    return JavaVersion.CURRENT.newArrayType(componentType);
  }

  /**
   * Returns a type where {@code rawType} is parameterized by {@code arguments} and is owned by
   * {@code ownerType}.
   */
  static ParameterizedType newParameterizedTypeWithOwner(
      @Nullable Type ownerType, Class<?> rawType, Type... arguments) {
    if (ownerType == null) {
      return newParameterizedType(rawType, arguments);
    }
    // ParameterizedTypeImpl constructor already checks, but we want to throw NPE before IAE
    checkNotNull(arguments);
    checkArgument(rawType.getEnclosingClass() != null, "Owner type for unenclosed %s", rawType);
    return new ParameterizedTypeImpl(ownerType, rawType, arguments);
  }

  /** Returns a type where {@code rawType} is parameterized by {@code arguments}. */
  static ParameterizedType newParameterizedType(Class<?> rawType, Type... arguments) {
    return new ParameterizedTypeImpl(
        ClassOwnership.JVM_BEHAVIOR.getOwnerType(rawType), rawType, arguments);
  }

  /** Decides what owner type to use for constructing {@link ParameterizedType} from a raw class. */
  private enum ClassOwnership {
    OWNED_BY_ENCLOSING_CLASS {
      @Override
      @Nullable
      Class<?> getOwnerType(Class<?> rawType) {
        return rawType.getEnclosingClass();
      }
    },
    LOCAL_CLASS_HAS_NO_OWNER {
      @Override
      @Nullable
      Class<?> getOwnerType(Class<?> rawType) {
        if (rawType.isLocalClass()) {
          return null;
        } else {
          return rawType.getEnclosingClass();
        }
      }
    };

    abstract @Nullable Class<?> getOwnerType(Class<?> rawType);

    static final ClassOwnership JVM_BEHAVIOR = detectJvmBehavior();

    private static ClassOwnership detectJvmBehavior() {
      class LocalClass<T> {}
      Class<?> subclass = new LocalClass<String>() {}.getClass();
      ParameterizedType parameterizedType = (ParameterizedType) subclass.getGenericSuperclass();
      for (ClassOwnership behavior : ClassOwnership.values()) {
        if (behavior.getOwnerType(LocalClass.class) == parameterizedType.getOwnerType()) {
          return behavior;
        }
      }
      throw new AssertionError();
    }
  }

  /**
   * Returns a new {@link TypeVariable} that belongs to {@code declaration} with {@code name} and
   * {@code bounds}.
   */
  static <D extends GenericDeclaration> TypeVariable<D> newArtificialTypeVariable(
      D declaration, String name, Type... bounds) {
    return newTypeVariableImpl(
        declaration, name, (bounds.length == 0) ? new Type[] {Object.class} : bounds);
  }

  /** Returns a new {@link WildcardType} with {@code upperBound}. */
  @VisibleForTesting
  static WildcardType subtypeOf(Type upperBound) {
    return new WildcardTypeImpl(new Type[0], new Type[] {upperBound});
  }

  /** Returns a new {@link WildcardType} with {@code lowerBound}. */
  @VisibleForTesting
  static WildcardType supertypeOf(Type lowerBound) {
    return new WildcardTypeImpl(new Type[] {lowerBound}, new Type[] {Object.class});
  }

  /**
   * Returns human readable string representation of {@code type}.
   *
   *
The format is subject to change.
   */
  static String toString(Type type) {
    return (type instanceof Class) ? ((Class<?>) type).getName() : type.toString();
  }

  static @Nullable Type getComponentType(Type type) {
    checkNotNull(type);
    final AtomicReference<Type> result = new AtomicReference<>();
    new TypeVisitor() {
      @Override
      void visitTypeVariable(TypeVariable<?> t) {
        result.set(subtypeOfComponentType(t.getBounds()));
      }

      @Override
      void visitWildcardType(WildcardType t) {
        result.set(subtypeOfComponentType(t.getUpperBounds()));
      }

      @Override
      void visitGenericArrayType(GenericArrayType t) {
        result.set(t.getGenericComponentType());
      }

      @Override
      void visitClass(Class<?> t) {
        result.set(t.getComponentType());
      }
    }.visit(type);
    return result.get();
  }

  /**
   * Returns {@code ? extends X} if any of {@code bounds} is a subtype of {@code X[]}; or null
   * otherwise.
   */
  private static @Nullable Type subtypeOfComponentType(Type[] bounds) {
    for (Type bound : bounds) {
      Type componentType = getComponentType(bound);
      if (componentType != null) {
        // Only the first bound can be a class or array.
        // Bounds after the first can only be interfaces.
        if (componentType instanceof Class) {
          Class<?> componentClass = (Class<?>) componentType;
          if (componentClass.isPrimitive()) {
            return componentClass;
          }
        }
        return subtypeOf(componentType);
      }
    }
    return null;
  }

  private static final class GenericArrayTypeImpl implements GenericArrayType, Serializable {

    private final Type componentType;

    GenericArrayTypeImpl(Type componentType) {
      this.componentType = JavaVersion.CURRENT.usedInGenericType(componentType);
    }

    @Override
    public Type getGenericComponentType() {
      return componentType;
    }

    @Override
    public String toString() {
      return Types.toString(componentType) + "[]";
    }

    @Override
    public int hashCode() {
      return componentType.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
      if (obj instanceof GenericArrayType) {
        GenericArrayType that = (GenericArrayType) obj;
        return Objects.equal(getGenericComponentType(), that.getGenericComponentType());
      }
      return false;
    }

    private static final long serialVersionUID = 0;
  }

  private static final class ParameterizedTypeImpl implements ParameterizedType, Serializable {

    private final @Nullable Type ownerType;
    private final ImmutableList<Type> argumentsList;
    private final Class<?> rawType;

    ParameterizedTypeImpl(@Nullable Type ownerType, Class<?> rawType, Type[] typeArguments) {
      checkNotNull(rawType);
      checkArgument(typeArguments.length == rawType.getTypeParameters().length);
      disallowPrimitiveType(typeArguments, "type parameter");
      this.ownerType = ownerType;
      this.rawType = rawType;
      this.argumentsList = JavaVersion.CURRENT.usedInGenericType(typeArguments);
    }

    @Override
    public Type[] getActualTypeArguments() {
      return toArray(argumentsList);
    }

    @Override
    public Type getRawType() {
      return rawType;
    }

    @Override
    public Type getOwnerType() {
      return ownerType;
    }

    @Override
    public String toString() {
      StringBuilder builder = new StringBuilder();
      if (ownerType != null && JavaVersion.CURRENT.jdkTypeDuplicatesOwnerName()) {
        builder.append(JavaVersion.CURRENT.typeName(ownerType)).append('.');
      }
      return builder
          .append(rawType.getName())
          .append('<')
          .append(COMMA_JOINER.join(transform(argumentsList, TYPE_NAME)))
          .append('>')
          .toString();
    }

    @Override
    public int hashCode() {
      return (ownerType == null ? 0 : ownerType.hashCode())
          ^ argumentsList.hashCode()
          ^ rawType.hashCode();
    }

    @Override
    public boolean equals(Object other) {
      if (!(other instanceof ParameterizedType)) {
        return false;
      }
      ParameterizedType that = (ParameterizedType) other;
      return getRawType().equals(that.getRawType())
          && Objects.equal(getOwnerType(), that.getOwnerType())
          && Arrays.equals(getActualTypeArguments(), that.getActualTypeArguments());
    }

    private static final long serialVersionUID = 0;
  }

  private static <D extends GenericDeclaration> TypeVariable<D> newTypeVariableImpl(
      D genericDeclaration, String name, Type[] bounds) {
    TypeVariableImpl<D> typeVariableImpl =
        new TypeVariableImpl<D>(genericDeclaration, name, bounds);
    @SuppressWarnings("unchecked")
    TypeVariable<D> typeVariable =
        Reflection.newProxy(
            TypeVariable.class, new TypeVariableInvocationHandler(typeVariableImpl));
    return typeVariable;
  }

  /**
   * Invocation handler to work around a compatibility problem between Java 7 and Java 8.
   *
   *
Java 8 introduced a new method {@code getAnnotatedBounds()} in the {@link TypeVariable}
   * interface, whose return type {@code AnnotatedType[]} is also new in Java 8. That means that we
   * cannot implement that interface in source code in a way that will compile on both Java 7 and
   * Java 8. If we include the {@code getAnnotatedBounds()} method then its return type means it
   * won't compile on Java 7, while if we don't include the method then the compiler will complain
   * that an abstract method is unimplemented. So instead we use a dynamic proxy to get an
   * implementation. If the method being called on the {@code TypeVariable} instance has the same
   * name as one of the public methods of {@link TypeVariableImpl}, the proxy calls the same method
   * on its instance of {@code TypeVariableImpl}. Otherwise it throws {@link
   * UnsupportedOperationException}; this should only apply to {@code getAnnotatedBounds()}. This
   * does mean that users on Java 8 who obtain an instance of {@code TypeVariable} from {@link
   * TypeResolver#resolveType} will not be able to call {@code getAnnotatedBounds()} on it, but that
   * should hopefully be rare.
   *
   *
This workaround should be removed at a distant future time when we no longer support Java
   * versions earlier than 8.
   */
  private static final class TypeVariableInvocationHandler implements InvocationHandler {
    private static final ImmutableMap<String, Method> typeVariableMethods;

    static {
      ImmutableMap.Builder<String, Method> builder = ImmutableMap.builder();
      for (Method method : TypeVariableImpl.class.getMethods()) {
        if (method.getDeclaringClass().equals(TypeVariableImpl.class)) {
          try {
            method.setAccessible(true);
          } catch (AccessControlException e) {
            // OK: the method is accessible to us anyway. The setAccessible call is only for
            // unusual execution environments where that might not be true.
          }
          builder.put(method.getName(), method);
        }
      }
      typeVariableMethods = builder.build();
    }

    private final TypeVariableImpl<?> typeVariableImpl;

    TypeVariableInvocationHandler(TypeVariableImpl<?> typeVariableImpl) {
      this.typeVariableImpl = typeVariableImpl;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      String methodName = method.getName();
      Method typeVariableMethod = typeVariableMethods.get(methodName);
      if (typeVariableMethod == null) {
        throw new UnsupportedOperationException(methodName);
      } else {
        try {
          return typeVariableMethod.invoke(typeVariableImpl, args);
        } catch (InvocationTargetException e) {
          throw e.getCause();
        }
      }
    }
  }

  private static final class TypeVariableImpl<D extends GenericDeclaration> {

    private final D genericDeclaration;
    private final String name;
    private final ImmutableList<Type> bounds;

    TypeVariableImpl(D genericDeclaration, String name, Type[] bounds) {
      disallowPrimitiveType(bounds, "bound for type variable");
      this.genericDeclaration = checkNotNull(genericDeclaration);
      this.name = checkNotNull(name);
      this.bounds = ImmutableList.copyOf(bounds);
    }

    public Type[] getBounds() {
      return toArray(bounds);
    }

    public D getGenericDeclaration() {
      return genericDeclaration;
    }

    public String getName() {
      return name;
    }

    public String getTypeName() {
      return name;
    }

    @Override
    public String toString() {
      return name;
    }

    @Override
    public int hashCode() {
      return genericDeclaration.hashCode() ^ name.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
      if (NativeTypeVariableEquals.NATIVE_TYPE_VARIABLE_ONLY) {
        // equal only to our TypeVariable implementation with identical bounds
        if (obj != null
            && Proxy.isProxyClass(obj.getClass())
            && Proxy.getInvocationHandler(obj) instanceof TypeVariableInvocationHandler) {
          TypeVariableInvocationHandler typeVariableInvocationHandler =
              (TypeVariableInvocationHandler) Proxy.getInvocationHandler(obj);
          TypeVariableImpl<?> that = typeVariableInvocationHandler.typeVariableImpl;
          return name.equals(that.getName())
              && genericDeclaration.equals(that.getGenericDeclaration())
              && bounds.equals(that.bounds);
        }
        return false;
      } else {
        // equal to any TypeVariable implementation regardless of bounds
        if (obj instanceof TypeVariable) {
          TypeVariable<?> that = (TypeVariable<?>) obj;
          return name.equals(that.getName())
              && genericDeclaration.equals(that.getGenericDeclaration());
        }
        return false;
      }
    }
  }

  static final class WildcardTypeImpl implements WildcardType, Serializable {

    private final ImmutableList<Type> lowerBounds;
    private final ImmutableList<Type> upperBounds;

    WildcardTypeImpl(Type[] lowerBounds, Type[] upperBounds) {
      disallowPrimitiveType(lowerBounds, "lower bound for wildcard");
      disallowPrimitiveType(upperBounds, "upper bound for wildcard");
      this.lowerBounds = JavaVersion.CURRENT.usedInGenericType(lowerBounds);
      this.upperBounds = JavaVersion.CURRENT.usedInGenericType(upperBounds);
    }

    @Override
    public Type[] getLowerBounds() {
      return toArray(lowerBounds);
    }

    @Override
    public Type[] getUpperBounds() {
      return toArray(upperBounds);
    }

    @Override
    public boolean equals(Object obj) {
      if (obj instanceof WildcardType) {
        WildcardType that = (WildcardType) obj;
        return lowerBounds.equals(Arrays.asList(that.getLowerBounds()))
            && upperBounds.equals(Arrays.asList(that.getUpperBounds()));
      }
      return false;
    }

    @Override
    public int hashCode() {
      return lowerBounds.hashCode() ^ upperBounds.hashCode();
    }

    @Override
    public String toString() {
      StringBuilder builder = new StringBuilder("?");
      for (Type lowerBound : lowerBounds) {
        builder.append(" super ").append(JavaVersion.CURRENT.typeName(lowerBound));
      }
      for (Type upperBound : filterUpperBounds(upperBounds)) {
        builder.append(" extends ").append(JavaVersion.CURRENT.typeName(upperBound));
      }
      return builder.toString();
    }

    private static final long serialVersionUID = 0;
  }

  private static Type[] toArray(Collection<Type> types) {
    return types.toArray(new Type[types.size()]);
  }

  private static Iterable<Type> filterUpperBounds(Iterable<Type> bounds) {
    return Iterables.filter(bounds, Predicates.not(Predicates.<Type>equalTo(Object.class)));
  }

  private static void disallowPrimitiveType(Type[] types, String usedAs) {
    for (Type type : types) {
      if (type instanceof Class) {
        Class<?> cls = (Class<?>) type;
        checkArgument(!cls.isPrimitive(), "Primitive type '%s' used as %s", cls, usedAs);
      }
    }
  }

  /** Returns the {@code Class} object of arrays with {@code componentType}. */
  static Class<?> getArrayClass(Class<?> componentType) {
    // TODO(user): This is not the most efficient way to handle generic
    // arrays, but is there another way to extract the array class in a
    // non-hacky way (i.e. using String value class names- "[L...")?
    return Array.newInstance(componentType, 0).getClass();
  }

  // TODO(benyu): Once behavior is the same for all Java versions we support, delete this.
  enum JavaVersion {
    JAVA6 {
      @Override
      GenericArrayType newArrayType(Type componentType) {
        return new GenericArrayTypeImpl(componentType);
      }

      @Override
      Type usedInGenericType(Type type) {
        checkNotNull(type);
        if (type instanceof Class) {
          Class<?> cls = (Class<?>) type;
          if (cls.isArray()) {
            return new GenericArrayTypeImpl(cls.getComponentType());
          }
        }
        return type;
      }
    },
    JAVA7 {
      @Override
      Type newArrayType(Type componentType) {
        if (componentType instanceof Class) {
          return getArrayClass((Class<?>) componentType);
        } else {
          return new GenericArrayTypeImpl(componentType);
        }
      }

      @Override
      Type usedInGenericType(Type type) {
        return checkNotNull(type);
      }
    },
    JAVA8 {
      @Override
      Type newArrayType(Type componentType) {
        return JAVA7.newArrayType(componentType);
      }

      @Override
      Type usedInGenericType(Type type) {
        return JAVA7.usedInGenericType(type);
      }

      @Override
      String typeName(Type type) {
        try {
          Method getTypeName = Type.class.getMethod("getTypeName");
          return (String) getTypeName.invoke(type);
        } catch (NoSuchMethodException e) {
          throw new AssertionError("Type.getTypeName should be available in Java 8");
        } catch (InvocationTargetException | IllegalAccessException e) {
          throw new RuntimeException(e);
        }
      }
    },
    JAVA9 {
      @Override
      Type newArrayType(Type componentType) {
        return JAVA8.newArrayType(componentType);
      }

      @Override
      Type usedInGenericType(Type type) {
        return JAVA8.usedInGenericType(type);
      }

      @Override
      String typeName(Type type) {
        return JAVA8.typeName(type);
      }

      @Override
      boolean jdkTypeDuplicatesOwnerName() {
        return false;
      }
    };

    static final JavaVersion CURRENT;

    static {
      if (AnnotatedElement.class.isAssignableFrom(TypeVariable.class)) {
        if (new TypeCapture<Entry<String, int[][]>>() {}.capture()
            .toString()
            .contains("java.util.Map.java.util.Map")) {
          CURRENT = JAVA8;
        } else {
          CURRENT = JAVA9;
        }
      } else if (new TypeCapture<int[]>() {}.capture() instanceof Class) {
        CURRENT = JAVA7;
      } else {
        CURRENT = JAVA6;
      }
    }

    abstract Type newArrayType(Type componentType);

    abstract Type usedInGenericType(Type type);

    final ImmutableList<Type> usedInGenericType(Type[] types) {
      ImmutableList.Builder<Type> builder = ImmutableList.builder();
      for (Type type : types) {
        builder.add(usedInGenericType(type));
      }
      return builder.build();
    }

    String typeName(Type type) {
      return Types.toString(type);
    }

    boolean jdkTypeDuplicatesOwnerName() {
      return true;
    }
  }

  /**
   * Per issue 1635,
   * In JDK 1.7.0_51-b13, {@link TypeVariableImpl#equals(Object)} is changed to no longer be equal
   * to custom TypeVariable implementations. As a result, we need to make sure our TypeVariable
   * implementation respects symmetry. Moreover, we don't want to reconstruct a native type variable
   * {@code } using our implementation unless some of its bounds have changed in resolution. This
   * avoids creating unequal TypeVariable implementation unnecessarily. When the bounds do change,
   * however, it's fine for the synthetic TypeVariable to be unequal to any native TypeVariable
   * anyway.
   */
  static final class NativeTypeVariableEquals<X> {
    static final boolean NATIVE_TYPE_VARIABLE_ONLY =
        !NativeTypeVariableEquals.class.getTypeParameters()[0].equals(
            newArtificialTypeVariable(NativeTypeVariableEquals.class, "X"));
  }

  private Types() {}
}


/*
 * Copyright (C) 2008 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.common.collect;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Supplier;
import com.google.common.collect.Table.Cell;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.Spliterator;
import java.util.function.BinaryOperator;
import java.util.stream.Collector;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
 * Provides static methods that involve a {@code Table}.
 *
 *
See the Guava User Guide article on  {@code Tables}.
 *
 * @author Jared Levy
 * @author Louis Wasserman
 * @since 7.0
 */
@GwtCompatible
public final class Tables {
  private Tables() {}

  /**
   * Returns a {@link Collector} that accumulates elements into a {@code Table} created using the
   * specified supplier, whose cells are generated by applying the provided mapping functions to the
   * input elements. Cells are inserted into the generated {@code Table} in encounter order.
   *
   *
If multiple input elements map to the same row and column, an {@code IllegalStateException}
   * is thrown when the collection operation is performed.
   *
   * @since 21.0
   */
  @Beta
  public static <T, R, C, V, I extends Table<R, C, V>> Collector<T, ?, I> toTable(
      java.util.function.Function<? super T, ? extends R> rowFunction,
      java.util.function.Function<? super T, ? extends C> columnFunction,
      java.util.function.Function<? super T, ? extends V> valueFunction,
      java.util.function.Supplier tableSupplier) {
    return toTable(
        rowFunction,
        columnFunction,
        valueFunction,
        (v1, v2) -> {
          throw new IllegalStateException("Conflicting values " + v1 + " and " + v2);
        },
        tableSupplier);
  }

  /**
   * Returns a {@link Collector} that accumulates elements into a {@code Table} created using the
   * specified supplier, whose cells are generated by applying the provided mapping functions to the
   * input elements. Cells are inserted into the generated {@code Table} in encounter order.
   *
   *
If multiple input elements map to the same row and column, the specified merging function is
   * used to combine the values. Like {@link
   * java.util.stream.Collectors#toMap(java.util.function.Function, java.util.function.Function,
   * BinaryOperator, java.util.function.Supplier)}, this Collector throws a {@code
   * NullPointerException} on null values returned from {@code valueFunction}, and treats nulls
   * returned from {@code mergeFunction} as removals of that row/column pair.
   *
   * @since 21.0
   */
  public static <T, R, C, V, I extends Table<R, C, V>> Collector<T, ?, I> toTable(
      java.util.function.Function<? super T, ? extends R> rowFunction,
      java.util.function.Function<? super T, ? extends C> columnFunction,
      java.util.function.Function<? super T, ? extends V> valueFunction,
      BinaryOperator<V> mergeFunction,
      java.util.function.Supplier tableSupplier) {
    checkNotNull(rowFunction);
    checkNotNull(columnFunction);
    checkNotNull(valueFunction);
    checkNotNull(mergeFunction);
    checkNotNull(tableSupplier);
    return Collector.of(
        tableSupplier,
        (table, input) ->
            merge(
                table,
                rowFunction.apply(input),
                columnFunction.apply(input),
                valueFunction.apply(input),
                mergeFunction),
        (table1, table2) -> {
          for (Table.Cell<R, C, V> cell2 : table2.cellSet()) {
            merge(table1, cell2.getRowKey(), cell2.getColumnKey(), cell2.getValue(), mergeFunction);
          }
          return table1;
        });
  }

  private static <R, C, V> void merge(
      Table<R, C, V> table, R row, C column, V value, BinaryOperator<V> mergeFunction) {
    checkNotNull(value);
    V oldValue = table.get(row, column);
    if (oldValue == null) {
      table.put(row, column, value);
    } else {
      V newValue = mergeFunction.apply(oldValue, value);
      if (newValue == null) {
        table.remove(row, column);
      } else {
        table.put(row, column, newValue);
      }
    }
  }

  /**
   * Returns an immutable cell with the specified row key, column key, and value.
   *
   *
The returned cell is serializable.
   *
   * @param rowKey the row key to be associated with the returned cell
   * @param columnKey the column key to be associated with the returned cell
   * @param value the value to be associated with the returned cell
   */
  public static <R, C, V> Cell<R, C, V> immutableCell(
      @Nullable R rowKey, @Nullable C columnKey, @Nullable V value) {
    return new ImmutableCell<>(rowKey, columnKey, value);
  }

  static final class ImmutableCell<R, C, V> extends AbstractCell<R, C, V> implements Serializable {
    private final @Nullable R rowKey;
    private final @Nullable C columnKey;
    private final @Nullable V value;

    ImmutableCell(@Nullable R rowKey, @Nullable C columnKey, @Nullable V value) {
      this.rowKey = rowKey;
      this.columnKey = columnKey;
      this.value = value;
    }

    @Override
    public R getRowKey() {
      return rowKey;
    }

    @Override
    public C getColumnKey() {
      return columnKey;
    }

    @Override
    public V getValue() {
      return value;
    }

    private static final long serialVersionUID = 0;
  }

  abstract static class AbstractCell<R, C, V> implements Cell<R, C, V> {
    // needed for serialization
    AbstractCell() {}

    @Override
    public boolean equals(Object obj) {
      if (obj == this) {
        return true;
      }
      if (obj instanceof Cell) {
        Cell<?, ?, ?> other = (Cell<?, ?, ?>) obj;
        return Objects.equal(getRowKey(), other.getRowKey())
            && Objects.equal(getColumnKey(), other.getColumnKey())
            && Objects.equal(getValue(), other.getValue());
      }
      return false;
    }

    @Override
    public int hashCode() {
      return Objects.hashCode(getRowKey(), getColumnKey(), getValue());
    }

    @Override
    public String toString() {
      return "(" + getRowKey() + "," + getColumnKey() + ")=" + getValue();
    }
  }

  /**
   * Creates a transposed view of a given table that flips its row and column keys. In other words,
   * calling {@code get(columnKey, rowKey)} on the generated table always returns the same value as
   * calling {@code get(rowKey, columnKey)} on the original table. Updating the original table
   * changes the contents of the transposed table and vice versa.
   *
   *
The returned table supports update operations as long as the input table supports the
   * analogous operation with swapped rows and columns. For example, in a {@link HashBasedTable}
   * instance, {@code rowKeySet().iterator()} supports {@code remove()} but {@code
   * columnKeySet().iterator()} doesn't. With a transposed {@link HashBasedTable}, it's the other
   * way around.
   */
  public static <R, C, V> Table<C, R, V> transpose(Table<R, C, V> table) {
    return (table instanceof TransposeTable)
        ? ((TransposeTable<R, C, V>) table).original
        : new TransposeTable<C, R, V>(table);
  }

  private static class TransposeTable<C, R, V> extends AbstractTable<C, R, V> {
    final Table<R, C, V> original;

    TransposeTable(Table<R, C, V> original) {
      this.original = checkNotNull(original);
    }

    @Override
    public void clear() {
      original.clear();
    }

    @Override
    public Map<C, V> column(R columnKey) {
      return original.row(columnKey);
    }

    @Override
    public Set<R> columnKeySet() {
      return original.rowKeySet();
    }

    @Override
    public Map<R, Map<C, V>> columnMap() {
      return original.rowMap();
    }

    @Override
    public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) {
      return original.contains(columnKey, rowKey);
    }

    @Override
    public boolean containsColumn(@Nullable Object columnKey) {
      return original.containsRow(columnKey);
    }

    @Override
    public boolean containsRow(@Nullable Object rowKey) {
      return original.containsColumn(rowKey);
    }

    @Override
    public boolean containsValue(@Nullable Object value) {
      return original.containsValue(value);
    }

    @Override
    public V get(@Nullable Object rowKey, @Nullable Object columnKey) {
      return original.get(columnKey, rowKey);
    }

    @Override
    public V put(C rowKey, R columnKey, V value) {
      return original.put(columnKey, rowKey, value);
    }

    @Override
    public void putAll(Table<? extends C, ? extends R, ? extends V> table) {
      original.putAll(transpose(table));
    }

    @Override
    public V remove(@Nullable Object rowKey, @Nullable Object columnKey) {
      return original.remove(columnKey, rowKey);
    }

    @Override
    public Map<R, V> row(C rowKey) {
      return original.column(rowKey);
    }

    @Override
    public Set<C> rowKeySet() {
      return original.columnKeySet();
    }

    @Override
    public Map<C, Map<R, V>> rowMap() {
      return original.columnMap();
    }

    @Override
    public int size() {
      return original.size();
    }

    @Override
    public Collection<V> values() {
      return original.values();
    }

    // Will cast TRANSPOSE_CELL to a type that always succeeds
    private static final Function<Cell<?, ?, ?>, Cell<?, ?, ?>> TRANSPOSE_CELL =
        new Function<Cell<?, ?, ?>, Cell<?, ?, ?>>() {
          @Override
          public Cell<?, ?, ?> apply(Cell<?, ?, ?> cell) {
            return immutableCell(cell.getColumnKey(), cell.getRowKey(), cell.getValue());
          }
        };

    @SuppressWarnings("unchecked")
    @Override
    Iterator<Cell<C, R, V>> cellIterator() {
      return Iterators.transform(original.cellSet().iterator(), (Function) TRANSPOSE_CELL);
    }

    @SuppressWarnings("unchecked")
    @Override
    Spliterator<Cell<C, R, V>> cellSpliterator() {
      return CollectSpliterators.map(original.cellSet().spliterator(), (Function) TRANSPOSE_CELL);
    }
  }

  /**
   * Creates a table that uses the specified backing map and factory. It can generate a table based
   * on arbitrary {@link Map} classes.
   *
   *
The {@code factory}-generated and {@code backingMap} classes determine the table iteration
   * order. However, the table's {@code row()} method returns instances of a different class than
   * {@code factory.get()} does.
   *
   *
Call this method only when the simpler factory methods in classes like {@link
   * HashBasedTable} and {@link TreeBasedTable} won't suffice.
   *
   *
The views returned by the {@code Table} methods {@link Table#column}, {@link
   * Table#columnKeySet}, and {@link Table#columnMap} have iterators that don't support {@code
   * remove()}. Otherwise, all optional operations are supported. Null row keys, columns keys, and
   * values are not supported.
   *
   *
Lookups by row key are often faster than lookups by column key, because the data is stored
   * in a {@code Map<R, Map<C, V>>}. A method call like {@code column(columnKey).get(rowKey)} still
   * runs quickly, since the row key is provided. However, {@code column(columnKey).size()} takes
   * longer, since an iteration across all row keys occurs.
   *
   *
Note that this implementation is not synchronized. If multiple threads access this table
   * concurrently and one of the threads modifies the table, it must be synchronized externally.
   *
   *
The table is serializable if {@code backingMap}, {@code factory}, the maps generated by
   * {@code factory}, and the table contents are all serializable.
   *
   *
Note: the table assumes complete ownership over of {@code backingMap} and the maps returned
   * by {@code factory}. Those objects should not be manually updated and they should not use soft,
   * weak, or phantom references.
   *
   * @param backingMap place to store the mapping from each row key to its corresponding column key
   *     / value map
   * @param factory supplier of new, empty maps that will each hold all column key / value mappings
   *     for a given row key
   * @throws IllegalArgumentException if {@code backingMap} is not empty
   * @since 10.0
   */
  @Beta
  public static <R, C, V> Table<R, C, V> newCustomTable(
      Map<R, Map<C, V>> backingMap, Supplier<? extends Map<C, V>> factory) {
    checkArgument(backingMap.isEmpty());
    checkNotNull(factory);
    // TODO(jlevy): Wrap factory to validate that the supplied maps are empty?
    return new StandardTable<>(backingMap, factory);
  }

  /**
   * Returns a view of a table where each value is transformed by a function. All other properties
   * of the table, such as iteration order, are left intact.
   *
   *
Changes in the underlying table are reflected in this view. Conversely, this view supports
   * removal operations, and these are reflected in the underlying table.
   *
   *
It's acceptable for the underlying table to contain null keys, and even null values provided
   * that the function is capable of accepting null input. The transformed table might contain null
   * values, if the function sometimes gives a null result.
   *
   *
The returned table is not thread-safe or serializable, even if the underlying table is.
   *
   *
The function is applied lazily, invoked when needed. This is necessary for the returned
   * table to be a view, but it means that the function will be applied many times for bulk
   * operations like {@link Table#containsValue} and {@code Table.toString()}. For this to perform
   * well, {@code function} should be fast. To avoid lazy evaluation when the returned table doesn't
   * need to be a view, copy the returned table into a new table of your choosing.
   *
   * @since 10.0
   */
  @Beta
  public static <R, C, V1, V2> Table<R, C, V2> transformValues(
      Table<R, C, V1> fromTable, Function<? super V1, V2> function) {
    return new TransformedTable<>(fromTable, function);
  }

  private static class TransformedTable<R, C, V1, V2> extends AbstractTable<R, C, V2> {
    final Table<R, C, V1> fromTable;
    final Function<? super V1, V2> function;

    TransformedTable(Table<R, C, V1> fromTable, Function<? super V1, V2> function) {
      this.fromTable = checkNotNull(fromTable);
      this.function = checkNotNull(function);
    }

    @Override
    public boolean contains(Object rowKey, Object columnKey) {
      return fromTable.contains(rowKey, columnKey);
    }

    @Override
    public V2 get(Object rowKey, Object columnKey) {
      // The function is passed a null input only when the table contains a null
      // value.
      return contains(rowKey, columnKey) ? function.apply(fromTable.get(rowKey, columnKey)) : null;
    }

    @Override
    public int size() {
      return fromTable.size();
    }

    @Override
    public void clear() {
      fromTable.clear();
    }

    @Override
    public V2 put(R rowKey, C columnKey, V2 value) {
      throw new UnsupportedOperationException();
    }

    @Override
    public void putAll(Table<? extends R, ? extends C, ? extends V2> table) {
      throw new UnsupportedOperationException();
    }

    @Override
    public V2 remove(Object rowKey, Object columnKey) {
      return contains(rowKey, columnKey)
          ? function.apply(fromTable.remove(rowKey, columnKey))
          : null;
    }

    @Override
    public Map<C, V2> row(R rowKey) {
      return Maps.transformValues(fromTable.row(rowKey), function);
    }

    @Override
    public Map<R, V2> column(C columnKey) {
      return Maps.transformValues(fromTable.column(columnKey), function);
    }

    Function<Cell<R, C, V1>, Cell<R, C, V2>> cellFunction() {
      return new Function<Cell<R, C, V1>, Cell<R, C, V2>>() {
        @Override
        public Cell<R, C, V2> apply(Cell<R, C, V1> cell) {
          return immutableCell(
              cell.getRowKey(), cell.getColumnKey(), function.apply(cell.getValue()));
        }
      };
    }

    @Override
    Iterator<Cell<R, C, V2>> cellIterator() {
      return Iterators.transform(fromTable.cellSet().iterator(), cellFunction());
    }

    @Override
    Spliterator<Cell<R, C, V2>> cellSpliterator() {
      return CollectSpliterators.map(fromTable.cellSet().spliterator(), cellFunction());
    }

    @Override
    public Set<R> rowKeySet() {
      return fromTable.rowKeySet();
    }

    @Override
    public Set<C> columnKeySet() {
      return fromTable.columnKeySet();
    }

    @Override
    Collection<V2> createValues() {
      return Collections2.transform(fromTable.values(), function);
    }

    @Override
    public Map<R, Map<C, V2>> rowMap() {
      Function<Map<C, V1>, Map<C, V2>> rowFunction =
          new Function<Map<C, V1>, Map<C, V2>>() {
            @Override
            public Map<C, V2> apply(Map<C, V1> row) {
              return Maps.transformValues(row, function);
            }
          };
      return Maps.transformValues(fromTable.rowMap(), rowFunction);
    }

    @Override
    public Map<C, Map<R, V2>> columnMap() {
      Function<Map<R, V1>, Map<R, V2>> columnFunction =
          new Function<Map<R, V1>, Map<R, V2>>() {
            @Override
            public Map<R, V2> apply(Map<R, V1> column) {
              return Maps.transformValues(column, function);
            }
          };
      return Maps.transformValues(fromTable.columnMap(), columnFunction);
    }
  }

  /**
   * Returns an unmodifiable view of the specified table. This method allows modules to provide
   * users with "read-only" access to internal tables. Query operations on the returned table "read
   * through" to the specified table, and attempts to modify the returned table, whether direct or
   * via its collection views, result in an {@code UnsupportedOperationException}.
   *
   *
The returned table will be serializable if the specified table is serializable.
   *
   *
Consider using an {@link ImmutableTable}, which is guaranteed never to change.
   *
   * @since 11.0
   */
  public static <R, C, V> Table<R, C, V> unmodifiableTable(
      Table<? extends R, ? extends C, ? extends V> table) {
    return new UnmodifiableTable<>(table);
  }

  private static class UnmodifiableTable<R, C, V> extends ForwardingTable<R, C, V>
      implements Serializable {
    final Table<? extends R, ? extends C, ? extends V> delegate;

    UnmodifiableTable(Table<? extends R, ? extends C, ? extends V> delegate) {
      this.delegate = checkNotNull(delegate);
    }

    @SuppressWarnings("unchecked") // safe, covariant cast
    @Override
    protected Table<R, C, V> delegate() {
      return (Table<R, C, V>) delegate;
    }

    @Override
    public Set<Cell<R, C, V>> cellSet() {
      return Collections.unmodifiableSet(super.cellSet());
    }

    @Override
    public void clear() {
      throw new UnsupportedOperationException();
    }

    @Override
    public Map<R, V> column(@Nullable C columnKey) {
      return Collections.unmodifiableMap(super.column(columnKey));
    }

    @Override
    public Set<C> columnKeySet() {
      return Collections.unmodifiableSet(super.columnKeySet());
    }

    @Override
    public Map<C, Map<R, V>> columnMap() {
      Function<Map<R, V>, Map<R, V>> wrapper = unmodifiableWrapper();
      return Collections.unmodifiableMap(Maps.transformValues(super.columnMap(), wrapper));
    }

    @Override
    public V put(@Nullable R rowKey, @Nullable C columnKey, @Nullable V value) {
      throw new UnsupportedOperationException();
    }

    @Override
    public void putAll(Table<? extends R, ? extends C, ? extends V> table) {
      throw new UnsupportedOperationException();
    }

    @Override
    public V remove(@Nullable Object rowKey, @Nullable Object columnKey) {
      throw new UnsupportedOperationException();
    }

    @Override
    public Map<C, V> row(@Nullable R rowKey) {
      return Collections.unmodifiableMap(super.row(rowKey));
    }

    @Override
    public Set<R> rowKeySet() {
      return Collections.unmodifiableSet(super.rowKeySet());
    }

    @Override
    public Map<R, Map<C, V>> rowMap() {
      Function<Map<C, V>, Map<C, V>> wrapper = unmodifiableWrapper();
      return Collections.unmodifiableMap(Maps.transformValues(super.rowMap(), wrapper));
    }

    @Override
    public Collection<V> values() {
      return Collections.unmodifiableCollection(super.values());
    }

    private static final long serialVersionUID = 0;
  }

  /**
   * Returns an unmodifiable view of the specified row-sorted table. This method allows modules to
   * provide users with "read-only" access to internal tables. Query operations on the returned
   * table "read through" to the specified table, and attempts to modify the returned table, whether
   * direct or via its collection views, result in an {@code UnsupportedOperationException}.
   *
   *
The returned table will be serializable if the specified table is serializable.
   *
   * @param table the row-sorted table for which an unmodifiable view is to be returned
   * @return an unmodifiable view of the specified table
   * @since 11.0
   */
  @Beta
  public static <R, C, V> RowSortedTable<R, C, V> unmodifiableRowSortedTable(
      RowSortedTable<R, ? extends C, ? extends V> table) {
    /*
     * It's not ? extends R, because it's technically not covariant in R. Specifically,
     * table.rowMap().comparator() could return a comparator that only works for the ? extends R.
     * Collections.unmodifiableSortedMap makes the same distinction.
     */
    return new UnmodifiableRowSortedMap<>(table);
  }

  static final class UnmodifiableRowSortedMap<R, C, V> extends UnmodifiableTable<R, C, V>
      implements RowSortedTable<R, C, V> {

    public UnmodifiableRowSortedMap(RowSortedTable<R, ? extends C, ? extends V> delegate) {
      super(delegate);
    }

    @Override
    protected RowSortedTable<R, C, V> delegate() {
      return (RowSortedTable<R, C, V>) super.delegate();
    }

    @Override
    public SortedMap<R, Map<C, V>> rowMap() {
      Function<Map<C, V>, Map<C, V>> wrapper = unmodifiableWrapper();
      return Collections.unmodifiableSortedMap(Maps.transformValues(delegate().rowMap(), wrapper));
    }

    @Override
    public SortedSet<R> rowKeySet() {
      return Collections.unmodifiableSortedSet(delegate().rowKeySet());
    }

    private static final long serialVersionUID = 0;
  }

  @SuppressWarnings("unchecked")
  private static <K, V> Function<Map<K, V>, Map<K, V>> unmodifiableWrapper() {
    return (Function) UNMODIFIABLE_WRAPPER;
  }

  private static final Function<? extends Map<?, ?>, ? extends Map<?, ?>> UNMODIFIABLE_WRAPPER =
      new Function<Map<Object, Object>, Map<Object, Object>>() {
        @Override
        public Map<Object, Object> apply(Map<Object, Object> input) {
          return Collections.unmodifiableMap(input);
        }
      };

  /**
   * Returns a synchronized (thread-safe) table backed by the specified table. In order to guarantee
   * serial access, it is critical that all access to the backing table is accomplished
   * through the returned table.
   *
   *
It is imperative that the user manually synchronize on the returned table when accessing any
   * of its collection views:
   *
   *
{@code
   * Table<R, C, V> table = Tables.synchronizedTable(HashBasedTable.<R, C, V>create());
   * ...
   * Map<C, V> row = table.row(rowKey);  // Needn't be in synchronized block
   * ...
   * synchronized (table) {  // Synchronizing on table, not row!
   *   Iterator<Entry<C, V>> i = row.entrySet().iterator(); // Must be in synchronized block
   *   while (i.hasNext()) {
   *     foo(i.next());
   *   }
   * }
   * }

   *
   *
Failure to follow this advice may result in non-deterministic behavior.
   *
   *
The returned table will be serializable if the specified table is serializable.
   *
   * @param table the table to be wrapped in a synchronized view
   * @return a synchronized view of the specified table
   * @since 22.0
   */
  public static <R, C, V> Table<R, C, V> synchronizedTable(Table<R, C, V> table) {
    return Synchronized.table(table, null);
  }

  static boolean equalsImpl(Table<?, ?, ?> table, @Nullable Object obj) {
    if (obj == table) {
      return true;
    } else if (obj instanceof Table) {
      Table<?, ?, ?> that = (Table<?, ?, ?>) obj;
      return table.cellSet().equals(that.cellSet());
    } else {
      return false;
    }
  }
}


/*
 * Copyright (C) 2012 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.common.reflect;

import com.google.common.annotations.Beta;
import com.google.common.collect.ForwardingMap;
import com.google.common.collect.ImmutableMap;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.Map;

/**
 * A type-to-instance map backed by an {@link ImmutableMap}. See also {@link
 * MutableTypeToInstanceMap}.
 *
 * @author Ben Yu
 * @since 13.0
 */
@Beta
public final class ImmutableTypeToInstanceMap extends ForwardingMap<TypeToken<? extends B>, B>
    implements TypeToInstanceMap {

  /** Returns an empty type to instance map. */
  public static  ImmutableTypeToInstanceMap of() {
    return new ImmutableTypeToInstanceMap(ImmutableMap.<TypeToken<? extends B>, B>of());
  }

  /** Returns a new builder. */
  public static  Builder builder() {
    return new Builder();
  }

  /**
   * A builder for creating immutable type-to-instance maps. Example:
   *
   *
{@code
   * static final ImmutableTypeToInstanceMap<Handler<?>> HANDLERS =
   *     ImmutableTypeToInstanceMap.<Handler<?>>builder()
   *         .put(new TypeToken<Handler<Foo>>() {}, new FooHandler())
   *         .put(new TypeToken<Handler<Bar>>() {}, new SubBarHandler())
   *         .build();
   * }

   *
   *
After invoking {@link #build()} it is still possible to add more entries and build again.
   * Thus each map generated by this builder will be a superset of any map generated before it.
   *
   * @since 13.0
   */
  @Beta
  public static final class Builder {
    private final ImmutableMap.Builder<TypeToken<? extends B>, B> mapBuilder =
        ImmutableMap.builder();

    private Builder() {}

    /**
     * Associates {@code key} with {@code value} in the built map. Duplicate keys are not allowed,
     * and will cause {@link #build} to fail.
     */
    @CanIgnoreReturnValue
    public <T extends B> Builder put(Class<T> key, T value) {
      mapBuilder.put(TypeToken.of(key), value);
      return this;
    }

    /**
     * Associates {@code key} with {@code value} in the built map. Duplicate keys are not allowed,
     * and will cause {@link #build} to fail.
     */
    @CanIgnoreReturnValue
    public <T extends B> Builder put(TypeToken<T> key, T value) {
      mapBuilder.put(key.rejectTypeVariables(), value);
      return this;
    }

    /**
     * Returns a new immutable type-to-instance map containing the entries provided to this builder.
     *
     * @throws IllegalArgumentException if duplicate keys were added
     */
    public ImmutableTypeToInstanceMap build() {
      return new ImmutableTypeToInstanceMap(mapBuilder.build());
    }
  }

  private final ImmutableMap<TypeToken<? extends B>, B> delegate;

  private ImmutableTypeToInstanceMap(ImmutableMap<TypeToken<? extends B>, B> delegate) {
    this.delegate = delegate;
  }

  @Override
  public <T extends B> T getInstance(TypeToken<T> type) {
    return trustedGet(type.rejectTypeVariables());
  }

  @Override
  public <T extends B> T getInstance(Class<T> type) {
    return trustedGet(TypeToken.of(type));
  }

  /**
   * Guaranteed to throw an exception and leave the map unmodified.
   *
   * @deprecated unsupported operation
   * @throws UnsupportedOperationException always
   */
  @CanIgnoreReturnValue
  @Deprecated
  @Override
  public <T extends B> T putInstance(TypeToken<T> type, T value) {
    throw new UnsupportedOperationException();
  }

  /**
   * Guaranteed to throw an exception and leave the map unmodified.
   *
   * @deprecated unsupported operation
   * @throws UnsupportedOperationException always
   */
  @CanIgnoreReturnValue
  @Deprecated
  @Override
  public <T extends B> T putInstance(Class<T> type, T value) {
    throw new UnsupportedOperationException();
  }

  /**
   * Guaranteed to throw an exception and leave the map unmodified.
   *
   * @deprecated unsupported operation
   * @throws UnsupportedOperationException always
   */
  @CanIgnoreReturnValue
  @Deprecated
  @Override
  public B put(TypeToken<? extends B> key, B value) {
    throw new UnsupportedOperationException();
  }

  /**
   * Guaranteed to throw an exception and leave the map unmodified.
   *
   * @deprecated unsupported operation
   * @throws UnsupportedOperationException always
   */
  @Deprecated
  @Override
  public void putAll(Map<? extends TypeToken<? extends B>, ? extends B> map) {
    throw new UnsupportedOperationException();
  }

  @Override
  protected Map<TypeToken<? extends B>, B> delegate() {
    return delegate;
  }

  @SuppressWarnings("unchecked") // value could not get in if not a T
  private <T extends B> T trustedGet(TypeToken<T> type) {
    return (T) delegate.get(type);
  }
}