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.collect.ForwardingMap;
018import com.google.common.collect.ImmutableMap;
019import com.google.errorprone.annotations.CanIgnoreReturnValue;
020import com.google.errorprone.annotations.DoNotCall;
021import java.util.Map;
022import javax.annotation.CheckForNull;
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@ElementTypesAreNonnullByDefault
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<>(ImmutableMap.<TypeToken<? extends B>, B>of());
038  }
039
040  /** Returns a new builder. */
041  public static <B> Builder<B> builder() {
042    return new Builder<>();
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  public static final class Builder<B> {
062    private final ImmutableMap.Builder<TypeToken<? extends B>, B> mapBuilder =
063        ImmutableMap.builder();
064
065    private Builder() {}
066
067    /**
068     * Associates {@code key} with {@code value} in the built map. Duplicate keys are not allowed,
069     * and will cause {@link #build} to fail.
070     */
071    @CanIgnoreReturnValue
072    public <T extends B> Builder<B> put(Class<T> key, T value) {
073      mapBuilder.put(TypeToken.of(key), value);
074      return this;
075    }
076
077    /**
078     * Associates {@code key} with {@code value} in the built map. Duplicate keys are not allowed,
079     * and will cause {@link #build} to fail.
080     */
081    @CanIgnoreReturnValue
082    public <T extends B> Builder<B> put(TypeToken<T> key, T value) {
083      mapBuilder.put(key.rejectTypeVariables(), value);
084      return this;
085    }
086
087    /**
088     * Returns a new immutable type-to-instance map containing the entries provided to this builder.
089     *
090     * @throws IllegalArgumentException if duplicate keys were added
091     */
092    public ImmutableTypeToInstanceMap<B> build() {
093      return new ImmutableTypeToInstanceMap<>(mapBuilder.build());
094    }
095  }
096
097  private final ImmutableMap<TypeToken<? extends B>, B> delegate;
098
099  private ImmutableTypeToInstanceMap(ImmutableMap<TypeToken<? extends B>, B> delegate) {
100    this.delegate = delegate;
101  }
102
103  @Override
104  @CheckForNull
105  public <T extends B> T getInstance(TypeToken<T> type) {
106    return trustedGet(type.rejectTypeVariables());
107  }
108
109  @Override
110  @CheckForNull
111  public <T extends B> T getInstance(Class<T> type) {
112    return trustedGet(TypeToken.of(type));
113  }
114
115  /**
116   * Guaranteed to throw an exception and leave the map unmodified.
117   *
118   * @deprecated unsupported operation
119   * @throws UnsupportedOperationException always
120   */
121  @CanIgnoreReturnValue
122  @Deprecated
123  @Override
124  @DoNotCall("Always throws UnsupportedOperationException")
125  @CheckForNull
126  public <T extends B> T putInstance(TypeToken<T> type, T value) {
127    throw new UnsupportedOperationException();
128  }
129
130  /**
131   * Guaranteed to throw an exception and leave the map unmodified.
132   *
133   * @deprecated unsupported operation
134   * @throws UnsupportedOperationException always
135   */
136  @CanIgnoreReturnValue
137  @Deprecated
138  @Override
139  @DoNotCall("Always throws UnsupportedOperationException")
140  @CheckForNull
141  public <T extends B> T putInstance(Class<T> type, T value) {
142    throw new UnsupportedOperationException();
143  }
144
145  /**
146   * Guaranteed to throw an exception and leave the map unmodified.
147   *
148   * @deprecated unsupported operation
149   * @throws UnsupportedOperationException always
150   */
151  @CanIgnoreReturnValue
152  @Deprecated
153  @Override
154  @DoNotCall("Always throws UnsupportedOperationException")
155  @CheckForNull
156  public B put(TypeToken<? extends B> key, B value) {
157    throw new UnsupportedOperationException();
158  }
159
160  /**
161   * Guaranteed to throw an exception and leave the map unmodified.
162   *
163   * @deprecated unsupported operation
164   * @throws UnsupportedOperationException always
165   */
166  @Deprecated
167  @Override
168  @DoNotCall("Always throws UnsupportedOperationException")
169  public void putAll(Map<? extends TypeToken<? extends B>, ? extends B> map) {
170    throw new UnsupportedOperationException();
171  }
172
173  @Override
174  protected Map<TypeToken<? extends B>, B> delegate() {
175    return delegate;
176  }
177
178  @SuppressWarnings("unchecked") // value could not get in if not a T
179  @CheckForNull
180  private <T extends B> T trustedGet(TypeToken<T> type) {
181    return (T) delegate.get(type);
182  }
183}