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