From 49efe9d7db877022e76375df2d4daadab98619b6 Mon Sep 17 00:00:00 2001 From: nmittler Date: Fri, 8 Jan 2016 09:19:11 -0800 Subject: Restructuring protobuf to multiple modules protobuf/java will become a parent pom that will contain two modules: core - contains all of the code for the protobuf-java artifact util - contains all of the code for the protobuf-java-util artifact Also cleaned up various Maven warnings. --- .../main/java/com/google/protobuf/MapField.java | 286 +++++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 java/core/src/main/java/com/google/protobuf/MapField.java (limited to 'java/core/src/main/java/com/google/protobuf/MapField.java') diff --git a/java/core/src/main/java/com/google/protobuf/MapField.java b/java/core/src/main/java/com/google/protobuf/MapField.java new file mode 100644 index 00000000..b290993c --- /dev/null +++ b/java/core/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 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 mapData; + private List listData; + + // Convert between a map entry Message and a key-value pair. + private static interface Converter { + Message convertKeyAndValueToMessage(K key, V value); + void convertMessageToKeyAndValue(Message message, Map map); + + Message getMessageDefaultInstance(); + } + + private static class ImmutableMessageConverter implements Converter { + private final MapEntry defaultEntry; + public ImmutableMessageConverter(MapEntry 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 map) { + MapEntry entry = (MapEntry) message; + map.put(entry.getKey(), entry.getValue()); + } + + public Message getMessageDefaultInstance() { + return defaultEntry; + } + } + + + private final Converter converter; + + private MapField( + Converter converter, + StorageMode mode, + Map mapData) { + this.converter = converter; + this.isMutable = true; + this.mode = mode; + this.mapData = new MutatabilityAwareMap(this, mapData); + this.listData = null; + } + + private MapField( + MapEntry defaultEntry, + StorageMode mode, + Map mapData) { + this(new ImmutableMessageConverter(defaultEntry), mode, mapData); + } + + + /** Returns an immutable empty MapField. */ + public static MapField emptyMapField( + MapEntry defaultEntry) { + return new MapField( + defaultEntry, StorageMode.MAP, Collections.emptyMap()); + } + + + /** Creates a new mutable empty MapField. */ + public static MapField newMapField(MapEntry defaultEntry) { + return new MapField( + defaultEntry, StorageMode.MAP, new LinkedHashMap()); + } + + + private Message convertKeyAndValueToMessage(K key, V value) { + return converter.convertKeyAndValueToMessage(key, value); + } + + @SuppressWarnings("unchecked") + private void convertMessageToKeyAndValue(Message message, Map map) { + converter.convertMessageToKeyAndValue(message, map); + } + + private List convertMapToList(MutatabilityAwareMap mapData) { + List listData = new ArrayList(); + for (Map.Entry entry : mapData.entrySet()) { + listData.add( + convertKeyAndValueToMessage( + entry.getKey(), entry.getValue())); + } + return listData; + } + + private MutatabilityAwareMap convertListToMap(List listData) { + Map mapData = new LinkedHashMap(); + for (Message item : listData) { + convertMessageToKeyAndValue(item, mapData); + } + return new MutatabilityAwareMap(this, mapData); + } + + /** Returns the content of this MapField as a read-only Map. */ + public Map 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 getMutableMap() { + if (mode != StorageMode.MAP) { + if (mode == StorageMode.LIST) { + mapData = convertListToMap(listData); + } + listData = null; + mode = StorageMode.MAP; + } + return mapData; + } + + public void mergeFrom(MapField other) { + getMutableMap().putAll(MapFieldLite.copy(other.getMap())); + } + + public void clear() { + mapData = new MutatabilityAwareMap(this, new LinkedHashMap()); + mode = StorageMode.MAP; + } + + @SuppressWarnings("unchecked") + @Override + public boolean equals(Object object) { + if (!(object instanceof MapField)) { + return false; + } + MapField other = (MapField) object; + return MapFieldLite.equals(getMap(), other.getMap()); + } + + @Override + public int hashCode() { + return MapFieldLite.calculateHashCodeForMap(getMap()); + } + + /** Returns a deep copy of this MapField. */ + public MapField copy() { + return new MapField( + converter, StorageMode.MAP, MapFieldLite.copy(getMap())); + } + + /** Gets the content of this MapField as a read-only List. */ + List 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 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(); + } + } +} -- cgit v1.2.3