1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file or at 6 // https://developers.google.com/open-source/licenses/bsd 7 8 package com.google.protobuf; 9 10 import static com.google.protobuf.Internal.checkNotNull; 11 12 import com.google.protobuf.Internal.EnumLite; 13 import java.util.Arrays; 14 import java.util.Collections; 15 import java.util.LinkedHashMap; 16 import java.util.Map; 17 import java.util.Set; 18 19 /** 20 * Internal representation of map fields in generated lite-runtime messages. 21 * 22 * <p>This class is a protobuf implementation detail. Users shouldn't use this class directly. 23 */ 24 public final class MapFieldLite<K, V> extends LinkedHashMap<K, V> { 25 26 private boolean isMutable; 27 MapFieldLite()28 private MapFieldLite() { 29 this.isMutable = true; 30 } 31 MapFieldLite(Map<K, V> mapData)32 private MapFieldLite(Map<K, V> mapData) { 33 super(mapData); 34 this.isMutable = true; 35 } 36 37 private static final MapFieldLite<?, ?> EMPTY_MAP_FIELD = new MapFieldLite<>(); 38 39 static { EMPTY_MAP_FIELD.makeImmutable()40 EMPTY_MAP_FIELD.makeImmutable(); 41 } 42 43 /** Returns a singleton immutable empty MapFieldLite instance. */ 44 @SuppressWarnings("unchecked") emptyMapField()45 public static <K, V> MapFieldLite<K, V> emptyMapField() { 46 return (MapFieldLite<K, V>) EMPTY_MAP_FIELD; 47 } 48 mergeFrom(MapFieldLite<K, V> other)49 public void mergeFrom(MapFieldLite<K, V> other) { 50 ensureMutable(); 51 if (!other.isEmpty()) { 52 putAll(other); 53 } 54 } 55 56 @Override entrySet()57 public Set<Map.Entry<K, V>> entrySet() { 58 return isEmpty() ? Collections.<Map.Entry<K, V>>emptySet() : super.entrySet(); 59 } 60 61 @Override clear()62 public void clear() { 63 ensureMutable(); 64 super.clear(); 65 } 66 67 @Override put(K key, V value)68 public V put(K key, V value) { 69 ensureMutable(); 70 checkNotNull(key); 71 72 checkNotNull(value); 73 return super.put(key, value); 74 } 75 put(Map.Entry<K, V> entry)76 public V put(Map.Entry<K, V> entry) { 77 return put(entry.getKey(), entry.getValue()); 78 } 79 80 @Override putAll(Map<? extends K, ? extends V> m)81 public void putAll(Map<? extends K, ? extends V> m) { 82 ensureMutable(); 83 checkForNullKeysAndValues(m); 84 super.putAll(m); 85 } 86 87 @Override remove(Object key)88 public V remove(Object key) { 89 ensureMutable(); 90 return super.remove(key); 91 } 92 checkForNullKeysAndValues(Map<?, ?> m)93 private static void checkForNullKeysAndValues(Map<?, ?> m) { 94 for (Object key : m.keySet()) { 95 checkNotNull(key); 96 checkNotNull(m.get(key)); 97 } 98 } 99 equals(Object a, Object b)100 private static boolean equals(Object a, Object b) { 101 if (a instanceof byte[] && b instanceof byte[]) { 102 return Arrays.equals((byte[]) a, (byte[]) b); 103 } 104 return a.equals(b); 105 } 106 107 /** 108 * Checks whether two {@link Map}s are equal. We don't use the default equals method of {@link 109 * Map} because it compares by identity not by content for byte arrays. 110 */ equals(Map<K, V> a, Map<K, V> b)111 static <K, V> boolean equals(Map<K, V> a, Map<K, V> b) { 112 if (a == b) { 113 return true; 114 } 115 if (a.size() != b.size()) { 116 return false; 117 } 118 for (Map.Entry<K, V> entry : a.entrySet()) { 119 if (!b.containsKey(entry.getKey())) { 120 return false; 121 } 122 if (!equals(entry.getValue(), b.get(entry.getKey()))) { 123 return false; 124 } 125 } 126 return true; 127 } 128 129 /** Checks whether two map fields are equal. */ 130 @SuppressWarnings("unchecked") 131 @Override equals(Object object)132 public boolean equals(Object object) { 133 return (object instanceof Map) && equals(this, (Map<K, V>) object); 134 } 135 calculateHashCodeForObject(Object a)136 private static int calculateHashCodeForObject(Object a) { 137 if (a instanceof byte[]) { 138 return Internal.hashCode((byte[]) a); 139 } 140 // Enums should be stored as integers internally. 141 if (a instanceof EnumLite) { 142 throw new UnsupportedOperationException(); 143 } 144 return a.hashCode(); 145 } 146 147 /** 148 * Calculates the hash code for a {@link Map}. We don't use the default hash code method of {@link 149 * Map} because for byte arrays and protobuf enums it use {@link Object#hashCode()}. 150 */ calculateHashCodeForMap(Map<K, V> a)151 static <K, V> int calculateHashCodeForMap(Map<K, V> a) { 152 int result = 0; 153 for (Map.Entry<K, V> entry : a.entrySet()) { 154 result += 155 calculateHashCodeForObject(entry.getKey()) ^ calculateHashCodeForObject(entry.getValue()); 156 } 157 return result; 158 } 159 160 @Override hashCode()161 public int hashCode() { 162 return calculateHashCodeForMap(this); 163 } 164 copy(Object object)165 private static Object copy(Object object) { 166 if (object instanceof byte[]) { 167 byte[] data = (byte[]) object; 168 return Arrays.copyOf(data, data.length); 169 } 170 return object; 171 } 172 173 /** 174 * Makes a deep copy of a {@link Map}. Immutable objects in the map will be shared (e.g., 175 * integers, strings, immutable messages) and mutable ones will have a copy (e.g., byte arrays, 176 * mutable messages). 177 */ 178 @SuppressWarnings("unchecked") copy(Map<K, V> map)179 static <K, V> Map<K, V> copy(Map<K, V> map) { 180 Map<K, V> result = new LinkedHashMap<K, V>(map.size() * 4 / 3 + 1); 181 for (Map.Entry<K, V> entry : map.entrySet()) { 182 result.put(entry.getKey(), (V) copy(entry.getValue())); 183 } 184 return result; 185 } 186 187 /** Returns a deep copy of this map field. */ mutableCopy()188 public MapFieldLite<K, V> mutableCopy() { 189 return isEmpty() ? new MapFieldLite<K, V>() : new MapFieldLite<K, V>(this); 190 } 191 192 /** 193 * Makes this field immutable. All subsequent modifications will throw an {@link 194 * UnsupportedOperationException}. 195 */ makeImmutable()196 public void makeImmutable() { 197 isMutable = false; 198 } 199 200 /** Returns whether this field can be modified. */ isMutable()201 public boolean isMutable() { 202 return isMutable; 203 } 204 ensureMutable()205 private void ensureMutable() { 206 if (!isMutable()) { 207 throw new UnsupportedOperationException(); 208 } 209 } 210 } 211