1 /* 2 * Copyright (C) 2009 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.collect; 18 19 import static com.google.common.collect.MutableClassToInstanceMap.cast; 20 21 import java.util.Map; 22 23 /** 24 * A class-to-instance map backed by an {@link ImmutableMap}. See also {@link 25 * MutableClassToInstanceMap}. 26 * 27 * @author Kevin Bourrillion 28 * @since 2010.01.04 <b>stable</b> (imported from Google Collections Library) 29 */ 30 public final class ImmutableClassToInstanceMap<B> extends 31 ForwardingMap<Class<? extends B>, B> implements ClassToInstanceMap<B> { 32 /** 33 * Returns a new builder. The generated builder is equivalent to the builder 34 * created by the {@link Builder} constructor. 35 */ builder()36 public static <B> Builder<B> builder() { 37 return new Builder<B>(); 38 } 39 40 /** 41 * A builder for creating immutable class-to-instance maps. Example: 42 * <pre> {@code 43 * 44 * static final ImmutableClassToInstanceMap<Handler> HANDLERS = 45 * new ImmutableClassToInstanceMap.Builder<Handler>() 46 * .put(FooHandler.class, new FooHandler()) 47 * .put(BarHandler.class, new SubBarHandler()) 48 * .put(Handler.class, new QuuxHandler()) 49 * .build();}</pre> 50 * 51 * <p>After invoking {@link #build()} it is still possible to add more 52 * entries and build again. Thus each map generated by this builder will be 53 * a superset of any map generated before it. 54 */ 55 public static final class Builder<B> { 56 private final ImmutableMap.Builder<Class<? extends B>, B> mapBuilder 57 = ImmutableMap.builder(); 58 59 /** 60 * Associates {@code key} with {@code value} in the built map. Duplicate 61 * keys are not allowed, and will cause {@link #build} to fail. 62 */ put(Class<T> type, T value)63 public <T extends B> Builder<B> put(Class<T> type, T value) { 64 mapBuilder.put(type, value); 65 return this; 66 } 67 68 /** 69 * Associates all of {@code map's} keys and values in the built map. 70 * Duplicate keys are not allowed, and will cause {@link #build} to fail. 71 * 72 * @throws NullPointerException if any key or value in {@code map} is null 73 * @throws ClassCastException if any value is not an instance of the type 74 * specified by its key 75 */ putAll( Map<? extends Class<? extends T>, ? extends T> map)76 public <T extends B> Builder<B> putAll( 77 Map<? extends Class<? extends T>, ? extends T> map) { 78 for (Entry<? extends Class<? extends T>, ? extends T> entry 79 : map.entrySet()) { 80 Class<? extends T> type = entry.getKey(); 81 T value = entry.getValue(); 82 mapBuilder.put(type, cast(type, value)); 83 } 84 return this; 85 } 86 87 /** 88 * Returns a new immutable class-to-instance map containing the entries 89 * provided to this builder. 90 * 91 * @throws IllegalArgumentException if duplicate keys were added 92 */ build()93 public ImmutableClassToInstanceMap<B> build() { 94 return new ImmutableClassToInstanceMap<B>(mapBuilder.build()); 95 } 96 } 97 98 /** 99 * Returns an immutable map containing the same entries as {@code map}. If 100 * {@code map} somehow contains entries with duplicate keys (for example, if 101 * it is a {@code SortedMap} whose comparator is not <i>consistent with 102 * equals</i>), the results of this method are undefined. 103 * 104 * <p><b>Note:</b> Despite what the method name suggests, if {@code map} is 105 * an {@code ImmutableClassToInstanceMap}, no copy will actually be performed. 106 * 107 * @throws NullPointerException if any key or value in {@code map} is null 108 * @throws ClassCastException if any value is not an instance of the type 109 * specified by its key 110 */ 111 @SuppressWarnings("unchecked") // covariant casts safe (unmodifiable) copyOf( Map<? extends Class<? extends S>, ? extends S> map)112 public static <B, S extends B> ImmutableClassToInstanceMap<B> copyOf( 113 Map<? extends Class<? extends S>, ? extends S> map) { 114 if (map instanceof ImmutableClassToInstanceMap) { 115 return (ImmutableClassToInstanceMap<B>) (Map) map; 116 } 117 return new Builder<B>().putAll(map).build(); 118 } 119 120 private final ImmutableMap<Class<? extends B>, B> delegate; 121 ImmutableClassToInstanceMap( ImmutableMap<Class<? extends B>, B> delegate)122 private ImmutableClassToInstanceMap( 123 ImmutableMap<Class<? extends B>, B> delegate) { 124 this.delegate = delegate; 125 } 126 delegate()127 @Override protected Map<Class<? extends B>, B> delegate() { 128 return delegate; 129 } 130 131 @SuppressWarnings("unchecked") // value could not get in if not a T getInstance(Class<T> type)132 public <T extends B> T getInstance(Class<T> type) { 133 return (T) delegate.get(type); 134 } 135 136 /** 137 * Guaranteed to throw an exception and leave the map unmodified. 138 * 139 * @throws UnsupportedOperationException always 140 */ putInstance(Class<T> type, T value)141 public <T extends B> T putInstance(Class<T> type, T value) { 142 throw new UnsupportedOperationException(); 143 } 144 } 145