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 com.google.common.primitives.Primitives; 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 2.0 (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 */ 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 * After invoking {@link #build()} it is still possible to add more entries 52 * and build again. Thus each map generated by this builder will be a superset 53 * of any map generated before it. 54 * 55 * @since 2.0 (imported from Google Collections Library) 56 */ 57 public static final class Builder<B> { 58 private final ImmutableMap.Builder<Class<? extends B>, B> mapBuilder 59 = ImmutableMap.builder(); 60 61 /** 62 * Associates {@code key} with {@code value} in the built map. Duplicate 63 * keys are not allowed, and will cause {@link #build} to fail. 64 */ 65 public <T extends B> Builder<B> put(Class<T> key, T value) { 66 mapBuilder.put(key, value); 67 return this; 68 } 69 70 /** 71 * Associates all of {@code map's} keys and values in the built map. 72 * Duplicate keys are not allowed, and will cause {@link #build} to fail. 73 * 74 * @throws NullPointerException if any key or value in {@code map} is null 75 * @throws ClassCastException if any value is not an instance of the type 76 * specified by its key 77 */ 78 public <T extends B> Builder<B> putAll( 79 Map<? extends Class<? extends T>, ? extends T> map) { 80 for (Entry<? extends Class<? extends T>, ? extends T> entry 81 : map.entrySet()) { 82 Class<? extends T> type = entry.getKey(); 83 T value = entry.getValue(); 84 mapBuilder.put(type, cast(type, value)); 85 } 86 return this; 87 } 88 89 private static <B, T extends B> T cast(Class<T> type, B value) { 90 return Primitives.wrap(type).cast(value); 91 } 92 93 /** 94 * Returns a new immutable class-to-instance map containing the entries 95 * provided to this builder. 96 * 97 * @throws IllegalArgumentException if duplicate keys were added 98 */ 99 public ImmutableClassToInstanceMap<B> build() { 100 return new ImmutableClassToInstanceMap<B>(mapBuilder.build()); 101 } 102 } 103 104 /** 105 * Returns an immutable map containing the same entries as {@code map}. If 106 * {@code map} somehow contains entries with duplicate keys (for example, if 107 * it is a {@code SortedMap} whose comparator is not <i>consistent with 108 * equals</i>), the results of this method are undefined. 109 * 110 * <p><b>Note:</b> Despite what the method name suggests, if {@code map} is 111 * an {@code ImmutableClassToInstanceMap}, no copy will actually be performed. 112 * 113 * @throws NullPointerException if any key or value in {@code map} is null 114 * @throws ClassCastException if any value is not an instance of the type 115 * specified by its key 116 */ 117 public static <B, S extends B> ImmutableClassToInstanceMap<B> copyOf( 118 Map<? extends Class<? extends S>, ? extends S> map) { 119 if (map instanceof ImmutableClassToInstanceMap) { 120 @SuppressWarnings("unchecked") // covariant casts safe (unmodifiable) 121 // Eclipse won't compile if we cast to the parameterized type. 122 ImmutableClassToInstanceMap<B> cast = (ImmutableClassToInstanceMap) map; 123 return cast; 124 } 125 return new Builder<B>().putAll(map).build(); 126 } 127 128 private final ImmutableMap<Class<? extends B>, B> delegate; 129 130 private ImmutableClassToInstanceMap( 131 ImmutableMap<Class<? extends B>, B> delegate) { 132 this.delegate = delegate; 133 } 134 135 @Override protected Map<Class<? extends B>, B> delegate() { 136 return delegate; 137 } 138 139 @Override 140 @SuppressWarnings("unchecked") // value could not get in if not a T 141 public <T extends B> T getInstance(Class<T> type) { 142 return (T) delegate.get(type); 143 } 144 145 /** 146 * Guaranteed to throw an exception and leave the map unmodified. 147 * 148 * @throws UnsupportedOperationException always 149 */ 150 @Override 151 public <T extends B> T putInstance(Class<T> type, T value) { 152 throw new UnsupportedOperationException(); 153 } 154 } 155