001/*
002 * Copyright (C) 2012 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License
010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011 * or implied. See the License for the specific language governing permissions and limitations under
012 * the License.
013 */
014
015package com.google.common.reflect;
016
017import com.google.common.annotations.Beta;
018import com.google.common.collect.ForwardingMap;
019import com.google.common.collect.ImmutableMap;
020import com.google.errorprone.annotations.CanIgnoreReturnValue;
021import com.google.errorprone.annotations.DoNotCall;
022import java.util.Map;
023
024/**
025 * A type-to-instance map backed by an {@link ImmutableMap}. See also {@link
026 * MutableTypeToInstanceMap}.
027 *
028 * @author Ben Yu
029 * @since 13.0
030 */
031@Beta
032public final class ImmutableTypeToInstanceMap<B> extends ForwardingMap<TypeToken<? extends B>, B>
033    implements TypeToInstanceMap<B> {
034
035  /** Returns an empty type to instance map. */
036  public static <B> ImmutableTypeToInstanceMap<B> of() {
037    return new ImmutableTypeToInstanceMap<B>(ImmutableMap.<TypeToken<? extends B>, B>of());
038  }
039
040  /** Returns a new builder. */
041  public static <B> Builder<B> builder() {
042    return new Builder<B>();
043  }
044
045  /**
046   * A builder for creating immutable type-to-instance maps. Example:
047   *
048   * <pre>{@code
049   * static final ImmutableTypeToInstanceMap<Handler<?>> HANDLERS =
050   *     ImmutableTypeToInstanceMap.<Handler<?>>builder()
051   *         .put(new TypeToken<Handler<Foo>>() {}, new FooHandler())
052   *         .put(new TypeToken<Handler<Bar>>() {}, new SubBarHandler())
053   *         .build();
054   * }</pre>
055   *
056   * <p>After invoking {@link #build()} it is still possible to add more entries and build again.
057   * Thus each map generated by this builder will be a superset of any map generated before it.
058   *
059   * @since 13.0
060   */
061  @Beta
062  public static final class Builder<B> {
063    private final ImmutableMap.Builder<TypeToken<? extends B>, B> mapBuilder =
064        ImmutableMap.builder();
065
066    private Builder() {}
067
068    /**
069     * Associates {@code key} with {@code value} in the built map. Duplicate keys are not allowed,
070     * and will cause {@link #build} to fail.
071     */
072    @CanIgnoreReturnValue
073    public <T extends B> Builder<B> put(Class<T> key, T value) {
074      mapBuilder.put(TypeToken.of(key), value);
075      return this;
076    }
077
078    /**
079     * Associates {@code key} with {@code value} in the built map. Duplicate keys are not allowed,
080     * and will cause {@link #build} to fail.
081     */
082    @CanIgnoreReturnValue
083    public <T extends B> Builder<B> put(TypeToken<T> key, T value) {
084      mapBuilder.put(key.rejectTypeVariables(), value);
085      return this;
086    }
087
088    /**
089     * Returns a new immutable type-to-instance map containing the entries provided to this builder.
090     *
091     * @throws IllegalArgumentException if duplicate keys were added
092     */
093    public ImmutableTypeToInstanceMap<B> build() {
094      return new ImmutableTypeToInstanceMap<B>(mapBuilder.build());
095    }
096  }
097
098  private final ImmutableMap<TypeToken<? extends B>, B> delegate;
099
100  private ImmutableTypeToInstanceMap(ImmutableMap<TypeToken<? extends B>, B> delegate) {
101    this.delegate = delegate;
102  }
103
104  @Override
105  public <T extends B> T getInstance(TypeToken<T> type) {
106    return trustedGet(type.rejectTypeVariables());
107  }
108
109  @Override
110  public <T extends B> T getInstance(Class<T> type) {
111    return trustedGet(TypeToken.of(type));
112  }
113
114  /**
115   * Guaranteed to throw an exception and leave the map unmodified.
116   *
117   * @deprecated unsupported operation
118   * @throws UnsupportedOperationException always
119   */
120  @CanIgnoreReturnValue
121  @Deprecated
122  @Override
123  @DoNotCall("Always throws UnsupportedOperationException")
124  public <T extends B> T putInstance(TypeToken<T> type, T value) {
125    throw new UnsupportedOperationException();
126  }
127
128  /**
129   * Guaranteed to throw an exception and leave the map unmodified.
130   *
131   * @deprecated unsupported operation
132   * @throws UnsupportedOperationException always
133   */
134  @CanIgnoreReturnValue
135  @Deprecated
136  @Override
137  @DoNotCall("Always throws UnsupportedOperationException")
138  public <T extends B> T putInstance(Class<T> type, T value) {
139    throw new UnsupportedOperationException();
140  }
141
142  /**
143   * Guaranteed to throw an exception and leave the map unmodified.
144   *
145   * @deprecated unsupported operation
146   * @throws UnsupportedOperationException always
147   */
148  @CanIgnoreReturnValue
149  @Deprecated
150  @Override
151  @DoNotCall("Always throws UnsupportedOperationException")
152  public B put(TypeToken<? extends B> key, B value) {
153    throw new UnsupportedOperationException();
154  }
155
156  /**
157   * Guaranteed to throw an exception and leave the map unmodified.
158   *
159   * @deprecated unsupported operation
160   * @throws UnsupportedOperationException always
161   */
162  @Deprecated
163  @Override
164  @DoNotCall("Always throws UnsupportedOperationException")
165  public void putAll(Map<? extends TypeToken<? extends B>, ? extends B> map) {
166    throw new UnsupportedOperationException();
167  }
168
169  @Override
170  protected Map<TypeToken<? extends B>, B> delegate() {
171    return delegate;
172  }
173
174  @SuppressWarnings("unchecked") // value could not get in if not a T
175  private <T extends B> T trustedGet(TypeToken<T> type) {
176    return (T) delegate.get(type);
177  }
178}