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 31package com.google.protobuf; 32 33import com.google.protobuf.Internal.EnumLite; 34 35import java.util.Arrays; 36import java.util.Collection; 37import java.util.Collections; 38import java.util.Iterator; 39import java.util.LinkedHashMap; 40import java.util.Map; 41import java.util.Set; 42 43/** 44 * Internal representation of map fields in generated lite-runtime messages. 45 * 46 * This class is a protobuf implementation detail. Users shouldn't use this 47 * class directly. 48 */ 49public final class MapFieldLite<K, V> implements MutabilityOracle { 50 private MutatabilityAwareMap<K, V> mapData; 51 private boolean isMutable; 52 53 private MapFieldLite(Map<K, V> mapData) { 54 this.mapData = new MutatabilityAwareMap<K, V>(this, mapData); 55 this.isMutable = true; 56 } 57 58 @SuppressWarnings({"rawtypes", "unchecked"}) 59 private static final MapFieldLite EMPTY_MAP_FIELD = 60 new MapFieldLite(Collections.emptyMap()); 61 static { 62 EMPTY_MAP_FIELD.makeImmutable(); 63 } 64 65 /** Returns an singleton immutable empty MapFieldLite instance. */ 66 @SuppressWarnings({"unchecked", "cast"}) 67 public static <K, V> MapFieldLite<K, V> emptyMapField() { 68 return (MapFieldLite<K, V>) EMPTY_MAP_FIELD; 69 } 70 71 /** Creates a new MapFieldLite instance. */ 72 public static <K, V> MapFieldLite<K, V> newMapField() { 73 return new MapFieldLite<K, V>(new LinkedHashMap<K, V>()); 74 } 75 76 /** Gets the content of this MapField as a read-only Map. */ 77 public Map<K, V> getMap() { 78 return Collections.unmodifiableMap(mapData); 79 } 80 81 /** Gets a mutable Map view of this MapField. */ 82 public Map<K, V> getMutableMap() { 83 return mapData; 84 } 85 86 public void mergeFrom(MapFieldLite<K, V> other) { 87 mapData.putAll(copy(other.mapData)); 88 } 89 90 public void clear() { 91 mapData.clear(); 92 } 93 94 private static boolean equals(Object a, Object b) { 95 if (a instanceof byte[] && b instanceof byte[]) { 96 return Arrays.equals((byte[]) a, (byte[]) b); 97 } 98 return a.equals(b); 99 } 100 101 /** 102 * Checks whether two {@link Map}s are equal. We don't use the default equals 103 * method of {@link Map} because it compares by identity not by content for 104 * byte arrays. 105 */ 106 static <K, V> boolean equals(Map<K, V> a, Map<K, V> b) { 107 if (a == b) { 108 return true; 109 } 110 if (a.size() != b.size()) { 111 return false; 112 } 113 for (Map.Entry<K, V> entry : a.entrySet()) { 114 if (!b.containsKey(entry.getKey())) { 115 return false; 116 } 117 if (!equals(entry.getValue(), b.get(entry.getKey()))) { 118 return false; 119 } 120 } 121 return true; 122 } 123 124 /** 125 * Checks whether two map fields are equal. 126 */ 127 @SuppressWarnings("unchecked") 128 @Override 129 public boolean equals(Object object) { 130 if (!(object instanceof MapFieldLite)) { 131 return false; 132 } 133 MapFieldLite<K, V> other = (MapFieldLite<K, V>) object; 134 return equals(mapData, other.mapData); 135 } 136 137 private static int calculateHashCodeForObject(Object a) { 138 if (a instanceof byte[]) { 139 return Internal.hashCode((byte[]) a); 140 } 141 // Enums should be stored as integers internally. 142 if (a instanceof EnumLite) { 143 throw new UnsupportedOperationException(); 144 } 145 return a.hashCode(); 146 } 147 148 /** 149 * Calculates the hash code for a {@link Map}. We don't use the default hash 150 * code method of {@link Map} because for byte arrays and protobuf enums it 151 * use {@link Object#hashCode()}. 152 */ 153 static <K, V> int calculateHashCodeForMap(Map<K, V> a) { 154 int result = 0; 155 for (Map.Entry<K, V> entry : a.entrySet()) { 156 result += calculateHashCodeForObject(entry.getKey()) 157 ^ calculateHashCodeForObject(entry.getValue()); 158 } 159 return result; 160 } 161 162 @Override 163 public int hashCode() { 164 return calculateHashCodeForMap(mapData); 165 } 166 167 private static Object copy(Object object) { 168 if (object instanceof byte[]) { 169 byte[] data = (byte[]) object; 170 return Arrays.copyOf(data, data.length); 171 } 172 return object; 173 } 174 175 /** 176 * Makes a deep copy of a {@link Map}. Immutable objects in the map will be 177 * shared (e.g., integers, strings, immutable messages) and mutable ones will 178 * have a copy (e.g., byte arrays, mutable messages). 179 */ 180 @SuppressWarnings("unchecked") 181 static <K, V> Map<K, V> copy(Map<K, V> map) { 182 Map<K, V> result = new LinkedHashMap<K, V>(); 183 for (Map.Entry<K, V> entry : map.entrySet()) { 184 result.put(entry.getKey(), (V) copy(entry.getValue())); 185 } 186 return result; 187 } 188 189 /** Returns a deep copy of this map field. */ 190 public MapFieldLite<K, V> copy() { 191 return new MapFieldLite<K, V>(copy(mapData)); 192 } 193 194 /** 195 * Makes this field immutable. All subsequent modifications will throw an 196 * {@link UnsupportedOperationException}. 197 */ 198 public void makeImmutable() { 199 isMutable = false; 200 } 201 202 /** 203 * Returns whether this field can be modified. 204 */ 205 public boolean isMutable() { 206 return isMutable; 207 } 208 209 @Override 210 public void ensureMutable() { 211 if (!isMutable()) { 212 throw new UnsupportedOperationException(); 213 } 214 } 215 216 /** 217 * An internal map that checks for mutability before delegating. 218 */ 219 static class MutatabilityAwareMap<K, V> implements Map<K, V> { 220 private final MutabilityOracle mutabilityOracle; 221 private final Map<K, V> delegate; 222 223 MutatabilityAwareMap(MutabilityOracle mutabilityOracle, Map<K, V> delegate) { 224 this.mutabilityOracle = mutabilityOracle; 225 this.delegate = delegate; 226 } 227 228 @Override 229 public int size() { 230 return delegate.size(); 231 } 232 233 @Override 234 public boolean isEmpty() { 235 return delegate.isEmpty(); 236 } 237 238 @Override 239 public boolean containsKey(Object key) { 240 return delegate.containsKey(key); 241 } 242 243 @Override 244 public boolean containsValue(Object value) { 245 return delegate.containsValue(value); 246 } 247 248 @Override 249 public V get(Object key) { 250 return delegate.get(key); 251 } 252 253 @Override 254 public V put(K key, V value) { 255 mutabilityOracle.ensureMutable(); 256 return delegate.put(key, value); 257 } 258 259 @Override 260 public V remove(Object key) { 261 mutabilityOracle.ensureMutable(); 262 return delegate.remove(key); 263 } 264 265 @Override 266 public void putAll(Map<? extends K, ? extends V> m) { 267 mutabilityOracle.ensureMutable(); 268 delegate.putAll(m); 269 } 270 271 @Override 272 public void clear() { 273 mutabilityOracle.ensureMutable(); 274 delegate.clear(); 275 } 276 277 @Override 278 public Set<K> keySet() { 279 return new MutatabilityAwareSet<K>(mutabilityOracle, delegate.keySet()); 280 } 281 282 @Override 283 public Collection<V> values() { 284 return new MutatabilityAwareCollection<V>(mutabilityOracle, delegate.values()); 285 } 286 287 @Override 288 public Set<java.util.Map.Entry<K, V>> entrySet() { 289 return new MutatabilityAwareSet<Entry<K, V>>(mutabilityOracle, delegate.entrySet()); 290 } 291 292 @Override 293 public boolean equals(Object o) { 294 return delegate.equals(o); 295 } 296 297 @Override 298 public int hashCode() { 299 return delegate.hashCode(); 300 } 301 302 @Override 303 public String toString() { 304 return delegate.toString(); 305 } 306 } 307 308 /** 309 * An internal collection that checks for mutability before delegating. 310 */ 311 private static class MutatabilityAwareCollection<E> implements Collection<E> { 312 private final MutabilityOracle mutabilityOracle; 313 private final Collection<E> delegate; 314 315 MutatabilityAwareCollection(MutabilityOracle mutabilityOracle, Collection<E> delegate) { 316 this.mutabilityOracle = mutabilityOracle; 317 this.delegate = delegate; 318 } 319 320 @Override 321 public int size() { 322 return delegate.size(); 323 } 324 325 @Override 326 public boolean isEmpty() { 327 return delegate.isEmpty(); 328 } 329 330 @Override 331 public boolean contains(Object o) { 332 return delegate.contains(o); 333 } 334 335 @Override 336 public Iterator<E> iterator() { 337 return new MutatabilityAwareIterator<E>(mutabilityOracle, delegate.iterator()); 338 } 339 340 @Override 341 public Object[] toArray() { 342 return delegate.toArray(); 343 } 344 345 @Override 346 public <T> T[] toArray(T[] a) { 347 return delegate.toArray(a); 348 } 349 350 @Override 351 public boolean add(E e) { 352 // Unsupported operation in the delegate. 353 throw new UnsupportedOperationException(); 354 } 355 356 @Override 357 public boolean remove(Object o) { 358 mutabilityOracle.ensureMutable(); 359 return delegate.remove(o); 360 } 361 362 @Override 363 public boolean containsAll(Collection<?> c) { 364 return delegate.containsAll(c); 365 } 366 367 @Override 368 public boolean addAll(Collection<? extends E> c) { 369 // Unsupported operation in the delegate. 370 throw new UnsupportedOperationException(); 371 } 372 373 @Override 374 public boolean removeAll(Collection<?> c) { 375 mutabilityOracle.ensureMutable(); 376 return delegate.removeAll(c); 377 } 378 379 @Override 380 public boolean retainAll(Collection<?> c) { 381 mutabilityOracle.ensureMutable(); 382 return delegate.retainAll(c); 383 } 384 385 @Override 386 public void clear() { 387 mutabilityOracle.ensureMutable(); 388 delegate.clear(); 389 } 390 391 @Override 392 public boolean equals(Object o) { 393 return delegate.equals(o); 394 } 395 396 @Override 397 public int hashCode() { 398 return delegate.hashCode(); 399 } 400 401 @Override 402 public String toString() { 403 return delegate.toString(); 404 } 405 } 406 407 /** 408 * An internal set that checks for mutability before delegating. 409 */ 410 private static class MutatabilityAwareSet<E> implements Set<E> { 411 private final MutabilityOracle mutabilityOracle; 412 private final Set<E> delegate; 413 414 MutatabilityAwareSet(MutabilityOracle mutabilityOracle, Set<E> delegate) { 415 this.mutabilityOracle = mutabilityOracle; 416 this.delegate = delegate; 417 } 418 419 @Override 420 public int size() { 421 return delegate.size(); 422 } 423 424 @Override 425 public boolean isEmpty() { 426 return delegate.isEmpty(); 427 } 428 429 @Override 430 public boolean contains(Object o) { 431 return delegate.contains(o); 432 } 433 434 @Override 435 public Iterator<E> iterator() { 436 return new MutatabilityAwareIterator<E>(mutabilityOracle, delegate.iterator()); 437 } 438 439 @Override 440 public Object[] toArray() { 441 return delegate.toArray(); 442 } 443 444 @Override 445 public <T> T[] toArray(T[] a) { 446 return delegate.toArray(a); 447 } 448 449 @Override 450 public boolean add(E e) { 451 mutabilityOracle.ensureMutable(); 452 return delegate.add(e); 453 } 454 455 @Override 456 public boolean remove(Object o) { 457 mutabilityOracle.ensureMutable(); 458 return delegate.remove(o); 459 } 460 461 @Override 462 public boolean containsAll(Collection<?> c) { 463 return delegate.containsAll(c); 464 } 465 466 @Override 467 public boolean addAll(Collection<? extends E> c) { 468 mutabilityOracle.ensureMutable(); 469 return delegate.addAll(c); 470 } 471 472 @Override 473 public boolean retainAll(Collection<?> c) { 474 mutabilityOracle.ensureMutable(); 475 return delegate.retainAll(c); 476 } 477 478 @Override 479 public boolean removeAll(Collection<?> c) { 480 mutabilityOracle.ensureMutable(); 481 return delegate.removeAll(c); 482 } 483 484 @Override 485 public void clear() { 486 mutabilityOracle.ensureMutable(); 487 delegate.clear(); 488 } 489 490 @Override 491 public boolean equals(Object o) { 492 return delegate.equals(o); 493 } 494 495 @Override 496 public int hashCode() { 497 return delegate.hashCode(); 498 } 499 500 @Override 501 public String toString() { 502 return delegate.toString(); 503 } 504 } 505 506 /** 507 * An internal iterator that checks for mutability before delegating. 508 */ 509 private static class MutatabilityAwareIterator<E> implements Iterator<E> { 510 private final MutabilityOracle mutabilityOracle; 511 private final Iterator<E> delegate; 512 513 MutatabilityAwareIterator(MutabilityOracle mutabilityOracle, Iterator<E> delegate) { 514 this.mutabilityOracle = mutabilityOracle; 515 this.delegate = delegate; 516 } 517 518 @Override 519 public boolean hasNext() { 520 return delegate.hasNext(); 521 } 522 523 @Override 524 public E next() { 525 return delegate.next(); 526 } 527 528 @Override 529 public void remove() { 530 mutabilityOracle.ensureMutable(); 531 delegate.remove(); 532 } 533 534 @Override 535 public boolean equals(Object obj) { 536 return delegate.equals(obj); 537 } 538 539 @Override 540 public int hashCode() { 541 return delegate.hashCode(); 542 } 543 544 @Override 545 public String toString() { 546 return delegate.toString(); 547 } 548 } 549} 550