• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.MapFieldLite.MutatabilityAwareMap;
34 
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.LinkedHashMap;
38 import java.util.List;
39 import java.util.Map;
40 
41 /**
42  * Internal representation of map fields in generated messages.
43  *
44  * This class supports accessing the map field as a {@link Map} to be used in
45  * generated API and also supports accessing the field as a {@link List} to be
46  * used in reflection API. It keeps track of where the data is currently stored
47  * and do necessary conversions between map and list.
48  *
49  * This class is a protobuf implementation detail. Users shouldn't use this
50  * class directly.
51  *
52  * THREAD-SAFETY NOTE: Read-only access is thread-safe. Users can call getMap()
53  * and getList() concurrently in multiple threads. If write-access is needed,
54  * all access must be synchronized.
55  */
56 public class MapField<K, V> implements MutabilityOracle {
57   /**
58    * Indicates where the data of this map field is currently stored.
59    *
60    * MAP: Data is stored in mapData.
61    * LIST: Data is stored in listData.
62    * BOTH: mapData and listData have the same data.
63    *
64    * When the map field is accessed (through generated API or reflection API),
65    * it will shift between these 3 modes:
66    *
67    *          getMap()   getList()   getMutableMap()   getMutableList()
68    *   MAP      MAP        BOTH          MAP               LIST
69    *   LIST     BOTH       LIST          MAP               LIST
70    *   BOTH     BOTH       BOTH          MAP               LIST
71    *
72    * As the map field changes its mode, the list/map reference returned in a
73    * previous method call may be invalidated.
74    */
75   private enum StorageMode {MAP, LIST, BOTH}
76 
77   private volatile boolean isMutable;
78   private volatile StorageMode mode;
79   private MutatabilityAwareMap<K, V> mapData;
80   private List<Message> listData;
81 
82   // Convert between a map entry Message and a key-value pair.
83   private static interface Converter<K, V> {
convertKeyAndValueToMessage(K key, V value)84     Message convertKeyAndValueToMessage(K key, V value);
convertMessageToKeyAndValue(Message message, Map<K, V> map)85     void convertMessageToKeyAndValue(Message message, Map<K, V> map);
86 
getMessageDefaultInstance()87     Message getMessageDefaultInstance();
88   }
89 
90   private static class ImmutableMessageConverter<K, V> implements Converter<K, V> {
91     private final MapEntry<K, V> defaultEntry;
ImmutableMessageConverter(MapEntry<K, V> defaultEntry)92     public ImmutableMessageConverter(MapEntry<K, V> defaultEntry) {
93       this.defaultEntry = defaultEntry;
94     }
95 
96     @Override
convertKeyAndValueToMessage(K key, V value)97     public Message convertKeyAndValueToMessage(K key, V value) {
98       return defaultEntry.newBuilderForType().setKey(key).setValue(value).buildPartial();
99     }
100 
101     @Override
convertMessageToKeyAndValue(Message message, Map<K, V> map)102     public void convertMessageToKeyAndValue(Message message, Map<K, V> map) {
103       MapEntry<K, V> entry = (MapEntry<K, V>) message;
104       map.put(entry.getKey(), entry.getValue());
105     }
106 
107     @Override
getMessageDefaultInstance()108     public Message getMessageDefaultInstance() {
109       return defaultEntry;
110     }
111   }
112 
113 
114   private final Converter<K, V> converter;
115 
MapField( Converter<K, V> converter, StorageMode mode, Map<K, V> mapData)116   private MapField(
117       Converter<K, V> converter,
118       StorageMode mode,
119       Map<K, V> mapData) {
120     this.converter = converter;
121     this.isMutable = true;
122     this.mode = mode;
123     this.mapData = new MutatabilityAwareMap<K, V>(this, mapData);
124     this.listData = null;
125   }
126 
MapField( MapEntry<K, V> defaultEntry, StorageMode mode, Map<K, V> mapData)127   private MapField(
128       MapEntry<K, V> defaultEntry,
129       StorageMode mode,
130       Map<K, V> mapData) {
131     this(new ImmutableMessageConverter<K, V>(defaultEntry), mode, mapData);
132   }
133 
134 
135   /** Returns an immutable empty MapField. */
emptyMapField( MapEntry<K, V> defaultEntry)136   public static <K, V> MapField<K, V> emptyMapField(
137       MapEntry<K, V> defaultEntry) {
138     return new MapField<K, V>(
139         defaultEntry, StorageMode.MAP, Collections.<K, V>emptyMap());
140   }
141 
142 
143   /** Creates a new mutable empty MapField. */
newMapField(MapEntry<K, V> defaultEntry)144   public static <K, V> MapField<K, V> newMapField(MapEntry<K, V> defaultEntry) {
145     return new MapField<K, V>(
146         defaultEntry, StorageMode.MAP, new LinkedHashMap<K, V>());
147   }
148 
149 
convertKeyAndValueToMessage(K key, V value)150   private Message convertKeyAndValueToMessage(K key, V value) {
151     return converter.convertKeyAndValueToMessage(key, value);
152   }
153 
154   @SuppressWarnings("unchecked")
convertMessageToKeyAndValue(Message message, Map<K, V> map)155   private void convertMessageToKeyAndValue(Message message, Map<K, V> map) {
156     converter.convertMessageToKeyAndValue(message, map);
157   }
158 
convertMapToList(MutatabilityAwareMap<K, V> mapData)159   private List<Message> convertMapToList(MutatabilityAwareMap<K, V> mapData) {
160     List<Message> listData = new ArrayList<Message>();
161     for (Map.Entry<K, V> entry : mapData.entrySet()) {
162       listData.add(
163           convertKeyAndValueToMessage(
164               entry.getKey(), entry.getValue()));
165     }
166     return listData;
167   }
168 
convertListToMap(List<Message> listData)169   private MutatabilityAwareMap<K, V> convertListToMap(List<Message> listData) {
170     Map<K, V> mapData = new LinkedHashMap<K, V>();
171     for (Message item : listData) {
172       convertMessageToKeyAndValue(item, mapData);
173     }
174     return new MutatabilityAwareMap<K, V>(this, mapData);
175   }
176 
177   /** Returns the content of this MapField as a read-only Map. */
getMap()178   public Map<K, V> getMap() {
179     if (mode == StorageMode.LIST) {
180       synchronized (this) {
181         if (mode == StorageMode.LIST) {
182           mapData = convertListToMap(listData);
183           mode = StorageMode.BOTH;
184         }
185       }
186     }
187     return Collections.unmodifiableMap(mapData);
188   }
189 
190   /** Gets a mutable Map view of this MapField. */
getMutableMap()191   public Map<K, V> getMutableMap() {
192     if (mode != StorageMode.MAP) {
193       if (mode == StorageMode.LIST) {
194         mapData = convertListToMap(listData);
195       }
196       listData = null;
197       mode = StorageMode.MAP;
198     }
199     return mapData;
200   }
201 
mergeFrom(MapField<K, V> other)202   public void mergeFrom(MapField<K, V> other) {
203     getMutableMap().putAll(MapFieldLite.copy(other.getMap()));
204   }
205 
clear()206   public void clear() {
207     mapData = new MutatabilityAwareMap<K, V>(this, new LinkedHashMap<K, V>());
208     mode = StorageMode.MAP;
209   }
210 
211   @SuppressWarnings("unchecked")
212   @Override
equals(Object object)213   public boolean equals(Object object) {
214     if (!(object instanceof MapField)) {
215       return false;
216     }
217     MapField<K, V> other = (MapField<K, V>) object;
218     return MapFieldLite.<K, V>equals(getMap(), other.getMap());
219   }
220 
221   @Override
hashCode()222   public int hashCode() {
223     return MapFieldLite.<K, V>calculateHashCodeForMap(getMap());
224   }
225 
226   /** Returns a deep copy of this MapField. */
copy()227   public MapField<K, V> copy() {
228     return new MapField<K, V>(
229         converter, StorageMode.MAP, MapFieldLite.copy(getMap()));
230   }
231 
232   /** Gets the content of this MapField as a read-only List. */
getList()233   List<Message> getList() {
234     if (mode == StorageMode.MAP) {
235       synchronized (this) {
236         if (mode == StorageMode.MAP) {
237           listData = convertMapToList(mapData);
238           mode = StorageMode.BOTH;
239         }
240       }
241     }
242     return Collections.unmodifiableList(listData);
243   }
244 
245   /** Gets a mutable List view of this MapField. */
getMutableList()246   List<Message> getMutableList() {
247     if (mode != StorageMode.LIST) {
248       if (mode == StorageMode.MAP) {
249         listData = convertMapToList(mapData);
250       }
251       mapData = null;
252       mode = StorageMode.LIST;
253     }
254     return listData;
255   }
256 
257   /**
258    * Gets the default instance of the message stored in the list view of this
259    * map field.
260    */
getMapEntryMessageDefaultInstance()261   Message getMapEntryMessageDefaultInstance() {
262     return converter.getMessageDefaultInstance();
263   }
264 
265   /**
266    * Makes this list immutable. All subsequent modifications will throw an
267    * {@link UnsupportedOperationException}.
268    */
makeImmutable()269   public void makeImmutable() {
270     isMutable = false;
271   }
272 
273   /**
274    * Returns whether this field can be modified.
275    */
isMutable()276   public boolean isMutable() {
277     return isMutable;
278   }
279 
280   /* (non-Javadoc)
281    * @see com.google.protobuf.MutabilityOracle#ensureMutable()
282    */
283   @Override
ensureMutable()284   public void ensureMutable() {
285     if (!isMutable()) {
286       throw new UnsupportedOperationException();
287     }
288   }
289 }
290