com.google.protobuf.MapFieldBuilder Maven / Gradle / Ivy
Show all versions of protobuf-java-util Show documentation
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
package com.google.protobuf;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Internal representation of map fields in generated builders.
*
* 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.
*/
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 builderMap = new LinkedHashMap<>();
/** nullable */
Map messageMap = null;
// We need a List for reflection.
//
// messageList elements are always MapEntry, where SomeT and MessageT
// have the same descriptor (i.e. SomeT can be DynamicMessage)
/** nullable */
List messageList = null;
Converter 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 defaultEntry();
}
public MapFieldBuilder(Converter converter) {
this.converter = converter;
}
@SuppressWarnings("unchecked")
private List> getMapEntryList() {
ArrayList> list = new ArrayList<>(messageList.size());
Class> valueClass = converter.defaultEntry().getValue().getClass();
for (Message entry : messageList) {
MapEntry typedEntry = (MapEntry) entry;
if (valueClass.isInstance(typedEntry.getValue())) {
list.add((MapEntry) typedEntry);
} else {
// This needs to use mergeFrom to allow MapEntry to be used.
list.add(converter.defaultEntry().toBuilder().mergeFrom(entry).build());
}
}
return list;
}
public Map ensureBuilderMap() {
if (builderMap != null) {
return builderMap;
}
if (messageMap != null) {
builderMap = new LinkedHashMap<>(messageMap.size());
for (Map.Entry entry : messageMap.entrySet()) {
builderMap.put(entry.getKey(), entry.getValue());
}
messageMap = null;
return builderMap;
}
builderMap = new LinkedHashMap<>(messageList.size());
for (MapEntry entry : getMapEntryList()) {
builderMap.put(entry.getKey(), entry.getValue());
}
messageList = null;
return builderMap;
}
public List ensureMessageList() {
if (messageList != null) {
return messageList;
}
if (builderMap != null) {
messageList = new ArrayList<>(builderMap.size());
for (Map.Entry entry : builderMap.entrySet()) {
messageList.add(
converter.defaultEntry().toBuilder()
.setKey(entry.getKey())
.setValue(converter.build(entry.getValue()))
.build());
}
builderMap = null;
return messageList;
}
messageList = new ArrayList<>(messageMap.size());
for (Map.Entry entry : messageMap.entrySet()) {
messageList.add(
converter.defaultEntry().toBuilder()
.setKey(entry.getKey())
.setValue(entry.getValue())
.build());
}
messageMap = null;
return messageList;
}
public Map ensureMessageMap() {
messageMap = populateMutableMap();
builderMap = null;
messageList = null;
return messageMap;
}
public Map getImmutableMap() {
return new MapField.MutabilityAwareMap<>(MutabilityOracle.IMMUTABLE, populateMutableMap());
}
private Map populateMutableMap() {
if (messageMap != null) {
return messageMap;
}
if (builderMap != null) {
Map toReturn = new LinkedHashMap<>(builderMap.size());
for (Map.Entry entry : builderMap.entrySet()) {
toReturn.put(entry.getKey(), converter.build(entry.getValue()));
}
return toReturn;
}
Map toReturn = new LinkedHashMap<>(messageList.size());
for (MapEntry entry : getMapEntryList()) {
toReturn.put(entry.getKey(), entry.getValue());
}
return toReturn;
}
public void mergeFrom(MapField other) {
ensureBuilderMap().putAll(MapFieldLite.copy(other.getMap()));
}
public void clear() {
builderMap = new LinkedHashMap<>();
messageMap = null;
messageList = null;
}
private boolean typedEquals(MapFieldBuilder other) {
return MapFieldLite.equals(
ensureBuilderMap(), other.ensureBuilderMap());
}
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object object) {
if (!(object instanceof MapFieldBuilder)) {
return false;
}
return typedEquals((MapFieldBuilder) object);
}
@Override
public int hashCode() {
return MapFieldLite.calculateHashCodeForMap(ensureBuilderMap());
}
/** Returns a deep copy of this MapFieldBuilder. */
public MapFieldBuilder copy() {
MapFieldBuilder clone =
new MapFieldBuilder<>(converter);
clone.ensureBuilderMap().putAll(ensureBuilderMap());
return clone;
}
/** Converts this MapFieldBuilder to a MapField. */
public MapField build(MapEntry defaultEntry) {
MapField mapField = MapField.newMapField(defaultEntry);
Map map = mapField.getMutableMap();
for (Map.Entry entry : ensureBuilderMap().entrySet()) {
map.put(entry.getKey(), converter.build(entry.getValue()));
}
mapField.makeImmutable();
return mapField;
}
// MapFieldReflectionAccessor implementation.
/** Gets the content of this MapField as a read-only List. */
@Override
List getList() {
return ensureMessageList();
}
/** Gets a mutable List view of this MapField. */
@Override
List 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();
}
}