001 /* 002 * Copyright (C) 2009 Google Inc. 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 (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 * <p>After invoking {@link #build()} it is still possible to add more 052 * entries and build again. Thus each map generated by this builder will be 053 * a superset of any map generated before it. 054 */ 055 public static final class Builder<B> { 056 private final ImmutableMap.Builder<Class<? extends B>, B> mapBuilder 057 = ImmutableMap.builder(); 058 059 /** 060 * Associates {@code key} with {@code value} in the built map. Duplicate 061 * keys are not allowed, and will cause {@link #build} to fail. 062 */ 063 public <T extends B> Builder<B> put(Class<T> type, T value) { 064 mapBuilder.put(type, value); 065 return this; 066 } 067 068 /** 069 * Associates all of {@code map's} keys and values in the built map. 070 * Duplicate keys are not allowed, and will cause {@link #build} to fail. 071 * 072 * @throws NullPointerException if any key or value in {@code map} is null 073 * @throws ClassCastException if any value is not an instance of the type 074 * specified by its key 075 */ 076 public <T extends B> Builder<B> putAll( 077 Map<? extends Class<? extends T>, ? extends T> map) { 078 for (Entry<? extends Class<? extends T>, ? extends T> entry 079 : map.entrySet()) { 080 Class<? extends T> type = entry.getKey(); 081 T value = entry.getValue(); 082 mapBuilder.put(type, cast(type, value)); 083 } 084 return this; 085 } 086 087 private static <B, T extends B> T cast(Class<T> type, B value) { 088 return Primitives.wrap(type).cast(value); 089 } 090 091 /** 092 * Returns a new immutable class-to-instance map containing the entries 093 * provided to this builder. 094 * 095 * @throws IllegalArgumentException if duplicate keys were added 096 */ 097 public ImmutableClassToInstanceMap<B> build() { 098 return new ImmutableClassToInstanceMap<B>(mapBuilder.build()); 099 } 100 } 101 102 /** 103 * Returns an immutable map containing the same entries as {@code map}. If 104 * {@code map} somehow contains entries with duplicate keys (for example, if 105 * it is a {@code SortedMap} whose comparator is not <i>consistent with 106 * equals</i>), the results of this method are undefined. 107 * 108 * <p><b>Note:</b> Despite what the method name suggests, if {@code map} is 109 * an {@code ImmutableClassToInstanceMap}, no copy will actually be performed. 110 * 111 * @throws NullPointerException if any key or value in {@code map} is null 112 * @throws ClassCastException if any value is not an instance of the type 113 * specified by its key 114 */ 115 @SuppressWarnings("unchecked") // covariant casts safe (unmodifiable) 116 public static <B, S extends B> ImmutableClassToInstanceMap<B> copyOf( 117 Map<? extends Class<? extends S>, ? extends S> map) { 118 if (map instanceof ImmutableClassToInstanceMap) { 119 return (ImmutableClassToInstanceMap<B>) (Map) map; 120 } 121 return new Builder<B>().putAll(map).build(); 122 } 123 124 private final ImmutableMap<Class<? extends B>, B> delegate; 125 126 private ImmutableClassToInstanceMap( 127 ImmutableMap<Class<? extends B>, B> delegate) { 128 this.delegate = delegate; 129 } 130 131 @Override protected Map<Class<? extends B>, B> delegate() { 132 return delegate; 133 } 134 135 @SuppressWarnings("unchecked") // value could not get in if not a T 136 public <T extends B> T getInstance(Class<T> type) { 137 return (T) delegate.get(type); 138 } 139 140 /** 141 * Guaranteed to throw an exception and leave the map unmodified. 142 * 143 * @throws UnsupportedOperationException always 144 */ 145 public <T extends B> T putInstance(Class<T> type, T value) { 146 throw new UnsupportedOperationException(); 147 } 148 }