| // 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 static com.google.protobuf.Internal.checkNotNull; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * Internal representation of map fields in generated messages. |
| * |
| * <p>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. |
| * |
| * <p>This class is a protobuf implementation detail. Users shouldn't use this class directly. |
| * |
| * <p>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. |
| * |
| * <ul> |
| * <li>MAP: Data is stored in mapData. |
| * <li>LIST: Data is stored in listData. |
| * <li>BOTH: mapData and listData have the same data. |
| * </ul> |
| * |
| * <p>When the map field is accessed (through generated API or reflection API), it will shift |
| * between these 3 modes: |
| * |
| * <pre> |
| * <b>getMap() getList() getMutableMap() getMutableList()</b> |
| * <b>MAP</b> MAP BOTH MAP LIST |
| * <b>LIST</b> BOTH LIST MAP LIST |
| * <b>BOTH</b> BOTH BOTH MAP LIST |
| * </pre> |
| * |
| * <p>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; |
| } |
| |
| @Override |
| public Message convertKeyAndValueToMessage(K key, V value) { |
| return defaultEntry.newBuilderForType().setKey(key).setValue(value).buildPartial(); |
| } |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public void convertMessageToKeyAndValue(Message message, Map<K, V> map) { |
| MapEntry<K, V> entry = (MapEntry<K, V>) message; |
| map.put(entry.getKey(), entry.getValue()); |
| } |
| |
| @Override |
| 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(); |
| } |
| } |
| |
| /** An internal map that checks for mutability before delegating. */ |
| private static class MutatabilityAwareMap<K, V> implements Map<K, V> { |
| private final MutabilityOracle mutabilityOracle; |
| private final Map<K, V> delegate; |
| |
| MutatabilityAwareMap(MutabilityOracle mutabilityOracle, Map<K, V> delegate) { |
| this.mutabilityOracle = mutabilityOracle; |
| this.delegate = delegate; |
| } |
| |
| @Override |
| public int size() { |
| return delegate.size(); |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return delegate.isEmpty(); |
| } |
| |
| @Override |
| public boolean containsKey(Object key) { |
| return delegate.containsKey(key); |
| } |
| |
| @Override |
| public boolean containsValue(Object value) { |
| return delegate.containsValue(value); |
| } |
| |
| @Override |
| public V get(Object key) { |
| return delegate.get(key); |
| } |
| |
| @Override |
| public V put(K key, V value) { |
| mutabilityOracle.ensureMutable(); |
| checkNotNull(key); |
| checkNotNull(value); |
| return delegate.put(key, value); |
| } |
| |
| @Override |
| public V remove(Object key) { |
| mutabilityOracle.ensureMutable(); |
| return delegate.remove(key); |
| } |
| |
| @Override |
| public void putAll(Map<? extends K, ? extends V> m) { |
| mutabilityOracle.ensureMutable(); |
| for (K key : m.keySet()) { |
| checkNotNull(key); |
| checkNotNull(m.get(key)); |
| } |
| delegate.putAll(m); |
| } |
| |
| @Override |
| public void clear() { |
| mutabilityOracle.ensureMutable(); |
| delegate.clear(); |
| } |
| |
| @Override |
| public Set<K> keySet() { |
| return new MutatabilityAwareSet<K>(mutabilityOracle, delegate.keySet()); |
| } |
| |
| @Override |
| public Collection<V> values() { |
| return new MutatabilityAwareCollection<V>(mutabilityOracle, delegate.values()); |
| } |
| |
| @Override |
| public Set<java.util.Map.Entry<K, V>> entrySet() { |
| return new MutatabilityAwareSet<Entry<K, V>>(mutabilityOracle, delegate.entrySet()); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| return delegate.equals(o); |
| } |
| |
| @Override |
| public int hashCode() { |
| return delegate.hashCode(); |
| } |
| |
| @Override |
| public String toString() { |
| return delegate.toString(); |
| } |
| |
| /** An internal collection that checks for mutability before delegating. */ |
| private static class MutatabilityAwareCollection<E> implements Collection<E> { |
| private final MutabilityOracle mutabilityOracle; |
| private final Collection<E> delegate; |
| |
| MutatabilityAwareCollection(MutabilityOracle mutabilityOracle, Collection<E> delegate) { |
| this.mutabilityOracle = mutabilityOracle; |
| this.delegate = delegate; |
| } |
| |
| @Override |
| public int size() { |
| return delegate.size(); |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return delegate.isEmpty(); |
| } |
| |
| @Override |
| public boolean contains(Object o) { |
| return delegate.contains(o); |
| } |
| |
| @Override |
| public Iterator<E> iterator() { |
| return new MutatabilityAwareIterator<E>(mutabilityOracle, delegate.iterator()); |
| } |
| |
| @Override |
| public Object[] toArray() { |
| return delegate.toArray(); |
| } |
| |
| @Override |
| public <T> T[] toArray(T[] a) { |
| return delegate.toArray(a); |
| } |
| |
| @Override |
| public boolean add(E e) { |
| // Unsupported operation in the delegate. |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public boolean remove(Object o) { |
| mutabilityOracle.ensureMutable(); |
| return delegate.remove(o); |
| } |
| |
| @Override |
| public boolean containsAll(Collection<?> c) { |
| return delegate.containsAll(c); |
| } |
| |
| @Override |
| public boolean addAll(Collection<? extends E> c) { |
| // Unsupported operation in the delegate. |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public boolean removeAll(Collection<?> c) { |
| mutabilityOracle.ensureMutable(); |
| return delegate.removeAll(c); |
| } |
| |
| @Override |
| public boolean retainAll(Collection<?> c) { |
| mutabilityOracle.ensureMutable(); |
| return delegate.retainAll(c); |
| } |
| |
| @Override |
| public void clear() { |
| mutabilityOracle.ensureMutable(); |
| delegate.clear(); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| return delegate.equals(o); |
| } |
| |
| @Override |
| public int hashCode() { |
| return delegate.hashCode(); |
| } |
| |
| @Override |
| public String toString() { |
| return delegate.toString(); |
| } |
| } |
| |
| /** An internal set that checks for mutability before delegating. */ |
| private static class MutatabilityAwareSet<E> implements Set<E> { |
| private final MutabilityOracle mutabilityOracle; |
| private final Set<E> delegate; |
| |
| MutatabilityAwareSet(MutabilityOracle mutabilityOracle, Set<E> delegate) { |
| this.mutabilityOracle = mutabilityOracle; |
| this.delegate = delegate; |
| } |
| |
| @Override |
| public int size() { |
| return delegate.size(); |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return delegate.isEmpty(); |
| } |
| |
| @Override |
| public boolean contains(Object o) { |
| return delegate.contains(o); |
| } |
| |
| @Override |
| public Iterator<E> iterator() { |
| return new MutatabilityAwareIterator<E>(mutabilityOracle, delegate.iterator()); |
| } |
| |
| @Override |
| public Object[] toArray() { |
| return delegate.toArray(); |
| } |
| |
| @Override |
| public <T> T[] toArray(T[] a) { |
| return delegate.toArray(a); |
| } |
| |
| @Override |
| public boolean add(E e) { |
| mutabilityOracle.ensureMutable(); |
| return delegate.add(e); |
| } |
| |
| @Override |
| public boolean remove(Object o) { |
| mutabilityOracle.ensureMutable(); |
| return delegate.remove(o); |
| } |
| |
| @Override |
| public boolean containsAll(Collection<?> c) { |
| return delegate.containsAll(c); |
| } |
| |
| @Override |
| public boolean addAll(Collection<? extends E> c) { |
| mutabilityOracle.ensureMutable(); |
| return delegate.addAll(c); |
| } |
| |
| @Override |
| public boolean retainAll(Collection<?> c) { |
| mutabilityOracle.ensureMutable(); |
| return delegate.retainAll(c); |
| } |
| |
| @Override |
| public boolean removeAll(Collection<?> c) { |
| mutabilityOracle.ensureMutable(); |
| return delegate.removeAll(c); |
| } |
| |
| @Override |
| public void clear() { |
| mutabilityOracle.ensureMutable(); |
| delegate.clear(); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| return delegate.equals(o); |
| } |
| |
| @Override |
| public int hashCode() { |
| return delegate.hashCode(); |
| } |
| |
| @Override |
| public String toString() { |
| return delegate.toString(); |
| } |
| } |
| |
| /** An internal iterator that checks for mutability before delegating. */ |
| private static class MutatabilityAwareIterator<E> implements Iterator<E> { |
| private final MutabilityOracle mutabilityOracle; |
| private final Iterator<E> delegate; |
| |
| MutatabilityAwareIterator(MutabilityOracle mutabilityOracle, Iterator<E> delegate) { |
| this.mutabilityOracle = mutabilityOracle; |
| this.delegate = delegate; |
| } |
| |
| @Override |
| public boolean hasNext() { |
| return delegate.hasNext(); |
| } |
| |
| @Override |
| public E next() { |
| return delegate.next(); |
| } |
| |
| @Override |
| public void remove() { |
| mutabilityOracle.ensureMutable(); |
| delegate.remove(); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| return delegate.equals(obj); |
| } |
| |
| @Override |
| public int hashCode() { |
| return delegate.hashCode(); |
| } |
| |
| @Override |
| public String toString() { |
| return delegate.toString(); |
| } |
| } |
| } |
| } |