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 java.util.Map;
022
023/**
024 * A type-to-instance map backed by an {@link ImmutableMap}. See also {@link
025 * MutableTypeToInstanceMap}.
026 *
027 * @author Ben Yu
028 * @since 13.0
029 */
030@Beta
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<B>(ImmutableMap.<TypeToken<? extends B>, B>of());
037  }
038
039  /** Returns a new builder. */
040  public static <B> Builder<B> builder() {
041    return new Builder<B>();
042  }
043
044  /**
045   * A builder for creating immutable type-to-instance maps. Example:
046   *
047   * <pre>{@code
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   * }</pre>
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  @Beta
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<B>(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  public <T extends B> T getInstance(TypeToken<T> type) {
105    return trustedGet(type.rejectTypeVariables());
106  }
107
108  @Override
109  public <T extends B> T getInstance(Class<T> type) {
110    return trustedGet(TypeToken.of(type));
111  }
112
113  /**
114   * Guaranteed to throw an exception and leave the map unmodified.
115   *
116   * @deprecated unsupported operation
117   * @throws UnsupportedOperationException always
118   */
119  @CanIgnoreReturnValue
120  @Deprecated
121  @Override
122  public <T extends B> 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  public <T extends B> T putInstance(Class<T> type, T value) {
136    throw new UnsupportedOperationException();
137  }
138
139  /**
140   * Guaranteed to throw an exception and leave the map unmodified.
141   *
142   * @deprecated unsupported operation
143   * @throws UnsupportedOperationException always
144   */
145  @CanIgnoreReturnValue
146  @Deprecated
147  @Override
148  public B put(TypeToken<? extends B> key, B value) {
149    throw new UnsupportedOperationException();
150  }
151
152  /**
153   * Guaranteed to throw an exception and leave the map unmodified.
154   *
155   * @deprecated unsupported operation
156   * @throws UnsupportedOperationException always
157   */
158  @Deprecated
159  @Override
160  public void putAll(Map<? extends TypeToken<? extends B>, ? extends B> map) {
161    throw new UnsupportedOperationException();
162  }
163
164  @Override
165  protected Map<TypeToken<? extends B>, B> delegate() {
166    return delegate;
167  }
168
169  @SuppressWarnings("unchecked") // value could not get in if not a T
170  private <T extends B> T trustedGet(TypeToken<T> type) {
171    return (T) delegate.get(type);
172  }
173}