1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // https://developers.google.com/protocol-buffers/ 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are 7 // met: 8 // 9 // * Redistributions of source code must retain the above copyright 10 // notice, this list of conditions and the following disclaimer. 11 // * Redistributions in binary form must reproduce the above 12 // copyright notice, this list of conditions and the following disclaimer 13 // in the documentation and/or other materials provided with the 14 // distribution. 15 // * Neither the name of Google Inc. nor the names of its 16 // contributors may be used to endorse or promote products derived from 17 // this software without specific prior written permission. 18 // 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 package com.google.protobuf; 32 33 import static com.google.protobuf.Internal.checkNotNull; 34 35 import com.google.protobuf.Internal.EnumLite; 36 import java.util.Arrays; 37 import java.util.Collections; 38 import java.util.LinkedHashMap; 39 import java.util.Map; 40 import java.util.Set; 41 42 /** 43 * Internal representation of map fields in generated lite-runtime messages. 44 * 45 * <p>This class is a protobuf implementation detail. Users shouldn't use this class directly. 46 */ 47 public final class MapFieldLite<K, V> extends LinkedHashMap<K, V> { 48 49 private boolean isMutable; 50 MapFieldLite()51 private MapFieldLite() { 52 this.isMutable = true; 53 } 54 MapFieldLite(Map<K, V> mapData)55 private MapFieldLite(Map<K, V> mapData) { 56 super(mapData); 57 this.isMutable = true; 58 } 59 60 @SuppressWarnings({"rawtypes", "unchecked"}) 61 private static final MapFieldLite EMPTY_MAP_FIELD = new MapFieldLite<>(); 62 63 static { EMPTY_MAP_FIELD.makeImmutable()64 EMPTY_MAP_FIELD.makeImmutable(); 65 } 66 67 /** Returns an singleton immutable empty MapFieldLite instance. */ 68 @SuppressWarnings({"unchecked", "cast"}) emptyMapField()69 public static <K, V> MapFieldLite<K, V> emptyMapField() { 70 return (MapFieldLite<K, V>) EMPTY_MAP_FIELD; 71 } 72 mergeFrom(MapFieldLite<K, V> other)73 public void mergeFrom(MapFieldLite<K, V> other) { 74 ensureMutable(); 75 if (!other.isEmpty()) { 76 putAll(other); 77 } 78 } 79 80 @SuppressWarnings({"unchecked", "cast"}) 81 @Override entrySet()82 public Set<Map.Entry<K, V>> entrySet() { 83 return isEmpty() ? Collections.<Map.Entry<K, V>>emptySet() : super.entrySet(); 84 } 85 86 @Override clear()87 public void clear() { 88 ensureMutable(); 89 super.clear(); 90 } 91 92 @Override put(K key, V value)93 public V put(K key, V value) { 94 ensureMutable(); 95 checkNotNull(key); 96 97 checkNotNull(value); 98 return super.put(key, value); 99 } 100 put(Map.Entry<K, V> entry)101 public V put(Map.Entry<K, V> entry) { 102 return put(entry.getKey(), entry.getValue()); 103 } 104 105 @Override putAll(Map<? extends K, ? extends V> m)106 public void putAll(Map<? extends K, ? extends V> m) { 107 ensureMutable(); 108 checkForNullKeysAndValues(m); 109 super.putAll(m); 110 } 111 112 @Override remove(Object key)113 public V remove(Object key) { 114 ensureMutable(); 115 return super.remove(key); 116 } 117 checkForNullKeysAndValues(Map<?, ?> m)118 private static void checkForNullKeysAndValues(Map<?, ?> m) { 119 for (Object key : m.keySet()) { 120 checkNotNull(key); 121 checkNotNull(m.get(key)); 122 } 123 } 124 equals(Object a, Object b)125 private static boolean equals(Object a, Object b) { 126 if (a instanceof byte[] && b instanceof byte[]) { 127 return Arrays.equals((byte[]) a, (byte[]) b); 128 } 129 return a.equals(b); 130 } 131 132 /** 133 * Checks whether two {@link Map}s are equal. We don't use the default equals method of {@link 134 * Map} because it compares by identity not by content for byte arrays. 135 */ equals(Map<K, V> a, Map<K, V> b)136 static <K, V> boolean equals(Map<K, V> a, Map<K, V> b) { 137 if (a == b) { 138 return true; 139 } 140 if (a.size() != b.size()) { 141 return false; 142 } 143 for (Map.Entry<K, V> entry : a.entrySet()) { 144 if (!b.containsKey(entry.getKey())) { 145 return false; 146 } 147 if (!equals(entry.getValue(), b.get(entry.getKey()))) { 148 return false; 149 } 150 } 151 return true; 152 } 153 154 /** Checks whether two map fields are equal. */ 155 @SuppressWarnings("unchecked") 156 @Override equals(Object object)157 public boolean equals(Object object) { 158 return (object instanceof Map) && equals(this, (Map<K, V>) object); 159 } 160 calculateHashCodeForObject(Object a)161 private static int calculateHashCodeForObject(Object a) { 162 if (a instanceof byte[]) { 163 return Internal.hashCode((byte[]) a); 164 } 165 // Enums should be stored as integers internally. 166 if (a instanceof EnumLite) { 167 throw new UnsupportedOperationException(); 168 } 169 return a.hashCode(); 170 } 171 172 /** 173 * Calculates the hash code for a {@link Map}. We don't use the default hash code method of {@link 174 * Map} because for byte arrays and protobuf enums it use {@link Object#hashCode()}. 175 */ calculateHashCodeForMap(Map<K, V> a)176 static <K, V> int calculateHashCodeForMap(Map<K, V> a) { 177 int result = 0; 178 for (Map.Entry<K, V> entry : a.entrySet()) { 179 result += 180 calculateHashCodeForObject(entry.getKey()) ^ calculateHashCodeForObject(entry.getValue()); 181 } 182 return result; 183 } 184 185 @Override hashCode()186 public int hashCode() { 187 return calculateHashCodeForMap(this); 188 } 189 copy(Object object)190 private static Object copy(Object object) { 191 if (object instanceof byte[]) { 192 byte[] data = (byte[]) object; 193 return Arrays.copyOf(data, data.length); 194 } 195 return object; 196 } 197 198 /** 199 * Makes a deep copy of a {@link Map}. Immutable objects in the map will be shared (e.g., 200 * integers, strings, immutable messages) and mutable ones will have a copy (e.g., byte arrays, 201 * mutable messages). 202 */ 203 @SuppressWarnings("unchecked") copy(Map<K, V> map)204 static <K, V> Map<K, V> copy(Map<K, V> map) { 205 Map<K, V> result = new LinkedHashMap<K, V>(); 206 for (Map.Entry<K, V> entry : map.entrySet()) { 207 result.put(entry.getKey(), (V) copy(entry.getValue())); 208 } 209 return result; 210 } 211 212 /** Returns a deep copy of this map field. */ mutableCopy()213 public MapFieldLite<K, V> mutableCopy() { 214 return isEmpty() ? new MapFieldLite<K, V>() : new MapFieldLite<K, V>(this); 215 } 216 217 /** 218 * Makes this field immutable. All subsequent modifications will throw an {@link 219 * UnsupportedOperationException}. 220 */ makeImmutable()221 public void makeImmutable() { 222 isMutable = false; 223 } 224 225 /** Returns whether this field can be modified. */ isMutable()226 public boolean isMutable() { 227 return isMutable; 228 } 229 ensureMutable()230 private void ensureMutable() { 231 if (!isMutable()) { 232 throw new UnsupportedOperationException(); 233 } 234 } 235 } 236