001/* 002 * Copyright (C) 2009 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.google.common.collect; 018 019import static com.google.common.base.Preconditions.checkNotNull; 020 021import com.google.common.primitives.Primitives; 022 023import java.io.Serializable; 024import java.util.Map; 025 026import javax.annotation.Nullable; 027 028/** 029 * A class-to-instance map backed by an {@link ImmutableMap}. See also {@link 030 * MutableClassToInstanceMap}. 031 * 032 * @author Kevin Bourrillion 033 * @since 2.0 (imported from Google Collections Library) 034 */ 035public final class ImmutableClassToInstanceMap<B> 036 extends ForwardingMap<Class<? extends B>, B> 037 implements ClassToInstanceMap<B>, Serializable { 038 039 /** 040 * Returns a new builder. The generated builder is equivalent to the builder 041 * created by the {@link Builder} constructor. 042 */ 043 public static <B> Builder<B> builder() { 044 return new Builder<B>(); 045 } 046 047 /** 048 * A builder for creating immutable class-to-instance maps. Example: 049 * <pre> {@code 050 * 051 * static final ImmutableClassToInstanceMap<Handler> HANDLERS = 052 * new ImmutableClassToInstanceMap.Builder<Handler>() 053 * .put(FooHandler.class, new FooHandler()) 054 * .put(BarHandler.class, new SubBarHandler()) 055 * .put(Handler.class, new QuuxHandler()) 056 * .build();}</pre> 057 * 058 * <p>After invoking {@link #build()} it is still possible to add more entries 059 * and build again. Thus each map generated by this builder will be a superset 060 * of any map generated before it. 061 * 062 * @since 2.0 (imported from Google Collections Library) 063 */ 064 public static final class Builder<B> { 065 private final ImmutableMap.Builder<Class<? extends B>, B> mapBuilder 066 = ImmutableMap.builder(); 067 068 /** 069 * Associates {@code key} with {@code value} in the built map. Duplicate 070 * keys are not allowed, and will cause {@link #build} to fail. 071 */ 072 public <T extends B> Builder<B> put(Class<T> key, T value) { 073 mapBuilder.put(key, value); 074 return this; 075 } 076 077 /** 078 * Associates all of {@code map's} keys and values in the built map. 079 * Duplicate keys are not allowed, and will cause {@link #build} to fail. 080 * 081 * @throws NullPointerException if any key or value in {@code map} is null 082 * @throws ClassCastException if any value is not an instance of the type 083 * specified by its key 084 */ 085 public <T extends B> Builder<B> putAll( 086 Map<? extends Class<? extends T>, ? extends T> map) { 087 for (Entry<? extends Class<? extends T>, ? extends T> entry 088 : map.entrySet()) { 089 Class<? extends T> type = entry.getKey(); 090 T value = entry.getValue(); 091 mapBuilder.put(type, cast(type, value)); 092 } 093 return this; 094 } 095 096 private static <B, T extends B> T cast(Class<T> type, B value) { 097 return Primitives.wrap(type).cast(value); 098 } 099 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}