content: // 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();
        }
      }
    }
  }
  