Add MapFieldBuilder to support a later change to codegen.

PiperOrigin-RevId: 549711661
pull/13366/head
Protobuf Team Bot 2 years ago committed by Copybara-Service
parent c3058fc45c
commit 3ee67038e5
  1. 37
      java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java
  2. 7
      java/core/src/main/java/com/google/protobuf/MapField.java
  3. 220
      java/core/src/main/java/com/google/protobuf/MapFieldBuilder.java
  4. 48
      java/core/src/main/java/com/google/protobuf/MapFieldReflectionAccessor.java

@ -959,13 +959,27 @@ public abstract class GeneratedMessageV3 extends AbstractMessage implements Seri
* map field directly and thus enables us to access the map field as a list. * map field directly and thus enables us to access the map field as a list.
*/ */
@SuppressWarnings({"unused", "rawtypes"}) @SuppressWarnings({"unused", "rawtypes"})
protected MapFieldReflectionAccessor internalGetMapFieldReflection(int fieldNumber) {
return internalGetMapField(fieldNumber);
}
/** TODO(b/258340024): Remove, exists for compatibility with generated code. */
@Deprecated
@SuppressWarnings({"unused", "rawtypes"})
protected MapField internalGetMapField(int fieldNumber) { protected MapField internalGetMapField(int fieldNumber) {
// Note that we can't use descriptor names here because this method will // Note that we can't use descriptor names here because this method will
// be called when descriptor is being initialized. // be called when descriptor is being initialized.
throw new IllegalArgumentException("No map fields found in " + getClass().getName()); throw new IllegalArgumentException("No map fields found in " + getClass().getName());
} }
/** Like {@link #internalGetMapField} but return a mutable version. */ /** Like {@link #internalGetMapFieldReflection} but return a mutable version. */
@SuppressWarnings({"unused", "rawtypes"})
protected MapFieldReflectionAccessor internalGetMutableMapFieldReflection(int fieldNumber) {
return internalGetMutableMapField(fieldNumber);
}
/** TODO(b/258340024): Remove, exists for compatibility with generated code. */
@Deprecated
@SuppressWarnings({"unused", "rawtypes"}) @SuppressWarnings({"unused", "rawtypes"})
protected MapField internalGetMutableMapField(int fieldNumber) { protected MapField internalGetMutableMapField(int fieldNumber) {
// Note that we can't use descriptor names here because this method will // Note that we can't use descriptor names here because this method will
@ -2047,6 +2061,13 @@ public abstract class GeneratedMessageV3 extends AbstractMessage implements Seri
* generated API only allows us to access it as a map. This method returns the underlying map * generated API only allows us to access it as a map. This method returns the underlying map
* field directly and thus enables us to access the map field as a list. * field directly and thus enables us to access the map field as a list.
*/ */
@SuppressWarnings("unused")
protected MapFieldReflectionAccessor internalGetMapFieldReflection(int fieldNumber) {
return internalGetMapField(fieldNumber);
}
/** TODO(b/258340024): Remove, exists for compatibility with generated code. */
@Deprecated
@SuppressWarnings({"rawtypes", "unused"}) @SuppressWarnings({"rawtypes", "unused"})
protected MapField internalGetMapField(int fieldNumber) { protected MapField internalGetMapField(int fieldNumber) {
// Note that we can't use descriptor names here because this method will // Note that we can't use descriptor names here because this method will
@ -2802,7 +2823,7 @@ public abstract class GeneratedMessageV3 extends AbstractMessage implements Seri
final FieldDescriptor descriptor, final Class<? extends GeneratedMessageV3> messageClass) { final FieldDescriptor descriptor, final Class<? extends GeneratedMessageV3> messageClass) {
field = descriptor; field = descriptor;
Method getDefaultInstanceMethod = getMethodOrDie(messageClass, "getDefaultInstance"); Method getDefaultInstanceMethod = getMethodOrDie(messageClass, "getDefaultInstance");
MapField<?, ?> defaultMapField = MapFieldReflectionAccessor defaultMapField =
getMapField((GeneratedMessageV3) invokeOrDie(getDefaultInstanceMethod, null)); getMapField((GeneratedMessageV3) invokeOrDie(getDefaultInstanceMethod, null));
mapEntryMessageDefaultInstance = defaultMapField.getMapEntryMessageDefaultInstance(); mapEntryMessageDefaultInstance = defaultMapField.getMapEntryMessageDefaultInstance();
} }
@ -2810,16 +2831,16 @@ public abstract class GeneratedMessageV3 extends AbstractMessage implements Seri
private final FieldDescriptor field; private final FieldDescriptor field;
private final Message mapEntryMessageDefaultInstance; private final Message mapEntryMessageDefaultInstance;
private MapField<?, ?> getMapField(GeneratedMessageV3 message) { private MapFieldReflectionAccessor getMapField(GeneratedMessageV3 message) {
return (MapField<?, ?>) message.internalGetMapField(field.getNumber()); return message.internalGetMapFieldReflection(field.getNumber());
} }
private MapField<?, ?> getMapField(GeneratedMessageV3.Builder<?> builder) { private MapFieldReflectionAccessor getMapField(GeneratedMessageV3.Builder<?> builder) {
return (MapField<?, ?>) builder.internalGetMapField(field.getNumber()); return builder.internalGetMapFieldReflection(field.getNumber());
} }
private MapField<?, ?> getMutableMapField(GeneratedMessageV3.Builder<?> builder) { private MapFieldReflectionAccessor getMutableMapField(GeneratedMessageV3.Builder<?> builder) {
return (MapField<?, ?>) builder.internalGetMutableMapField(field.getNumber()); return builder.internalGetMutableMapFieldReflection(field.getNumber());
} }
private Message coerceType(Message value) { private Message coerceType(Message value) {

@ -53,7 +53,7 @@ import java.util.Set;
* <p>THREAD-SAFETY NOTE: Read-only access is thread-safe. Users can call getMap() and getList() * <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. * concurrently in multiple threads. If write-access is needed, all access must be synchronized.
*/ */
public class MapField<K, V> implements MutabilityOracle { public class MapField<K, V> extends MapFieldReflectionAccessor implements MutabilityOracle {
/** /**
* Indicates where the data of this map field is currently stored. * Indicates where the data of this map field is currently stored.
@ -225,6 +225,7 @@ public class MapField<K, V> implements MutabilityOracle {
} }
/** Gets the content of this MapField as a read-only List. */ /** Gets the content of this MapField as a read-only List. */
@Override
List<Message> getList() { List<Message> getList() {
if (mode == StorageMode.MAP) { if (mode == StorageMode.MAP) {
synchronized (this) { synchronized (this) {
@ -238,6 +239,7 @@ public class MapField<K, V> implements MutabilityOracle {
} }
/** Gets a mutable List view of this MapField. */ /** Gets a mutable List view of this MapField. */
@Override
List<Message> getMutableList() { List<Message> getMutableList() {
if (mode != StorageMode.LIST) { if (mode != StorageMode.LIST) {
if (mode == StorageMode.MAP) { if (mode == StorageMode.MAP) {
@ -250,6 +252,7 @@ public class MapField<K, V> implements MutabilityOracle {
} }
/** Gets the default instance of the message stored in the list view of this map field. */ /** Gets the default instance of the message stored in the list view of this map field. */
@Override
Message getMapEntryMessageDefaultInstance() { Message getMapEntryMessageDefaultInstance() {
return converter.getMessageDefaultInstance(); return converter.getMessageDefaultInstance();
} }
@ -278,7 +281,7 @@ public class MapField<K, V> implements MutabilityOracle {
} }
/** An internal map that checks for mutability before delegating. */ /** An internal map that checks for mutability before delegating. */
private static class MutabilityAwareMap<K, V> implements Map<K, V> { static class MutabilityAwareMap<K, V> implements Map<K, V> {
private final MutabilityOracle mutabilityOracle; private final MutabilityOracle mutabilityOracle;
private final Map<K, V> delegate; private final Map<K, V> delegate;

@ -0,0 +1,220 @@
// 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 java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
/**
* Internal representation of map fields in generated builders.
*
* <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.
*/
public class MapFieldBuilder<
KeyT,
MessageOrBuilderT extends MessageOrBuilder,
MessageT extends MessageOrBuilderT,
BuilderT extends MessageOrBuilderT>
extends MapFieldReflectionAccessor {
// Only one of the three fields may be non-null at any time.
/** nullable */
Map<KeyT, MessageOrBuilderT> builderMap = new LinkedHashMap<>();
/** nullable */
Map<KeyT, MessageT> messageMap = null;
// messageList elements are always MapEntry<KeyT, MessageT>, but we need a List<Message> for
// reflection.
/** nullable */
List<Message> messageList = null;
Converter<KeyT, MessageOrBuilderT, MessageT> converter;
/** Convert a MessageOrBuilder to a Message regardless of which it holds. */
public interface Converter<
KeyT, MessageOrBuilderT extends MessageOrBuilder, MessageT extends MessageOrBuilderT> {
MessageT build(MessageOrBuilderT val);
MapEntry<KeyT, MessageT> defaultEntry();
}
public MapFieldBuilder(Converter<KeyT, MessageOrBuilderT, MessageT> converter) {
this.converter = converter;
}
@SuppressWarnings("unchecked")
private void forEachListEntry(BiConsumer<KeyT, MessageT> f) {
messageList.forEach(
entry -> {
MapEntry<KeyT, MessageT> typedEntry = (MapEntry<KeyT, MessageT>) entry;
f.accept(typedEntry.getKey(), typedEntry.getValue());
});
}
public Map<KeyT, MessageOrBuilderT> ensureBuilderMap() {
if (builderMap != null) {
return builderMap;
}
if (messageMap != null) {
builderMap = new LinkedHashMap<>(messageMap.size());
messageMap.forEach((key, value) -> builderMap.put(key, value));
messageMap = null;
return builderMap;
}
builderMap = new LinkedHashMap<>(messageList.size());
forEachListEntry((key, value) -> builderMap.put(key, value));
messageList = null;
return builderMap;
}
public List<Message> ensureMessageList() {
if (messageList != null) {
return messageList;
}
if (builderMap != null) {
messageList = new ArrayList<>(builderMap.size());
builderMap.forEach(
(key, value) ->
messageList.add(
converter.defaultEntry().toBuilder()
.setKey(key)
.setValue(converter.build(value))
.build()));
builderMap = null;
return messageList;
}
messageList = new ArrayList<>(messageMap.size());
messageMap.forEach(
(key, value) ->
messageList.add(
converter.defaultEntry().toBuilder().setKey(key).setValue(value).build()));
messageMap = null;
return messageList;
}
public Map<KeyT, MessageT> ensureMessageMap() {
messageMap = populateMutableMap();
builderMap = null;
messageList = null;
return messageMap;
}
public Map<KeyT, MessageT> getImmutableMap() {
return new MapField.MutabilityAwareMap<>(MutabilityOracle.IMMUTABLE, populateMutableMap());
}
private Map<KeyT, MessageT> populateMutableMap() {
if (messageMap != null) {
return messageMap;
}
if (builderMap != null) {
Map<KeyT, MessageT> toReturn = new LinkedHashMap<>(builderMap.size());
builderMap.forEach((key, value) -> toReturn.put(key, converter.build(value)));
return toReturn;
}
Map<KeyT, MessageT> toReturn = new LinkedHashMap<>(messageList.size());
forEachListEntry((key, value) -> toReturn.put(key, value));
return toReturn;
}
public void mergeFrom(MapField<KeyT, MessageT> other) {
ensureBuilderMap().putAll(MapFieldLite.copy(other.getMap()));
}
public void clear() {
builderMap = new LinkedHashMap<>();
messageMap = null;
messageList = null;
}
private boolean typedEquals(MapFieldBuilder<KeyT, MessageOrBuilderT, MessageT, BuilderT> other) {
return MapFieldLite.<KeyT, MessageOrBuilderT>equals(
ensureBuilderMap(), other.ensureBuilderMap());
}
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object object) {
if (!(object instanceof MapFieldBuilder)) {
return false;
}
return typedEquals((MapFieldBuilder<KeyT, MessageOrBuilderT, MessageT, BuilderT>) object);
}
@Override
public int hashCode() {
return MapFieldLite.<KeyT, MessageOrBuilderT>calculateHashCodeForMap(ensureBuilderMap());
}
/** Returns a deep copy of this MapFieldBuilder. */
public MapFieldBuilder<KeyT, MessageOrBuilderT, MessageT, BuilderT> copy() {
MapFieldBuilder<KeyT, MessageOrBuilderT, MessageT, BuilderT> clone =
new MapFieldBuilder<>(converter);
clone.ensureBuilderMap().putAll(ensureBuilderMap());
return clone;
}
/** Converts this MapFieldBuilder to a MapField. */
public MapField<KeyT, MessageT> build(MapEntry<KeyT, MessageT> defaultEntry) {
MapField<KeyT, MessageT> mapField = MapField.newMapField(defaultEntry);
Map<KeyT, MessageT> map = mapField.getMutableMap();
ensureBuilderMap().forEach((key, value) -> map.put(key, converter.build(value)));
mapField.makeImmutable();
return mapField;
}
// MapFieldReflectionAccessor implementation.
/** Gets the content of this MapField as a read-only List. */
@Override
List<Message> getList() {
return ensureMessageList();
}
/** Gets a mutable List view of this MapField. */
@Override
List<Message> getMutableList() {
return ensureMessageList();
}
/** Gets the default instance of the message stored in the list view of this map field. */
@Override
Message getMapEntryMessageDefaultInstance() {
return converter.defaultEntry();
}
}

@ -0,0 +1,48 @@
// 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 java.util.List;
/**
* A base class for package private shared methods between MapField and MapFieldBuilder to allow
* reflection to access both.
*/
public abstract class MapFieldReflectionAccessor {
/** Gets the content of this MapField as a read-only List. */
abstract List<Message> getList();
/** Gets a mutable List view of this MapField. */
abstract List<Message> getMutableList();
/** Gets the default instance of the message stored in the list view of this map field. */
abstract Message getMapEntryMessageDefaultInstance();
}
Loading…
Cancel
Save