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.util.Map; 024 025import javax.annotation.Nullable; 026 027/** 028 * A class-to-instance map backed by an {@link ImmutableMap}. See also {@link 029 * MutableClassToInstanceMap}. 030 * 031 * @author Kevin Bourrillion 032 * @since 2.0 (imported from Google Collections Library) 033 */ 034public final class ImmutableClassToInstanceMap<B> extends 035 ForwardingMap<Class<? extends B>, B> implements ClassToInstanceMap<B> { 036 /** 037 * Returns a new builder. The generated builder is equivalent to the builder 038 * created by the {@link Builder} constructor. 039 */ 040 public static <B> Builder<B> builder() { 041 return new Builder<B>(); 042 } 043 044 /** 045 * A builder for creating immutable class-to-instance maps. Example: 046 * <pre> {@code 047 * 048 * static final ImmutableClassToInstanceMap<Handler> HANDLERS = 049 * new ImmutableClassToInstanceMap.Builder<Handler>() 050 * .put(FooHandler.class, new FooHandler()) 051 * .put(BarHandler.class, new SubBarHandler()) 052 * .put(Handler.class, new QuuxHandler()) 053 * .build();}</pre> 054 * 055 * After invoking {@link #build()} it is still possible to add more entries 056 * and build again. Thus each map generated by this builder will be a superset 057 * of any map generated before it. 058 * 059 * @since 2.0 (imported from Google Collections Library) 060 */ 061 public static final class Builder<B> { 062 private final ImmutableMap.Builder<Class<? extends B>, B> mapBuilder 063 = ImmutableMap.builder(); 064 065 /** 066 * Associates {@code key} with {@code value} in the built map. Duplicate 067 * keys are not allowed, and will cause {@link #build} to fail. 068 */ 069 public <T extends B> Builder<B> put(Class<T> key, T value) { 070 mapBuilder.put(key, value); 071 return this; 072 } 073 074 /** 075 * Associates all of {@code map's} keys and values in the built map. 076 * Duplicate keys are not allowed, and will cause {@link #build} to fail. 077 * 078 * @throws NullPointerException if any key or value in {@code map} is null 079 * @throws ClassCastException if any value is not an instance of the type 080 * specified by its key 081 */ 082 public <T extends B> Builder<B> putAll( 083 Map<? extends Class<? extends T>, ? extends T> map) { 084 for (Entry<? extends Class<? extends T>, ? extends T> entry 085 : map.entrySet()) { 086 Class<? extends T> type = entry.getKey(); 087 T value = entry.getValue(); 088 mapBuilder.put(type, cast(type, value)); 089 } 090 return this; 091 } 092 093 private static <B, T extends B> T cast(Class<T> type, B value) { 094 return Primitives.wrap(type).cast(value); 095 } 096 097 /** 098 * Returns a new immutable class-to-instance map containing the entries 099 * provided to this builder. 100 * 101 * @throws IllegalArgumentException if duplicate keys were added 102 */ 103 public ImmutableClassToInstanceMap<B> build() { 104 return new ImmutableClassToInstanceMap<B>(mapBuilder.build()); 105 } 106 } 107 108 /** 109 * Returns an immutable map containing the same entries as {@code map}. If 110 * {@code map} somehow contains entries with duplicate keys (for example, if 111 * it is a {@code SortedMap} whose comparator is not <i>consistent with 112 * equals</i>), the results of this method are undefined. 113 * 114 * <p><b>Note:</b> Despite what the method name suggests, if {@code map} is 115 * an {@code ImmutableClassToInstanceMap}, no copy will actually be performed. 116 * 117 * @throws NullPointerException if any key or value in {@code map} is null 118 * @throws ClassCastException if any value is not an instance of the type 119 * specified by its key 120 */ 121 public static <B, S extends B> ImmutableClassToInstanceMap<B> copyOf( 122 Map<? extends Class<? extends S>, ? extends S> map) { 123 if (map instanceof ImmutableClassToInstanceMap) { 124 @SuppressWarnings("unchecked") // covariant casts safe (unmodifiable) 125 // Eclipse won't compile if we cast to the parameterized type. 126 ImmutableClassToInstanceMap<B> cast = (ImmutableClassToInstanceMap) map; 127 return cast; 128 } 129 return new Builder<B>().putAll(map).build(); 130 } 131 132 private final ImmutableMap<Class<? extends B>, B> delegate; 133 134 private ImmutableClassToInstanceMap( 135 ImmutableMap<Class<? extends B>, B> delegate) { 136 this.delegate = delegate; 137 } 138 139 @Override protected Map<Class<? extends B>, B> delegate() { 140 return delegate; 141 } 142 143 @Override 144 @SuppressWarnings("unchecked") // value could not get in if not a T 145 @Nullable 146 public <T extends B> T getInstance(Class<T> type) { 147 return (T) delegate.get(checkNotNull(type)); 148 } 149 150 /** 151 * Guaranteed to throw an exception and leave the map unmodified. 152 * 153 * @throws UnsupportedOperationException always 154 * @deprecated Unsupported operation. 155 */ 156 @Deprecated 157 @Override 158 public <T extends B> T putInstance(Class<T> type, T value) { 159 throw new UnsupportedOperationException(); 160 } 161}