1 /* 2 * Copyright (C) 2009 The Guava Authors 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.base.Preconditions.checkNotNull; 20 21 import com.google.common.primitives.Primitives; 22 23 import java.io.Serializable; 24 import java.util.Map; 25 26 import javax.annotation.Nullable; 27 28 /** 29 * A class-to-instance map backed by an {@link ImmutableMap}. See also {@link 30 * MutableClassToInstanceMap}. 31 * 32 * @author Kevin Bourrillion 33 * @since 2.0 (imported from Google Collections Library) 34 */ 35 public final class ImmutableClassToInstanceMap<B> 36 extends ForwardingMap<Class<? extends B>, B> 37 implements ClassToInstanceMap<B>, Serializable { 38 39 /** 40 * Returns a new builder. The generated builder is equivalent to the builder 41 * created by the {@link Builder} constructor. 42 */ 43 public static <B> Builder<B> builder() { 44 return new Builder<B>(); 45 } 46 47 /** 48 * A builder for creating immutable class-to-instance maps. Example: 49 * <pre> {@code 50 * 51 * static final ImmutableClassToInstanceMap<Handler> HANDLERS = 52 * new ImmutableClassToInstanceMap.Builder<Handler>() 53 * .put(FooHandler.class, new FooHandler()) 54 * .put(BarHandler.class, new SubBarHandler()) 55 * .put(Handler.class, new QuuxHandler()) 56 * .build();}</pre> 57 * 58 * <p>After invoking {@link #build()} it is still possible to add more entries 59 * and build again. Thus each map generated by this builder will be a superset 60 * of any map generated before it. 61 * 62 * @since 2.0 (imported from Google Collections Library) 63 */ 64 public static final class Builder<B> { 65 private final ImmutableMap.Builder<Class<? extends B>, B> mapBuilder 66 = ImmutableMap.builder(); 67 68 /** 69 * Associates {@code key} with {@code value} in the built map. Duplicate 70 * keys are not allowed, and will cause {@link #build} to fail. 71 */ 72 public <T extends B> Builder<B> put(Class<T> key, T value) { 73 mapBuilder.put(key, value); 74 return this; 75 } 76 77 /** 78 * Associates all of {@code map's} keys and values in the built map. 79 * Duplicate keys are not allowed, and will cause {@link #build} to fail. 80 * 81 * @throws NullPointerException if any key or value in {@code map} is null 82 * @throws ClassCastException if any value is not an instance of the type 83 * specified by its key 84 */ 85 public <T extends B> Builder<B> putAll( 86 Map<? extends Class<? extends T>, ? extends T> map) { 87 for (Entry<? extends Class<? extends T>, ? extends T> entry 88 : map.entrySet()) { 89 Class<? extends T> type = entry.getKey(); 90 T value = entry.getValue(); 91 mapBuilder.put(type, cast(type, value)); 92 } 93 return this; 94 } 95 96 private static <B, T extends B> T cast(Class<T> type, B value) { 97 return Primitives.wrap(type).cast(value); 98 } 99 100 /** 101 * Returns a new immutable class-to-instance map containing the entries 102 * provided to this builder. 103 * 104 * @throws IllegalArgumentException if duplicate keys were added 105 */ 106 public ImmutableClassToInstanceMap<B> build() { 107 return new ImmutableClassToInstanceMap<B>(mapBuilder.build()); 108 } 109 } 110 111 /** 112 * Returns an immutable map containing the same entries as {@code map}. If 113 * {@code map} somehow contains entries with duplicate keys (for example, if 114 * it is a {@code SortedMap} whose comparator is not <i>consistent with 115 * equals</i>), the results of this method are undefined. 116 * 117 * <p><b>Note:</b> Despite what the method name suggests, if {@code map} is 118 * an {@code ImmutableClassToInstanceMap}, no copy will actually be performed. 119 * 120 * @throws NullPointerException if any key or value in {@code map} is null 121 * @throws ClassCastException if any value is not an instance of the type 122 * specified by its key 123 */ 124 public static <B, S extends B> ImmutableClassToInstanceMap<B> copyOf( 125 Map<? extends Class<? extends S>, ? extends S> map) { 126 if (map instanceof ImmutableClassToInstanceMap) { 127 @SuppressWarnings("unchecked") // covariant casts safe (unmodifiable) 128 // Eclipse won't compile if we cast to the parameterized type. 129 ImmutableClassToInstanceMap<B> cast = (ImmutableClassToInstanceMap) map; 130 return cast; 131 } 132 return new Builder<B>().putAll(map).build(); 133 } 134 135 private final ImmutableMap<Class<? extends B>, B> delegate; 136 137 private ImmutableClassToInstanceMap( 138 ImmutableMap<Class<? extends B>, B> delegate) { 139 this.delegate = delegate; 140 } 141 142 @Override protected Map<Class<? extends B>, B> delegate() { 143 return delegate; 144 } 145 146 @Override 147 @SuppressWarnings("unchecked") // value could not get in if not a T 148 @Nullable 149 public <T extends B> T getInstance(Class<T> type) { 150 return (T) delegate.get(checkNotNull(type)); 151 } 152 153 /** 154 * Guaranteed to throw an exception and leave the map unmodified. 155 * 156 * @throws UnsupportedOperationException always 157 * @deprecated Unsupported operation. 158 */ 159 @Deprecated 160 @Override 161 public <T extends B> T putInstance(Class<T> type, T value) { 162 throw new UnsupportedOperationException(); 163 } 164 } 165