• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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