1090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson/*
21d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Copyright (C) 2009 The Guava Authors
3090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson *
4090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * Licensed under the Apache License, Version 2.0 (the "License");
5090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * you may not use this file except in compliance with the License.
6090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * You may obtain a copy of the License at
7090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson *
8090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * http://www.apache.org/licenses/LICENSE-2.0
9090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson *
10090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * Unless required by applicable law or agreed to in writing, software
11090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS,
12090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * See the License for the specific language governing permissions and
14090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * limitations under the License.
15090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson */
16090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
17090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilsonpackage com.google.common.collect;
18090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
197dd252788645e940eada959bdde927426e2531c9Paul Duffinimport static com.google.common.base.Preconditions.checkNotNull;
207dd252788645e940eada959bdde927426e2531c9Paul Duffin
211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.primitives.Primitives;
22090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
230888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport java.io.Serializable;
24090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilsonimport java.util.Map;
25090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
267dd252788645e940eada959bdde927426e2531c9Paul Duffinimport javax.annotation.Nullable;
277dd252788645e940eada959bdde927426e2531c9Paul Duffin
28090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson/**
29090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * A class-to-instance map backed by an {@link ImmutableMap}. See also {@link
30090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * MutableClassToInstanceMap}.
31090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson *
32090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson * @author Kevin Bourrillion
331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @since 2.0 (imported from Google Collections Library)
34090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson */
350888a09821a98ac0680fad765217302858e70fa4Paul Duffinpublic final class ImmutableClassToInstanceMap<B>
360888a09821a98ac0680fad765217302858e70fa4Paul Duffin    extends ForwardingMap<Class<? extends B>, B>
370888a09821a98ac0680fad765217302858e70fa4Paul Duffin    implements ClassToInstanceMap<B>, Serializable {
380888a09821a98ac0680fad765217302858e70fa4Paul Duffin
39090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
40090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * Returns a new builder. The generated builder is equivalent to the builder
41090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * created by the {@link Builder} constructor.
42090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
43090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  public static <B> Builder<B> builder() {
44090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    return new Builder<B>();
45090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
46090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
47090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
48090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * A builder for creating immutable class-to-instance maps. Example:
49090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * <pre>   {@code
50090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
51090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *   static final ImmutableClassToInstanceMap<Handler> HANDLERS =
52090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *       new ImmutableClassToInstanceMap.Builder<Handler>()
53090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *           .put(FooHandler.class, new FooHandler())
54090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *           .put(BarHandler.class, new SubBarHandler())
55090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *           .put(Handler.class, new QuuxHandler())
56090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *           .build();}</pre>
57090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
580888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * <p>After invoking {@link #build()} it is still possible to add more entries
591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * and build again. Thus each map generated by this builder will be a superset
601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * of any map generated before it.
611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   *
621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * @since 2.0 (imported from Google Collections Library)
63090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
64090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  public static final class Builder<B> {
650888a09821a98ac0680fad765217302858e70fa4Paul Duffin    private final ImmutableMap.Builder<Class<? extends B>, B> mapBuilder
660888a09821a98ac0680fad765217302858e70fa4Paul Duffin        = ImmutableMap.builder();
67090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
68090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    /**
69090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     * Associates {@code key} with {@code value} in the built map. Duplicate
70090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     * keys are not allowed, and will cause {@link #build} to fail.
71090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     */
721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    public <T extends B> Builder<B> put(Class<T> key, T value) {
731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      mapBuilder.put(key, value);
74090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      return this;
75090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
76090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
77090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    /**
78090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     * Associates all of {@code map's} keys and values in the built map.
79090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     * Duplicate keys are not allowed, and will cause {@link #build} to fail.
80090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     *
81090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     * @throws NullPointerException if any key or value in {@code map} is null
82090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     * @throws ClassCastException if any value is not an instance of the type
83090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     *     specified by its key
84090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     */
850888a09821a98ac0680fad765217302858e70fa4Paul Duffin    public <T extends B> Builder<B> putAll(
860888a09821a98ac0680fad765217302858e70fa4Paul Duffin        Map<? extends Class<? extends T>, ? extends T> map) {
870888a09821a98ac0680fad765217302858e70fa4Paul Duffin      for (Entry<? extends Class<? extends T>, ? extends T> entry
880888a09821a98ac0680fad765217302858e70fa4Paul Duffin          : map.entrySet()) {
89090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        Class<? extends T> type = entry.getKey();
90090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        T value = entry.getValue();
91090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson        mapBuilder.put(type, cast(type, value));
92090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      }
93090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      return this;
94090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
95090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    private static <B, T extends B> T cast(Class<T> type, B value) {
971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      return Primitives.wrap(type).cast(value);
981d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
100090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    /**
101090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     * Returns a new immutable class-to-instance map containing the entries
102090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     * provided to this builder.
103090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     *
104090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     * @throws IllegalArgumentException if duplicate keys were added
105090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson     */
106090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    public ImmutableClassToInstanceMap<B> build() {
107090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      return new ImmutableClassToInstanceMap<B>(mapBuilder.build());
108090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
109090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
110090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
111090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
112090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * Returns an immutable map containing the same entries as {@code map}. If
113090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * {@code map} somehow contains entries with duplicate keys (for example, if
114090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * it is a {@code SortedMap} whose comparator is not <i>consistent with
115090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * equals</i>), the results of this method are undefined.
116090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
117090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * <p><b>Note:</b> Despite what the method name suggests, if {@code map} is
118090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * an {@code ImmutableClassToInstanceMap}, no copy will actually be performed.
119090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
120090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * @throws NullPointerException if any key or value in {@code map} is null
121090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * @throws ClassCastException if any value is not an instance of the type
122090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *     specified by its key
123090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
124090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  public static <B, S extends B> ImmutableClassToInstanceMap<B> copyOf(
125090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson      Map<? extends Class<? extends S>, ? extends S> map) {
126090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    if (map instanceof ImmutableClassToInstanceMap) {
1270888a09821a98ac0680fad765217302858e70fa4Paul Duffin      @SuppressWarnings("unchecked") // covariant casts safe (unmodifiable)
1281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      // Eclipse won't compile if we cast to the parameterized type.
1291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      ImmutableClassToInstanceMap<B> cast = (ImmutableClassToInstanceMap) map;
1301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      return cast;
131090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    }
132090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    return new Builder<B>().putAll(map).build();
133090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
134090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
135090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  private final ImmutableMap<Class<? extends B>, B> delegate;
136090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
1370888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private ImmutableClassToInstanceMap(
1380888a09821a98ac0680fad765217302858e70fa4Paul Duffin      ImmutableMap<Class<? extends B>, B> delegate) {
139090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    this.delegate = delegate;
140090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
141090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
1420888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @Override protected Map<Class<? extends B>, B> delegate() {
143090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    return delegate;
144090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
145090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
1460888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @Override
1470888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @SuppressWarnings("unchecked") // value could not get in if not a T
1487dd252788645e940eada959bdde927426e2531c9Paul Duffin  @Nullable
149090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  public <T extends B> T getInstance(Class<T> type) {
1507dd252788645e940eada959bdde927426e2531c9Paul Duffin    return (T) delegate.get(checkNotNull(type));
151090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
152090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson
153090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  /**
154090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * Guaranteed to throw an exception and leave the map unmodified.
155090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   *
156090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   * @throws UnsupportedOperationException always
1577dd252788645e940eada959bdde927426e2531c9Paul Duffin   * @deprecated Unsupported operation.
158090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson   */
1597dd252788645e940eada959bdde927426e2531c9Paul Duffin  @Deprecated
1600888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @Override
161090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  public <T extends B> T putInstance(Class<T> type, T value) {
162090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson    throw new UnsupportedOperationException();
163090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson  }
164090f9b4c879985bc747c214f82c62471e60c7742Jesse Wilson}
165