001/* 002 * Copyright (C) 2007 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.annotations.GwtIncompatible; 022import com.google.common.primitives.Primitives; 023import com.google.errorprone.annotations.CanIgnoreReturnValue; 024import java.io.Serializable; 025import java.util.HashMap; 026import java.util.Iterator; 027import java.util.LinkedHashMap; 028import java.util.Map; 029import java.util.Set; 030 031/** 032 * A mutable class-to-instance map backed by an arbitrary user-provided map. See also {@link 033 * ImmutableClassToInstanceMap}. 034 * 035 * <p>See the Guava User Guide article on <a href= 036 * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#classtoinstancemap"> {@code 037 * ClassToInstanceMap}</a>. 038 * 039 * @author Kevin Bourrillion 040 * @since 2.0 041 */ 042@GwtIncompatible 043@SuppressWarnings("serial") // using writeReplace instead of standard serialization 044public final class MutableClassToInstanceMap<B> extends ForwardingMap<Class<? extends B>, B> 045 implements ClassToInstanceMap<B>, Serializable { 046 047 /** 048 * Returns a new {@code MutableClassToInstanceMap} instance backed by a {@link 049 * HashMap} using the default initial capacity and load factor. 050 */ 051 public static <B> MutableClassToInstanceMap<B> create() { 052 return new MutableClassToInstanceMap<B>(new HashMap<Class<? extends B>, B>()); 053 } 054 055 /** 056 * Returns a new {@code MutableClassToInstanceMap} instance backed by a given 057 * empty {@code backingMap}. The caller surrenders control of the backing map, 058 * and thus should not allow any direct references to it to remain accessible. 059 */ 060 public static <B> MutableClassToInstanceMap<B> create(Map<Class<? extends B>, B> backingMap) { 061 return new MutableClassToInstanceMap<B>(backingMap); 062 } 063 064 private final Map<Class<? extends B>, B> delegate; 065 066 private MutableClassToInstanceMap(Map<Class<? extends B>, B> delegate) { 067 this.delegate = checkNotNull(delegate); 068 } 069 070 @Override 071 protected Map<Class<? extends B>, B> delegate() { 072 return delegate; 073 } 074 075 static <B> Entry<Class<? extends B>, B> checkedEntry(final Entry<Class<? extends B>, B> entry) { 076 return new ForwardingMapEntry<Class<? extends B>, B>() { 077 @Override 078 protected Entry<Class<? extends B>, B> delegate() { 079 return entry; 080 } 081 082 @Override 083 public B setValue(B value) { 084 return super.setValue(cast(getKey(), value)); 085 } 086 }; 087 } 088 089 @Override 090 public Set<Entry<Class<? extends B>, B>> entrySet() { 091 return new ForwardingSet<Entry<Class<? extends B>, B>>() { 092 093 @Override 094 protected Set<Entry<Class<? extends B>, B>> delegate() { 095 return MutableClassToInstanceMap.this.delegate().entrySet(); 096 } 097 098 @Override 099 public Iterator<Entry<Class<? extends B>, B>> iterator() { 100 return new TransformedIterator<Entry<Class<? extends B>, B>, Entry<Class<? extends B>, B>>( 101 delegate().iterator()) { 102 @Override 103 Entry<Class<? extends B>, B> transform(Entry<Class<? extends B>, B> from) { 104 return checkedEntry(from); 105 } 106 }; 107 } 108 109 @Override 110 public Object[] toArray() { 111 return standardToArray(); 112 } 113 114 @Override 115 public <T> T[] toArray(T[] array) { 116 return standardToArray(array); 117 } 118 }; 119 } 120 121 @Override 122 @CanIgnoreReturnValue 123 public B put(Class<? extends B> key, B value) { 124 return super.put(key, cast(key, value)); 125 } 126 127 @Override 128 public void putAll(Map<? extends Class<? extends B>, ? extends B> map) { 129 Map<Class<? extends B>, B> copy = new LinkedHashMap<Class<? extends B>, B>(map); 130 for (Entry<? extends Class<? extends B>, B> entry : copy.entrySet()) { 131 cast(entry.getKey(), entry.getValue()); 132 } 133 super.putAll(copy); 134 } 135 136 @CanIgnoreReturnValue 137 @Override 138 public <T extends B> T putInstance(Class<T> type, T value) { 139 return cast(type, put(type, value)); 140 } 141 142 @Override 143 public <T extends B> T getInstance(Class<T> type) { 144 return cast(type, get(type)); 145 } 146 147 @CanIgnoreReturnValue 148 private static <B, T extends B> T cast(Class<T> type, B value) { 149 return Primitives.wrap(type).cast(value); 150 } 151 152 private Object writeReplace() { 153 return new SerializedForm(delegate()); 154 } 155 156 /** 157 * Serialized form of the map, to avoid serializing the constraint. 158 */ 159 private static final class SerializedForm<B> implements Serializable { 160 private final Map<Class<? extends B>, B> backingMap; 161 162 SerializedForm(Map<Class<? extends B>, B> backingMap) { 163 this.backingMap = backingMap; 164 } 165 166 Object readResolve() { 167 return create(backingMap); 168 } 169 170 private static final long serialVersionUID = 0; 171 } 172}