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