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