Home | History | Annotate | Download | only in protobuf
      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> {
     84     Message convertKeyAndValueToMessage(K key, V value);
     85     void convertMessageToKeyAndValue(Message message, Map<K, V> map);
     86 
     87     Message getMessageDefaultInstance();
     88   }
     89 
     90   private static class ImmutableMessageConverter<K, V> implements Converter<K, V> {
     91     private final MapEntry<K, V> defaultEntry;
     92     public ImmutableMessageConverter(MapEntry<K, V> defaultEntry) {
     93       this.defaultEntry = defaultEntry;
     94     }
     95 
     96     @Override
     97     public Message convertKeyAndValueToMessage(K key, V value) {
     98       return defaultEntry.newBuilderForType().setKey(key).setValue(value).buildPartial();
     99     }
    100 
    101     @Override
    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
    108     public Message getMessageDefaultInstance() {
    109       return defaultEntry;
    110     }
    111   }
    112 
    113 
    114   private final Converter<K, V> converter;
    115 
    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 
    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. */
    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. */
    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 
    150   private Message convertKeyAndValueToMessage(K key, V value) {
    151     return converter.convertKeyAndValueToMessage(key, value);
    152   }
    153 
    154   @SuppressWarnings("unchecked")
    155   private void convertMessageToKeyAndValue(Message message, Map<K, V> map) {
    156     converter.convertMessageToKeyAndValue(message, map);
    157   }
    158 
    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 
    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. */
    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. */
    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 
    202   public void mergeFrom(MapField<K, V> other) {
    203     getMutableMap().putAll(MapFieldLite.copy(other.getMap()));
    204   }
    205 
    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
    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
    222   public int hashCode() {
    223     return MapFieldLite.<K, V>calculateHashCodeForMap(getMap());
    224   }
    225 
    226   /** Returns a deep copy of this MapField. */
    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. */
    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. */
    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    */
    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    */
    269   public void makeImmutable() {
    270     isMutable = false;
    271   }
    272 
    273   /**
    274    * Returns whether this field can be modified.
    275    */
    276   public boolean isMutable() {
    277     return isMutable;
    278   }
    279 
    280   /* (non-Javadoc)
    281    * @see com.google.protobuf.MutabilityOracle#ensureMutable()
    282    */
    283   @Override
    284   public void ensureMutable() {
    285     if (!isMutable()) {
    286       throw new UnsupportedOperationException();
    287     }
    288   }
    289 }
    290