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 017 package com.google.common.collect; 018 019 import com.google.common.primitives.Primitives; 020 021 import java.util.Map; 022 023 /** 024 * A class-to-instance map backed by an {@link ImmutableMap}. See also {@link 025 * MutableClassToInstanceMap}. 026 * 027 * @author Kevin Bourrillion 028 * @since 2.0 (imported from Google Collections Library) 029 */ 030 public final class ImmutableClassToInstanceMap<B> extends 031 ForwardingMap<Class<? extends B>, B> implements ClassToInstanceMap<B> { 032 /** 033 * Returns a new builder. The generated builder is equivalent to the builder 034 * created by the {@link Builder} constructor. 035 */ 036 public static <B> Builder<B> builder() { 037 return new Builder<B>(); 038 } 039 040 /** 041 * A builder for creating immutable class-to-instance maps. Example: 042 * <pre> {@code 043 * 044 * static final ImmutableClassToInstanceMap<Handler> HANDLERS = 045 * new ImmutableClassToInstanceMap.Builder<Handler>() 046 * .put(FooHandler.class, new FooHandler()) 047 * .put(BarHandler.class, new SubBarHandler()) 048 * .put(Handler.class, new QuuxHandler()) 049 * .build();}</pre> 050 * 051 * After invoking {@link #build()} it is still possible to add more entries 052 * and build again. Thus each map generated by this builder will be a superset 053 * of any map generated before it. 054 * 055 * @since 2.0 (imported from Google Collections Library) 056 */ 057 public static final class Builder<B> { 058 private final ImmutableMap.Builder<Class<? extends B>, B> mapBuilder 059 = ImmutableMap.builder(); 060 061 /** 062 * Associates {@code key} with {@code value} in the built map. Duplicate 063 * keys are not allowed, and will cause {@link #build} to fail. 064 */ 065 public <T extends B> Builder<B> put(Class<T> key, T value) { 066 mapBuilder.put(key, value); 067 return this; 068 } 069 070 /** 071 * Associates all of {@code map's} keys and values in the built map. 072 * Duplicate keys are not allowed, and will cause {@link #build} to fail. 073 * 074 * @throws NullPointerException if any key or value in {@code map} is null 075 * @throws ClassCastException if any value is not an instance of the type 076 * specified by its key 077 */ 078 public <T extends B> Builder<B> putAll( 079 Map<? extends Class<? extends T>, ? extends T> map) { 080 for (Entry<? extends Class<? extends T>, ? extends T> entry 081 : map.entrySet()) { 082 Class<? extends T> type = entry.getKey(); 083 T value = entry.getValue(); 084 mapBuilder.put(type, cast(type, value)); 085 } 086 return this; 087 } 088 089 private static <B, T extends B> T cast(Class<T> type, B value) { 090 return Primitives.wrap(type).cast(value); 091 } 092 093 /** 094 * Returns a new immutable class-to-instance map containing the entries 095 * provided to this builder. 096 * 097 * @throws IllegalArgumentException if duplicate keys were added 098 */ 099 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 }