Index: third_party/protobuf/java/src/main/java/com/google/protobuf/MapField.java |
diff --git a/third_party/protobuf/java/src/main/java/com/google/protobuf/MapField.java b/third_party/protobuf/java/src/main/java/com/google/protobuf/MapField.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b290993ca1ab76246e091615ece46c14e3ffc4a1 |
--- /dev/null |
+++ b/third_party/protobuf/java/src/main/java/com/google/protobuf/MapField.java |
@@ -0,0 +1,286 @@ |
+// Protocol Buffers - Google's data interchange format |
+// Copyright 2008 Google Inc. All rights reserved. |
+// https://developers.google.com/protocol-buffers/ |
+// |
+// Redistribution and use in source and binary forms, with or without |
+// modification, are permitted provided that the following conditions are |
+// met: |
+// |
+// * Redistributions of source code must retain the above copyright |
+// notice, this list of conditions and the following disclaimer. |
+// * Redistributions in binary form must reproduce the above |
+// copyright notice, this list of conditions and the following disclaimer |
+// in the documentation and/or other materials provided with the |
+// distribution. |
+// * Neither the name of Google Inc. nor the names of its |
+// contributors may be used to endorse or promote products derived from |
+// this software without specific prior written permission. |
+// |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ |
+package com.google.protobuf; |
+ |
+import com.google.protobuf.MapFieldLite.MutatabilityAwareMap; |
+ |
+import java.util.ArrayList; |
+import java.util.Collections; |
+import java.util.LinkedHashMap; |
+import java.util.List; |
+import java.util.Map; |
+ |
+/** |
+ * Internal representation of map fields in generated messages. |
+ * |
+ * This class supports accessing the map field as a {@link Map} to be used in |
+ * generated API and also supports accessing the field as a {@link List} to be |
+ * used in reflection API. It keeps track of where the data is currently stored |
+ * and do necessary conversions between map and list. |
+ * |
+ * This class is a protobuf implementation detail. Users shouldn't use this |
+ * class directly. |
+ * |
+ * THREAD-SAFETY NOTE: Read-only access is thread-safe. Users can call getMap() |
+ * and getList() concurrently in multiple threads. If write-access is needed, |
+ * all access must be synchronized. |
+ */ |
+public class MapField<K, V> implements MutabilityOracle { |
+ /** |
+ * Indicates where the data of this map field is currently stored. |
+ * |
+ * MAP: Data is stored in mapData. |
+ * LIST: Data is stored in listData. |
+ * BOTH: mapData and listData have the same data. |
+ * |
+ * When the map field is accessed (through generated API or reflection API), |
+ * it will shift between these 3 modes: |
+ * |
+ * getMap() getList() getMutableMap() getMutableList() |
+ * MAP MAP BOTH MAP LIST |
+ * LIST BOTH LIST MAP LIST |
+ * BOTH BOTH BOTH MAP LIST |
+ * |
+ * As the map field changes its mode, the list/map reference returned in a |
+ * previous method call may be invalidated. |
+ */ |
+ private enum StorageMode {MAP, LIST, BOTH} |
+ |
+ private volatile boolean isMutable; |
+ private volatile StorageMode mode; |
+ private MutatabilityAwareMap<K, V> mapData; |
+ private List<Message> listData; |
+ |
+ // Convert between a map entry Message and a key-value pair. |
+ private static interface Converter<K, V> { |
+ Message convertKeyAndValueToMessage(K key, V value); |
+ void convertMessageToKeyAndValue(Message message, Map<K, V> map); |
+ |
+ Message getMessageDefaultInstance(); |
+ } |
+ |
+ private static class ImmutableMessageConverter<K, V> implements Converter<K, V> { |
+ private final MapEntry<K, V> defaultEntry; |
+ public ImmutableMessageConverter(MapEntry<K, V> defaultEntry) { |
+ this.defaultEntry = defaultEntry; |
+ } |
+ |
+ public Message convertKeyAndValueToMessage(K key, V value) { |
+ return defaultEntry.newBuilderForType().setKey(key).setValue(value).buildPartial(); |
+ } |
+ |
+ public void convertMessageToKeyAndValue(Message message, Map<K, V> map) { |
+ MapEntry<K, V> entry = (MapEntry<K, V>) message; |
+ map.put(entry.getKey(), entry.getValue()); |
+ } |
+ |
+ public Message getMessageDefaultInstance() { |
+ return defaultEntry; |
+ } |
+ } |
+ |
+ |
+ private final Converter<K, V> converter; |
+ |
+ private MapField( |
+ Converter<K, V> converter, |
+ StorageMode mode, |
+ Map<K, V> mapData) { |
+ this.converter = converter; |
+ this.isMutable = true; |
+ this.mode = mode; |
+ this.mapData = new MutatabilityAwareMap<K, V>(this, mapData); |
+ this.listData = null; |
+ } |
+ |
+ private MapField( |
+ MapEntry<K, V> defaultEntry, |
+ StorageMode mode, |
+ Map<K, V> mapData) { |
+ this(new ImmutableMessageConverter<K, V>(defaultEntry), mode, mapData); |
+ } |
+ |
+ |
+ /** Returns an immutable empty MapField. */ |
+ public static <K, V> MapField<K, V> emptyMapField( |
+ MapEntry<K, V> defaultEntry) { |
+ return new MapField<K, V>( |
+ defaultEntry, StorageMode.MAP, Collections.<K, V>emptyMap()); |
+ } |
+ |
+ |
+ /** Creates a new mutable empty MapField. */ |
+ public static <K, V> MapField<K, V> newMapField(MapEntry<K, V> defaultEntry) { |
+ return new MapField<K, V>( |
+ defaultEntry, StorageMode.MAP, new LinkedHashMap<K, V>()); |
+ } |
+ |
+ |
+ private Message convertKeyAndValueToMessage(K key, V value) { |
+ return converter.convertKeyAndValueToMessage(key, value); |
+ } |
+ |
+ @SuppressWarnings("unchecked") |
+ private void convertMessageToKeyAndValue(Message message, Map<K, V> map) { |
+ converter.convertMessageToKeyAndValue(message, map); |
+ } |
+ |
+ private List<Message> convertMapToList(MutatabilityAwareMap<K, V> mapData) { |
+ List<Message> listData = new ArrayList<Message>(); |
+ for (Map.Entry<K, V> entry : mapData.entrySet()) { |
+ listData.add( |
+ convertKeyAndValueToMessage( |
+ entry.getKey(), entry.getValue())); |
+ } |
+ return listData; |
+ } |
+ |
+ private MutatabilityAwareMap<K, V> convertListToMap(List<Message> listData) { |
+ Map<K, V> mapData = new LinkedHashMap<K, V>(); |
+ for (Message item : listData) { |
+ convertMessageToKeyAndValue(item, mapData); |
+ } |
+ return new MutatabilityAwareMap<K, V>(this, mapData); |
+ } |
+ |
+ /** Returns the content of this MapField as a read-only Map. */ |
+ public Map<K, V> getMap() { |
+ if (mode == StorageMode.LIST) { |
+ synchronized (this) { |
+ if (mode == StorageMode.LIST) { |
+ mapData = convertListToMap(listData); |
+ mode = StorageMode.BOTH; |
+ } |
+ } |
+ } |
+ return Collections.unmodifiableMap(mapData); |
+ } |
+ |
+ /** Gets a mutable Map view of this MapField. */ |
+ public Map<K, V> getMutableMap() { |
+ if (mode != StorageMode.MAP) { |
+ if (mode == StorageMode.LIST) { |
+ mapData = convertListToMap(listData); |
+ } |
+ listData = null; |
+ mode = StorageMode.MAP; |
+ } |
+ return mapData; |
+ } |
+ |
+ public void mergeFrom(MapField<K, V> other) { |
+ getMutableMap().putAll(MapFieldLite.copy(other.getMap())); |
+ } |
+ |
+ public void clear() { |
+ mapData = new MutatabilityAwareMap<K, V>(this, new LinkedHashMap<K, V>()); |
+ mode = StorageMode.MAP; |
+ } |
+ |
+ @SuppressWarnings("unchecked") |
+ @Override |
+ public boolean equals(Object object) { |
+ if (!(object instanceof MapField)) { |
+ return false; |
+ } |
+ MapField<K, V> other = (MapField<K, V>) object; |
+ return MapFieldLite.<K, V>equals(getMap(), other.getMap()); |
+ } |
+ |
+ @Override |
+ public int hashCode() { |
+ return MapFieldLite.<K, V>calculateHashCodeForMap(getMap()); |
+ } |
+ |
+ /** Returns a deep copy of this MapField. */ |
+ public MapField<K, V> copy() { |
+ return new MapField<K, V>( |
+ converter, StorageMode.MAP, MapFieldLite.copy(getMap())); |
+ } |
+ |
+ /** Gets the content of this MapField as a read-only List. */ |
+ List<Message> getList() { |
+ if (mode == StorageMode.MAP) { |
+ synchronized (this) { |
+ if (mode == StorageMode.MAP) { |
+ listData = convertMapToList(mapData); |
+ mode = StorageMode.BOTH; |
+ } |
+ } |
+ } |
+ return Collections.unmodifiableList(listData); |
+ } |
+ |
+ /** Gets a mutable List view of this MapField. */ |
+ List<Message> getMutableList() { |
+ if (mode != StorageMode.LIST) { |
+ if (mode == StorageMode.MAP) { |
+ listData = convertMapToList(mapData); |
+ } |
+ mapData = null; |
+ mode = StorageMode.LIST; |
+ } |
+ return listData; |
+ } |
+ |
+ /** |
+ * Gets the default instance of the message stored in the list view of this |
+ * map field. |
+ */ |
+ Message getMapEntryMessageDefaultInstance() { |
+ return converter.getMessageDefaultInstance(); |
+ } |
+ |
+ /** |
+ * Makes this list immutable. All subsequent modifications will throw an |
+ * {@link UnsupportedOperationException}. |
+ */ |
+ public void makeImmutable() { |
+ isMutable = false; |
+ } |
+ |
+ /** |
+ * Returns whether this field can be modified. |
+ */ |
+ public boolean isMutable() { |
+ return isMutable; |
+ } |
+ |
+ /* (non-Javadoc) |
+ * @see com.google.protobuf.MutabilityOracle#ensureMutable() |
+ */ |
+ @Override |
+ public void ensureMutable() { |
+ if (!isMutable()) { |
+ throw new UnsupportedOperationException(); |
+ } |
+ } |
+} |