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 com.google.protobuf.Internal.EnumLite; 34 35 import java.util.Arrays; 36 import java.util.Collections; 37 import java.util.LinkedHashMap; 38 import java.util.Map; 39 import java.util.Set; 40 41 /** 42 * Internal representation of map fields in generated lite-runtime messages. 43 * 44 * This class is a protobuf implementation detail. Users shouldn't use this 45 * 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(Collections.emptyMap()); 62 static { EMPTY_MAP_FIELD.makeImmutable()63 EMPTY_MAP_FIELD.makeImmutable(); 64 } 65 66 /** Returns an singleton immutable empty MapFieldLite instance. */ 67 @SuppressWarnings({"unchecked", "cast"}) emptyMapField()68 public static <K, V> MapFieldLite<K, V> emptyMapField() { 69 return (MapFieldLite<K, V>) EMPTY_MAP_FIELD; 70 } 71 mergeFrom(MapFieldLite<K, V> other)72 public void mergeFrom(MapFieldLite<K, V> other) { 73 ensureMutable(); 74 if (!other.isEmpty()) { 75 putAll(other); 76 } 77 } 78 79 @SuppressWarnings({"unchecked", "cast"}) entrySet()80 @Override public Set<Map.Entry<K, V>> entrySet() { 81 return isEmpty() ? Collections.<Map.Entry<K, V>>emptySet() : super.entrySet(); 82 } 83 clear()84 @Override public void clear() { 85 ensureMutable(); 86 clear(); 87 } 88 put(K key, V value)89 @Override public V put(K key, V value) { 90 ensureMutable(); 91 return super.put(key, value); 92 } 93 put(Map.Entry<K, V> entry)94 public V put(Map.Entry<K, V> entry) { 95 return put(entry.getKey(), entry.getValue()); 96 } 97 putAll(Map<? extends K, ? extends V> m)98 @Override public void putAll(Map<? extends K, ? extends V> m) { 99 ensureMutable(); 100 super.putAll(m); 101 } 102 remove(Object key)103 @Override public V remove(Object key) { 104 ensureMutable(); 105 return super.remove(key); 106 } 107 equals(Object a, Object b)108 private static boolean equals(Object a, Object b) { 109 if (a instanceof byte[] && b instanceof byte[]) { 110 return Arrays.equals((byte[]) a, (byte[]) b); 111 } 112 return a.equals(b); 113 } 114 115 /** 116 * Checks whether two {@link Map}s are equal. We don't use the default equals 117 * method of {@link Map} because it compares by identity not by content for 118 * byte arrays. 119 */ equals(Map<K, V> a, Map<K, V> b)120 static <K, V> boolean equals(Map<K, V> a, Map<K, V> b) { 121 if (a == b) { 122 return true; 123 } 124 if (a.size() != b.size()) { 125 return false; 126 } 127 for (Map.Entry<K, V> entry : a.entrySet()) { 128 if (!b.containsKey(entry.getKey())) { 129 return false; 130 } 131 if (!equals(entry.getValue(), b.get(entry.getKey()))) { 132 return false; 133 } 134 } 135 return true; 136 } 137 138 /** 139 * Checks whether two map fields are equal. 140 */ 141 @SuppressWarnings("unchecked") 142 @Override equals(Object object)143 public boolean equals(Object object) { 144 return (object instanceof Map) && equals(this, (Map<K, V>) object); 145 } 146 calculateHashCodeForObject(Object a)147 private static int calculateHashCodeForObject(Object a) { 148 if (a instanceof byte[]) { 149 return Internal.hashCode((byte[]) a); 150 } 151 // Enums should be stored as integers internally. 152 if (a instanceof EnumLite) { 153 throw new UnsupportedOperationException(); 154 } 155 return a.hashCode(); 156 } 157 158 /** 159 * Calculates the hash code for a {@link Map}. We don't use the default hash 160 * code method of {@link Map} because for byte arrays and protobuf enums it 161 * use {@link Object#hashCode()}. 162 */ calculateHashCodeForMap(Map<K, V> a)163 static <K, V> int calculateHashCodeForMap(Map<K, V> a) { 164 int result = 0; 165 for (Map.Entry<K, V> entry : a.entrySet()) { 166 result += calculateHashCodeForObject(entry.getKey()) 167 ^ calculateHashCodeForObject(entry.getValue()); 168 } 169 return result; 170 } 171 172 @Override hashCode()173 public int hashCode() { 174 return calculateHashCodeForMap(this); 175 } 176 copy(Object object)177 private static Object copy(Object object) { 178 if (object instanceof byte[]) { 179 byte[] data = (byte[]) object; 180 return Arrays.copyOf(data, data.length); 181 } 182 return object; 183 } 184 185 /** 186 * Makes a deep copy of a {@link Map}. Immutable objects in the map will be 187 * shared (e.g., integers, strings, immutable messages) and mutable ones will 188 * have a copy (e.g., byte arrays, mutable messages). 189 */ 190 @SuppressWarnings("unchecked") copy(Map<K, V> map)191 static <K, V> Map<K, V> copy(Map<K, V> map) { 192 Map<K, V> result = new LinkedHashMap<K, V>(); 193 for (Map.Entry<K, V> entry : map.entrySet()) { 194 result.put(entry.getKey(), (V) copy(entry.getValue())); 195 } 196 return result; 197 } 198 199 /** Returns a deep copy of this map field. */ mutableCopy()200 public MapFieldLite<K, V> mutableCopy() { 201 return isEmpty() ? new MapFieldLite<K, V>() : new MapFieldLite<K, V>(this); 202 } 203 204 /** 205 * Makes this field immutable. All subsequent modifications will throw an 206 * {@link UnsupportedOperationException}. 207 */ makeImmutable()208 public void makeImmutable() { 209 isMutable = false; 210 } 211 212 /** 213 * Returns whether this field can be modified. 214 */ isMutable()215 public boolean isMutable() { 216 return isMutable; 217 } 218 ensureMutable()219 private void ensureMutable() { 220 if (!isMutable()) { 221 throw new UnsupportedOperationException(); 222 } 223 } 224 } 225