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.annotations.J2ktIncompatible; 023import com.google.common.primitives.Primitives; 024import com.google.errorprone.annotations.CanIgnoreReturnValue; 025import java.io.InvalidObjectException; 026import java.io.ObjectInputStream; 027import java.io.Serializable; 028import java.util.HashMap; 029import java.util.Iterator; 030import java.util.LinkedHashMap; 031import java.util.Map; 032import java.util.Set; 033import javax.annotation.CheckForNull; 034import org.checkerframework.checker.nullness.qual.NonNull; 035import org.checkerframework.checker.nullness.qual.Nullable; 036 037/** 038 * A mutable class-to-instance map backed by an arbitrary user-provided map. See also {@link 039 * ImmutableClassToInstanceMap}. 040 * 041 * <p>See the Guava User Guide article on <a href= 042 * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#classtoinstancemap">{@code 043 * ClassToInstanceMap}</a>. 044 * 045 * @author Kevin Bourrillion 046 * @since 2.0 047 */ 048@J2ktIncompatible 049@GwtIncompatible 050@SuppressWarnings("serial") // using writeReplace instead of standard serialization 051@ElementTypesAreNonnullByDefault 052public final class MutableClassToInstanceMap<B extends @Nullable Object> 053 extends ForwardingMap<Class<? extends @NonNull B>, B> 054 implements ClassToInstanceMap<B>, Serializable { 055 056 /** 057 * Returns a new {@code MutableClassToInstanceMap} instance backed by a {@link HashMap} using the 058 * default initial capacity and load factor. 059 */ 060 public static <B extends @Nullable Object> MutableClassToInstanceMap<B> create() { 061 return new MutableClassToInstanceMap<B>(new HashMap<Class<? extends @NonNull B>, B>()); 062 } 063 064 /** 065 * Returns a new {@code MutableClassToInstanceMap} instance backed by a given empty {@code 066 * backingMap}. The caller surrenders control of the backing map, and thus should not allow any 067 * direct references to it to remain accessible. 068 */ 069 public static <B extends @Nullable Object> MutableClassToInstanceMap<B> create( 070 Map<Class<? extends @NonNull B>, B> backingMap) { 071 return new MutableClassToInstanceMap<B>(backingMap); 072 } 073 074 private final Map<Class<? extends @NonNull B>, B> delegate; 075 076 private MutableClassToInstanceMap(Map<Class<? extends @NonNull B>, B> delegate) { 077 this.delegate = checkNotNull(delegate); 078 } 079 080 @Override 081 protected Map<Class<? extends @NonNull B>, B> delegate() { 082 return delegate; 083 } 084 085 /** 086 * Wraps the {@code setValue} implementation of an {@code Entry} to enforce the class constraint. 087 */ 088 private static <B extends @Nullable Object> Entry<Class<? extends @NonNull B>, B> checkedEntry( 089 final Entry<Class<? extends @NonNull B>, B> entry) { 090 return new ForwardingMapEntry<Class<? extends @NonNull B>, B>() { 091 @Override 092 protected Entry<Class<? extends @NonNull B>, B> delegate() { 093 return entry; 094 } 095 096 @Override 097 @ParametricNullness 098 public B setValue(@ParametricNullness B value) { 099 cast(getKey(), value); 100 return super.setValue(value); 101 } 102 }; 103 } 104 105 @Override 106 public Set<Entry<Class<? extends @NonNull B>, B>> entrySet() { 107 return new ForwardingSet<Entry<Class<? extends @NonNull B>, B>>() { 108 109 @Override 110 protected Set<Entry<Class<? extends @NonNull B>, B>> delegate() { 111 return MutableClassToInstanceMap.this.delegate().entrySet(); 112 } 113 114 @Override 115 public Iterator<Entry<Class<? extends @NonNull B>, B>> iterator() { 116 return new TransformedIterator< 117 Entry<Class<? extends @NonNull B>, B>, Entry<Class<? extends @NonNull B>, B>>( 118 delegate().iterator()) { 119 @Override 120 Entry<Class<? extends @NonNull B>, B> transform( 121 Entry<Class<? extends @NonNull B>, B> from) { 122 return checkedEntry(from); 123 } 124 }; 125 } 126 127 @Override 128 public Object[] toArray() { 129 /* 130 * standardToArray returns `@Nullable Object[]` rather than `Object[]` but only because it 131 * can be used with collections that may contain null. This collection is a collection of 132 * non-null Entry objects (Entry objects that might contain null values but are not 133 * themselves null), so we can treat it as a plain `Object[]`. 134 */ 135 @SuppressWarnings("nullness") 136 Object[] result = standardToArray(); 137 return result; 138 } 139 140 @Override 141 @SuppressWarnings("nullness") // b/192354773 in our checker affects toArray declarations 142 public <T extends @Nullable Object> T[] toArray(T[] array) { 143 return standardToArray(array); 144 } 145 }; 146 } 147 148 @Override 149 @CanIgnoreReturnValue 150 @CheckForNull 151 public B put(Class<? extends @NonNull B> key, @ParametricNullness B value) { 152 cast(key, value); 153 return super.put(key, value); 154 } 155 156 @Override 157 public void putAll(Map<? extends Class<? extends @NonNull B>, ? extends B> map) { 158 Map<Class<? extends @NonNull B>, B> copy = new LinkedHashMap<>(map); 159 for (Entry<? extends Class<? extends @NonNull B>, B> entry : copy.entrySet()) { 160 cast(entry.getKey(), entry.getValue()); 161 } 162 super.putAll(copy); 163 } 164 165 @CanIgnoreReturnValue 166 @Override 167 @CheckForNull 168 public <T extends B> T putInstance(Class<@NonNull T> type, @ParametricNullness T value) { 169 return cast(type, put(type, value)); 170 } 171 172 @Override 173 @CheckForNull 174 public <T extends @NonNull B> T getInstance(Class<T> type) { 175 return cast(type, get(type)); 176 } 177 178 @CanIgnoreReturnValue 179 @CheckForNull 180 private static <T> T cast(Class<T> type, @CheckForNull Object value) { 181 return Primitives.wrap(type).cast(value); 182 } 183 184 private Object writeReplace() { 185 return new SerializedForm<>(delegate()); 186 } 187 188 private void readObject(ObjectInputStream stream) throws InvalidObjectException { 189 throw new InvalidObjectException("Use SerializedForm"); 190 } 191 192 /** Serialized form of the map, to avoid serializing the constraint. */ 193 private static final class SerializedForm<B extends @Nullable Object> implements Serializable { 194 private final Map<Class<? extends @NonNull B>, B> backingMap; 195 196 SerializedForm(Map<Class<? extends @NonNull B>, B> backingMap) { 197 this.backingMap = backingMap; 198 } 199 200 Object readResolve() { 201 return create(backingMap); 202 } 203 204 private static final long serialVersionUID = 0; 205 } 206}