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 org.jspecify.annotations.Nullable;
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 */
031public final class ImmutableTypeToInstanceMap<B> extends ForwardingMap<TypeToken<? extends B>, B>
032    implements TypeToInstanceMap<B> {
033
034  /** Returns an empty type to instance map. */
035  public static <B> ImmutableTypeToInstanceMap<B> of() {
036    return new ImmutableTypeToInstanceMap<>(ImmutableMap.<TypeToken<? extends B>, B>of());
037  }
038
039  /** Returns a new builder. */
040  public static <B> Builder<B> builder() {
041    return new Builder<>();
042  }
043
044  /**
045   * A builder for creating immutable type-to-instance maps. Example:
046   *
047   * {@snippet :
048   * static final ImmutableTypeToInstanceMap<Handler<?>> HANDLERS =
049   *     ImmutableTypeToInstanceMap.<Handler<?>>builder()
050   *         .put(new TypeToken<Handler<Foo>>() {}, new FooHandler())
051   *         .put(new TypeToken<Handler<Bar>>() {}, new SubBarHandler())
052   *         .build();
053   * }
054   *
055   * <p>After invoking {@link #build()} it is still possible to add more entries and build again.
056   * Thus each map generated by this builder will be a superset of any map generated before it.
057   *
058   * @since 13.0
059   */
060  public static final class Builder<B> {
061    private final ImmutableMap.Builder<TypeToken<? extends B>, B> mapBuilder =
062        ImmutableMap.builder();
063
064    private Builder() {}
065
066    /**
067     * Associates {@code key} with {@code value} in the built map. Duplicate keys are not allowed,
068     * and will cause {@link #build} to fail.
069     */
070    @CanIgnoreReturnValue
071    public <T extends B> Builder<B> put(Class<T> key, T value) {
072      mapBuilder.put(TypeToken.of(key), value);
073      return this;
074    }
075
076    /**
077     * Associates {@code key} with {@code value} in the built map. Duplicate keys are not allowed,
078     * and will cause {@link #build} to fail.
079     */
080    @CanIgnoreReturnValue
081    public <T extends B> Builder<B> put(TypeToken<T> key, T value) {
082      mapBuilder.put(key.rejectTypeVariables(), value);
083      return this;
084    }
085
086    /**
087     * Returns a new immutable type-to-instance map containing the entries provided to this builder.
088     *
089     * @throws IllegalArgumentException if duplicate keys were added
090     */
091    public ImmutableTypeToInstanceMap<B> build() {
092      return new ImmutableTypeToInstanceMap<>(mapBuilder.buildOrThrow());
093    }
094  }
095
096  private final ImmutableMap<TypeToken<? extends B>, B> delegate;
097
098  private ImmutableTypeToInstanceMap(ImmutableMap<TypeToken<? extends B>, B> delegate) {
099    this.delegate = delegate;
100  }
101
102  @Override
103  public <T extends B> @Nullable T getInstance(TypeToken<T> type) {
104    return trustedGet(type.rejectTypeVariables());
105  }
106
107  @Override
108  public <T extends B> @Nullable T getInstance(Class<T> type) {
109    return trustedGet(TypeToken.of(type));
110  }
111
112  /**
113   * Guaranteed to throw an exception and leave the map unmodified.
114   *
115   * @deprecated unsupported operation
116   * @throws UnsupportedOperationException always
117   */
118  @CanIgnoreReturnValue
119  @Deprecated
120  @Override
121  @DoNotCall("Always throws UnsupportedOperationException")
122  public <T extends B> @Nullable T putInstance(TypeToken<T> type, T value) {
123    throw new UnsupportedOperationException();
124  }
125
126  /**
127   * Guaranteed to throw an exception and leave the map unmodified.
128   *
129   * @deprecated unsupported operation
130   * @throws UnsupportedOperationException always
131   */
132  @CanIgnoreReturnValue
133  @Deprecated
134  @Override
135  @DoNotCall("Always throws UnsupportedOperationException")
136  public <T extends B> @Nullable T putInstance(Class<T> type, T value) {
137    throw new UnsupportedOperationException();
138  }
139
140  /**
141   * Guaranteed to throw an exception and leave the map unmodified.
142   *
143   * @deprecated unsupported operation
144   * @throws UnsupportedOperationException always
145   */
146  @CanIgnoreReturnValue
147  @Deprecated
148  @Override
149  @DoNotCall("Always throws UnsupportedOperationException")
150  public @Nullable B put(TypeToken<? extends B> key, B value) {
151    throw new UnsupportedOperationException();
152  }
153
154  /**
155   * Guaranteed to throw an exception and leave the map unmodified.
156   *
157   * @deprecated unsupported operation
158   * @throws UnsupportedOperationException always
159   */
160  @Deprecated
161  @Override
162  @DoNotCall("Always throws UnsupportedOperationException")
163  public void putAll(Map<? extends TypeToken<? extends B>, ? extends B> map) {
164    throw new UnsupportedOperationException();
165  }
166
167  @Override
168  protected Map<TypeToken<? extends B>, B> delegate() {
169    return delegate;
170  }
171
172  @SuppressWarnings("unchecked") // value could not get in if not a T
173  private <T extends B> @Nullable T trustedGet(TypeToken<T> type) {
174    return (T) delegate.get(type);
175  }
176}