|
|
|
@ -48,8 +48,7 @@ import java.util.Map; |
|
|
|
|
* |
|
|
|
|
* @author kenton@google.com Kenton Varda |
|
|
|
|
*/ |
|
|
|
|
final class FieldSet< |
|
|
|
|
FieldDescriptorType extends FieldSet.FieldDescriptorLite<FieldDescriptorType>> { |
|
|
|
|
final class FieldSet<T extends FieldSet.FieldDescriptorLite<T>> { |
|
|
|
|
/** |
|
|
|
|
* Interface for a FieldDescriptor or lite extension descriptor. This prevents FieldSet from |
|
|
|
|
* depending on {@link Descriptors.FieldDescriptor}. |
|
|
|
@ -72,18 +71,26 @@ final class FieldSet< |
|
|
|
|
MessageLite.Builder internalMergeFrom(MessageLite.Builder to, MessageLite from); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private final SmallSortedMap<FieldDescriptorType, Object> fields; |
|
|
|
|
private static final int DEFAULT_FIELD_MAP_ARRAY_SIZE = 16; |
|
|
|
|
|
|
|
|
|
private final SmallSortedMap<T, Object> fields; |
|
|
|
|
private boolean isImmutable; |
|
|
|
|
private boolean hasLazyField = false; |
|
|
|
|
private boolean hasLazyField; |
|
|
|
|
|
|
|
|
|
/** Construct a new FieldSet. */ |
|
|
|
|
private FieldSet() { |
|
|
|
|
this.fields = SmallSortedMap.newFieldMap(16); |
|
|
|
|
this.fields = SmallSortedMap.newFieldMap(DEFAULT_FIELD_MAP_ARRAY_SIZE); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Construct an empty FieldSet. This is only used to initialize DEFAULT_INSTANCE. */ |
|
|
|
|
@SuppressWarnings("unused") |
|
|
|
|
private FieldSet(final boolean dummy) { |
|
|
|
|
this.fields = SmallSortedMap.newFieldMap(0); |
|
|
|
|
this(SmallSortedMap.<T>newFieldMap(0)); |
|
|
|
|
makeImmutable(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private FieldSet(SmallSortedMap<T, Object> fields) { |
|
|
|
|
this.fields = fields; |
|
|
|
|
makeImmutable(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -98,6 +105,11 @@ final class FieldSet< |
|
|
|
|
return DEFAULT_INSTANCE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Construct a new Builder. */ |
|
|
|
|
public static <T extends FieldDescriptorLite<T>> Builder<T> newBuilder() { |
|
|
|
|
return new Builder<T>(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@SuppressWarnings("rawtypes") |
|
|
|
|
private static final FieldSet DEFAULT_INSTANCE = new FieldSet(true); |
|
|
|
|
|
|
|
|
@ -152,18 +164,16 @@ final class FieldSet< |
|
|
|
|
* @return the newly cloned FieldSet |
|
|
|
|
*/ |
|
|
|
|
@Override |
|
|
|
|
public FieldSet<FieldDescriptorType> clone() { |
|
|
|
|
public FieldSet<T> clone() { |
|
|
|
|
// We can't just call fields.clone because List objects in the map
|
|
|
|
|
// should not be shared.
|
|
|
|
|
FieldSet<FieldDescriptorType> clone = FieldSet.newFieldSet(); |
|
|
|
|
FieldSet<T> clone = FieldSet.newFieldSet(); |
|
|
|
|
for (int i = 0; i < fields.getNumArrayEntries(); i++) { |
|
|
|
|
Map.Entry<FieldDescriptorType, Object> entry = fields.getArrayEntryAt(i); |
|
|
|
|
FieldDescriptorType descriptor = entry.getKey(); |
|
|
|
|
clone.setField(descriptor, entry.getValue()); |
|
|
|
|
Map.Entry<T, Object> entry = fields.getArrayEntryAt(i); |
|
|
|
|
clone.setField(entry.getKey(), entry.getValue()); |
|
|
|
|
} |
|
|
|
|
for (Map.Entry<FieldDescriptorType, Object> entry : fields.getOverflowEntries()) { |
|
|
|
|
FieldDescriptorType descriptor = entry.getKey(); |
|
|
|
|
clone.setField(descriptor, entry.getValue()); |
|
|
|
|
for (Map.Entry<T, Object> entry : fields.getOverflowEntries()) { |
|
|
|
|
clone.setField(entry.getKey(), entry.getValue()); |
|
|
|
|
} |
|
|
|
|
clone.hasLazyField = hasLazyField; |
|
|
|
|
return clone; |
|
|
|
@ -179,15 +189,9 @@ final class FieldSet< |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Get a simple map containing all the fields. */ |
|
|
|
|
public Map<FieldDescriptorType, Object> getAllFields() { |
|
|
|
|
public Map<T, Object> getAllFields() { |
|
|
|
|
if (hasLazyField) { |
|
|
|
|
SmallSortedMap<FieldDescriptorType, Object> result = SmallSortedMap.newFieldMap(16); |
|
|
|
|
for (int i = 0; i < fields.getNumArrayEntries(); i++) { |
|
|
|
|
cloneFieldEntry(result, fields.getArrayEntryAt(i)); |
|
|
|
|
} |
|
|
|
|
for (Map.Entry<FieldDescriptorType, Object> entry : fields.getOverflowEntries()) { |
|
|
|
|
cloneFieldEntry(result, entry); |
|
|
|
|
} |
|
|
|
|
SmallSortedMap<T, Object> result = cloneAllFieldsMap(fields, /* copyList */ false); |
|
|
|
|
if (fields.isImmutable()) { |
|
|
|
|
result.makeImmutable(); |
|
|
|
|
} |
|
|
|
@ -196,12 +200,26 @@ final class FieldSet< |
|
|
|
|
return fields.isImmutable() ? fields : Collections.unmodifiableMap(fields); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void cloneFieldEntry( |
|
|
|
|
Map<FieldDescriptorType, Object> map, Map.Entry<FieldDescriptorType, Object> entry) { |
|
|
|
|
FieldDescriptorType key = entry.getKey(); |
|
|
|
|
private static <T extends FieldDescriptorLite<T>> SmallSortedMap<T, Object> cloneAllFieldsMap( |
|
|
|
|
SmallSortedMap<T, Object> fields, boolean copyList) { |
|
|
|
|
SmallSortedMap<T, Object> result = SmallSortedMap.newFieldMap(DEFAULT_FIELD_MAP_ARRAY_SIZE); |
|
|
|
|
for (int i = 0; i < fields.getNumArrayEntries(); i++) { |
|
|
|
|
cloneFieldEntry(result, fields.getArrayEntryAt(i), copyList); |
|
|
|
|
} |
|
|
|
|
for (Map.Entry<T, Object> entry : fields.getOverflowEntries()) { |
|
|
|
|
cloneFieldEntry(result, entry, copyList); |
|
|
|
|
} |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static <T extends FieldDescriptorLite<T>> void cloneFieldEntry( |
|
|
|
|
Map<T, Object> map, Map.Entry<T, Object> entry, boolean copyList) { |
|
|
|
|
T key = entry.getKey(); |
|
|
|
|
Object value = entry.getValue(); |
|
|
|
|
if (value instanceof LazyField) { |
|
|
|
|
map.put(key, ((LazyField) value).getValue()); |
|
|
|
|
} else if (copyList && value instanceof List) { |
|
|
|
|
map.put(key, new ArrayList<>((List<?>) value)); |
|
|
|
|
} else { |
|
|
|
|
map.put(key, value); |
|
|
|
|
} |
|
|
|
@ -211,9 +229,9 @@ final class FieldSet< |
|
|
|
|
* Get an iterator to the field map. This iterator should not be leaked out of the protobuf |
|
|
|
|
* library as it is not protected from mutation when fields is not immutable. |
|
|
|
|
*/ |
|
|
|
|
public Iterator<Map.Entry<FieldDescriptorType, Object>> iterator() { |
|
|
|
|
public Iterator<Map.Entry<T, Object>> iterator() { |
|
|
|
|
if (hasLazyField) { |
|
|
|
|
return new LazyIterator<FieldDescriptorType>(fields.entrySet().iterator()); |
|
|
|
|
return new LazyIterator<T>(fields.entrySet().iterator()); |
|
|
|
|
} |
|
|
|
|
return fields.entrySet().iterator(); |
|
|
|
|
} |
|
|
|
@ -223,15 +241,15 @@ final class FieldSet< |
|
|
|
|
* should not be leaked out of the protobuf library as it is not protected from mutation when |
|
|
|
|
* fields is not immutable. |
|
|
|
|
*/ |
|
|
|
|
Iterator<Map.Entry<FieldDescriptorType, Object>> descendingIterator() { |
|
|
|
|
Iterator<Map.Entry<T, Object>> descendingIterator() { |
|
|
|
|
if (hasLazyField) { |
|
|
|
|
return new LazyIterator<FieldDescriptorType>(fields.descendingEntrySet().iterator()); |
|
|
|
|
return new LazyIterator<T>(fields.descendingEntrySet().iterator()); |
|
|
|
|
} |
|
|
|
|
return fields.descendingEntrySet().iterator(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Useful for implementing {@link Message#hasField(Descriptors.FieldDescriptor)}. */ |
|
|
|
|
public boolean hasField(final FieldDescriptorType descriptor) { |
|
|
|
|
public boolean hasField(final T descriptor) { |
|
|
|
|
if (descriptor.isRepeated()) { |
|
|
|
|
throw new IllegalArgumentException("hasField() can only be called on non-repeated fields."); |
|
|
|
|
} |
|
|
|
@ -244,7 +262,7 @@ final class FieldSet< |
|
|
|
|
* returns {@code null} if the field is not set; in this case it is up to the caller to fetch the |
|
|
|
|
* field's default value. |
|
|
|
|
*/ |
|
|
|
|
public Object getField(final FieldDescriptorType descriptor) { |
|
|
|
|
public Object getField(final T descriptor) { |
|
|
|
|
Object o = fields.get(descriptor); |
|
|
|
|
if (o instanceof LazyField) { |
|
|
|
|
return ((LazyField) o).getValue(); |
|
|
|
@ -256,7 +274,7 @@ final class FieldSet< |
|
|
|
|
* Useful for implementing {@link Message.Builder#setField(Descriptors.FieldDescriptor,Object)}. |
|
|
|
|
*/ |
|
|
|
|
@SuppressWarnings({"unchecked", "rawtypes"}) |
|
|
|
|
public void setField(final FieldDescriptorType descriptor, Object value) { |
|
|
|
|
public void setField(final T descriptor, Object value) { |
|
|
|
|
if (descriptor.isRepeated()) { |
|
|
|
|
if (!(value instanceof List)) { |
|
|
|
|
throw new IllegalArgumentException( |
|
|
|
@ -282,7 +300,7 @@ final class FieldSet< |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Useful for implementing {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}. */ |
|
|
|
|
public void clearField(final FieldDescriptorType descriptor) { |
|
|
|
|
public void clearField(final T descriptor) { |
|
|
|
|
fields.remove(descriptor); |
|
|
|
|
if (fields.isEmpty()) { |
|
|
|
|
hasLazyField = false; |
|
|
|
@ -290,7 +308,7 @@ final class FieldSet< |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Useful for implementing {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}. */ |
|
|
|
|
public int getRepeatedFieldCount(final FieldDescriptorType descriptor) { |
|
|
|
|
public int getRepeatedFieldCount(final T descriptor) { |
|
|
|
|
if (!descriptor.isRepeated()) { |
|
|
|
|
throw new IllegalArgumentException( |
|
|
|
|
"getRepeatedField() can only be called on repeated fields."); |
|
|
|
@ -305,7 +323,7 @@ final class FieldSet< |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Useful for implementing {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)}. */ |
|
|
|
|
public Object getRepeatedField(final FieldDescriptorType descriptor, final int index) { |
|
|
|
|
public Object getRepeatedField(final T descriptor, final int index) { |
|
|
|
|
if (!descriptor.isRepeated()) { |
|
|
|
|
throw new IllegalArgumentException( |
|
|
|
|
"getRepeatedField() can only be called on repeated fields."); |
|
|
|
@ -325,8 +343,7 @@ final class FieldSet< |
|
|
|
|
* Message.Builder#setRepeatedField(Descriptors.FieldDescriptor,int,Object)}. |
|
|
|
|
*/ |
|
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
|
public void setRepeatedField( |
|
|
|
|
final FieldDescriptorType descriptor, final int index, final Object value) { |
|
|
|
|
public void setRepeatedField(final T descriptor, final int index, final Object value) { |
|
|
|
|
if (!descriptor.isRepeated()) { |
|
|
|
|
throw new IllegalArgumentException( |
|
|
|
|
"getRepeatedField() can only be called on repeated fields."); |
|
|
|
@ -346,7 +363,7 @@ final class FieldSet< |
|
|
|
|
* Message.Builder#addRepeatedField(Descriptors.FieldDescriptor,Object)}. |
|
|
|
|
*/ |
|
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
|
public void addRepeatedField(final FieldDescriptorType descriptor, final Object value) { |
|
|
|
|
public void addRepeatedField(final T descriptor, final Object value) { |
|
|
|
|
if (!descriptor.isRepeated()) { |
|
|
|
|
throw new IllegalArgumentException( |
|
|
|
|
"addRepeatedField() can only be called on repeated fields."); |
|
|
|
@ -373,53 +390,45 @@ final class FieldSet< |
|
|
|
|
* |
|
|
|
|
* @throws IllegalArgumentException The value is not of the right type. |
|
|
|
|
*/ |
|
|
|
|
private static void verifyType(final WireFormat.FieldType type, final Object value) { |
|
|
|
|
checkNotNull(value); |
|
|
|
|
private void verifyType(final WireFormat.FieldType type, final Object value) { |
|
|
|
|
if (!isValidType(type, value)) { |
|
|
|
|
// TODO(kenton): When chaining calls to setField(), it can be hard to
|
|
|
|
|
// tell from the stack trace which exact call failed, since the whole
|
|
|
|
|
// chain is considered one line of code. It would be nice to print
|
|
|
|
|
// more information here, e.g. naming the field. We used to do that.
|
|
|
|
|
// But we can't now that FieldSet doesn't use descriptors. Maybe this
|
|
|
|
|
// isn't a big deal, though, since it would only really apply when using
|
|
|
|
|
// reflection and generally people don't chain reflection setters.
|
|
|
|
|
throw new IllegalArgumentException( |
|
|
|
|
"Wrong object type used with protocol message reflection."); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
boolean isValid = false; |
|
|
|
|
private static boolean isValidType(final WireFormat.FieldType type, final Object value) { |
|
|
|
|
checkNotNull(value); |
|
|
|
|
switch (type.getJavaType()) { |
|
|
|
|
case INT: |
|
|
|
|
isValid = value instanceof Integer; |
|
|
|
|
break; |
|
|
|
|
return value instanceof Integer; |
|
|
|
|
case LONG: |
|
|
|
|
isValid = value instanceof Long; |
|
|
|
|
break; |
|
|
|
|
return value instanceof Long; |
|
|
|
|
case FLOAT: |
|
|
|
|
isValid = value instanceof Float; |
|
|
|
|
break; |
|
|
|
|
return value instanceof Float; |
|
|
|
|
case DOUBLE: |
|
|
|
|
isValid = value instanceof Double; |
|
|
|
|
break; |
|
|
|
|
return value instanceof Double; |
|
|
|
|
case BOOLEAN: |
|
|
|
|
isValid = value instanceof Boolean; |
|
|
|
|
break; |
|
|
|
|
return value instanceof Boolean; |
|
|
|
|
case STRING: |
|
|
|
|
isValid = value instanceof String; |
|
|
|
|
break; |
|
|
|
|
return value instanceof String; |
|
|
|
|
case BYTE_STRING: |
|
|
|
|
isValid = value instanceof ByteString || value instanceof byte[]; |
|
|
|
|
break; |
|
|
|
|
return value instanceof ByteString || value instanceof byte[]; |
|
|
|
|
case ENUM: |
|
|
|
|
// TODO(kenton): Caller must do type checking here, I guess.
|
|
|
|
|
isValid = (value instanceof Integer || value instanceof Internal.EnumLite); |
|
|
|
|
break; |
|
|
|
|
return (value instanceof Integer || value instanceof Internal.EnumLite); |
|
|
|
|
case MESSAGE: |
|
|
|
|
// TODO(kenton): Caller must do type checking here, I guess.
|
|
|
|
|
isValid = (value instanceof MessageLite) || (value instanceof LazyField); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!isValid) { |
|
|
|
|
// TODO(kenton): When chaining calls to setField(), it can be hard to
|
|
|
|
|
// tell from the stack trace which exact call failed, since the whole
|
|
|
|
|
// chain is considered one line of code. It would be nice to print
|
|
|
|
|
// more information here, e.g. naming the field. We used to do that.
|
|
|
|
|
// But we can't now that FieldSet doesn't use descriptors. Maybe this
|
|
|
|
|
// isn't a big deal, though, since it would only really apply when using
|
|
|
|
|
// reflection and generally people don't chain reflection setters.
|
|
|
|
|
throw new IllegalArgumentException( |
|
|
|
|
"Wrong object type used with protocol message reflection."); |
|
|
|
|
return (value instanceof MessageLite) || (value instanceof LazyField); |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// =================================================================
|
|
|
|
@ -436,7 +445,7 @@ final class FieldSet< |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
for (final Map.Entry<FieldDescriptorType, Object> entry : fields.getOverflowEntries()) { |
|
|
|
|
for (final Map.Entry<T, Object> entry : fields.getOverflowEntries()) { |
|
|
|
|
if (!isInitialized(entry)) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
@ -445,8 +454,9 @@ final class FieldSet< |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
|
private boolean isInitialized(final Map.Entry<FieldDescriptorType, Object> entry) { |
|
|
|
|
final FieldDescriptorType descriptor = entry.getKey(); |
|
|
|
|
private static <T extends FieldDescriptorLite<T>> boolean isInitialized( |
|
|
|
|
final Map.Entry<T, Object> entry) { |
|
|
|
|
final T descriptor = entry.getKey(); |
|
|
|
|
if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { |
|
|
|
|
if (descriptor.isRepeated()) { |
|
|
|
|
for (final MessageLite element : (List<MessageLite>) entry.getValue()) { |
|
|
|
@ -485,16 +495,16 @@ final class FieldSet< |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Like {@link Message.Builder#mergeFrom(Message)}, but merges from another {@link FieldSet}. */ |
|
|
|
|
public void mergeFrom(final FieldSet<FieldDescriptorType> other) { |
|
|
|
|
public void mergeFrom(final FieldSet<T> other) { |
|
|
|
|
for (int i = 0; i < other.fields.getNumArrayEntries(); i++) { |
|
|
|
|
mergeFromField(other.fields.getArrayEntryAt(i)); |
|
|
|
|
} |
|
|
|
|
for (final Map.Entry<FieldDescriptorType, Object> entry : other.fields.getOverflowEntries()) { |
|
|
|
|
for (final Map.Entry<T, Object> entry : other.fields.getOverflowEntries()) { |
|
|
|
|
mergeFromField(entry); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private Object cloneIfMutable(Object value) { |
|
|
|
|
private static Object cloneIfMutable(Object value) { |
|
|
|
|
if (value instanceof byte[]) { |
|
|
|
|
byte[] bytes = (byte[]) value; |
|
|
|
|
byte[] copy = new byte[bytes.length]; |
|
|
|
@ -506,8 +516,8 @@ final class FieldSet< |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@SuppressWarnings({"unchecked", "rawtypes"}) |
|
|
|
|
private void mergeFromField(final Map.Entry<FieldDescriptorType, Object> entry) { |
|
|
|
|
final FieldDescriptorType descriptor = entry.getKey(); |
|
|
|
|
private void mergeFromField(final Map.Entry<T, Object> entry) { |
|
|
|
|
final T descriptor = entry.getKey(); |
|
|
|
|
Object otherValue = entry.getValue(); |
|
|
|
|
if (otherValue instanceof LazyField) { |
|
|
|
|
otherValue = ((LazyField) otherValue).getValue(); |
|
|
|
@ -532,7 +542,6 @@ final class FieldSet< |
|
|
|
|
descriptor |
|
|
|
|
.internalMergeFrom(((MessageLite) value).toBuilder(), (MessageLite) otherValue) |
|
|
|
|
.build(); |
|
|
|
|
|
|
|
|
|
fields.put(descriptor, value); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
@ -567,10 +576,10 @@ final class FieldSet< |
|
|
|
|
/** See {@link Message#writeTo(CodedOutputStream)}. */ |
|
|
|
|
public void writeTo(final CodedOutputStream output) throws IOException { |
|
|
|
|
for (int i = 0; i < fields.getNumArrayEntries(); i++) { |
|
|
|
|
final Map.Entry<FieldDescriptorType, Object> entry = fields.getArrayEntryAt(i); |
|
|
|
|
final Map.Entry<T, Object> entry = fields.getArrayEntryAt(i); |
|
|
|
|
writeField(entry.getKey(), entry.getValue(), output); |
|
|
|
|
} |
|
|
|
|
for (final Map.Entry<FieldDescriptorType, Object> entry : fields.getOverflowEntries()) { |
|
|
|
|
for (final Map.Entry<T, Object> entry : fields.getOverflowEntries()) { |
|
|
|
|
writeField(entry.getKey(), entry.getValue(), output); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -580,15 +589,14 @@ final class FieldSet< |
|
|
|
|
for (int i = 0; i < fields.getNumArrayEntries(); i++) { |
|
|
|
|
writeMessageSetTo(fields.getArrayEntryAt(i), output); |
|
|
|
|
} |
|
|
|
|
for (final Map.Entry<FieldDescriptorType, Object> entry : fields.getOverflowEntries()) { |
|
|
|
|
for (final Map.Entry<T, Object> entry : fields.getOverflowEntries()) { |
|
|
|
|
writeMessageSetTo(entry, output); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void writeMessageSetTo( |
|
|
|
|
final Map.Entry<FieldDescriptorType, Object> entry, final CodedOutputStream output) |
|
|
|
|
private void writeMessageSetTo(final Map.Entry<T, Object> entry, final CodedOutputStream output) |
|
|
|
|
throws IOException { |
|
|
|
|
final FieldDescriptorType descriptor = entry.getKey(); |
|
|
|
|
final T descriptor = entry.getKey(); |
|
|
|
|
if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE |
|
|
|
|
&& !descriptor.isRepeated() |
|
|
|
|
&& !descriptor.isPacked()) { |
|
|
|
@ -750,10 +758,10 @@ final class FieldSet< |
|
|
|
|
public int getSerializedSize() { |
|
|
|
|
int size = 0; |
|
|
|
|
for (int i = 0; i < fields.getNumArrayEntries(); i++) { |
|
|
|
|
final Map.Entry<FieldDescriptorType, Object> entry = fields.getArrayEntryAt(i); |
|
|
|
|
final Map.Entry<T, Object> entry = fields.getArrayEntryAt(i); |
|
|
|
|
size += computeFieldSize(entry.getKey(), entry.getValue()); |
|
|
|
|
} |
|
|
|
|
for (final Map.Entry<FieldDescriptorType, Object> entry : fields.getOverflowEntries()) { |
|
|
|
|
for (final Map.Entry<T, Object> entry : fields.getOverflowEntries()) { |
|
|
|
|
size += computeFieldSize(entry.getKey(), entry.getValue()); |
|
|
|
|
} |
|
|
|
|
return size; |
|
|
|
@ -765,14 +773,14 @@ final class FieldSet< |
|
|
|
|
for (int i = 0; i < fields.getNumArrayEntries(); i++) { |
|
|
|
|
size += getMessageSetSerializedSize(fields.getArrayEntryAt(i)); |
|
|
|
|
} |
|
|
|
|
for (final Map.Entry<FieldDescriptorType, Object> entry : fields.getOverflowEntries()) { |
|
|
|
|
for (final Map.Entry<T, Object> entry : fields.getOverflowEntries()) { |
|
|
|
|
size += getMessageSetSerializedSize(entry); |
|
|
|
|
} |
|
|
|
|
return size; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private int getMessageSetSerializedSize(final Map.Entry<FieldDescriptorType, Object> entry) { |
|
|
|
|
final FieldDescriptorType descriptor = entry.getKey(); |
|
|
|
|
private int getMessageSetSerializedSize(final Map.Entry<T, Object> entry) { |
|
|
|
|
final T descriptor = entry.getKey(); |
|
|
|
|
Object value = entry.getValue(); |
|
|
|
|
if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE |
|
|
|
|
&& !descriptor.isRepeated() |
|
|
|
@ -904,4 +912,385 @@ final class FieldSet< |
|
|
|
|
return computeElementSize(type, number, value); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* A FieldSet Builder that accept a {@link MessageLite.Builder} as a field value. This is useful |
|
|
|
|
* for implementing methods in {@link MessageLite.Builder}. |
|
|
|
|
*/ |
|
|
|
|
static final class Builder<T extends FieldDescriptorLite<T>> { |
|
|
|
|
|
|
|
|
|
private SmallSortedMap<T, Object> fields; |
|
|
|
|
private boolean hasLazyField; |
|
|
|
|
private boolean isMutable; |
|
|
|
|
private boolean hasNestedBuilders; |
|
|
|
|
|
|
|
|
|
private Builder() { |
|
|
|
|
this(SmallSortedMap.<T>newFieldMap(DEFAULT_FIELD_MAP_ARRAY_SIZE)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private Builder(SmallSortedMap<T, Object> fields) { |
|
|
|
|
this.fields = fields; |
|
|
|
|
this.isMutable = true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Creates the FieldSet */ |
|
|
|
|
public FieldSet<T> build() { |
|
|
|
|
if (fields.isEmpty()) { |
|
|
|
|
return FieldSet.emptySet(); |
|
|
|
|
} |
|
|
|
|
isMutable = false; |
|
|
|
|
SmallSortedMap<T, Object> fieldsForBuild = fields; |
|
|
|
|
if (hasNestedBuilders) { |
|
|
|
|
// Make a copy of the fields map with all Builders replaced by Message.
|
|
|
|
|
fieldsForBuild = cloneAllFieldsMap(fields, /* copyList */ false); |
|
|
|
|
replaceBuilders(fieldsForBuild); |
|
|
|
|
} |
|
|
|
|
FieldSet<T> fieldSet = new FieldSet<>(fieldsForBuild); |
|
|
|
|
fieldSet.hasLazyField = hasLazyField; |
|
|
|
|
return fieldSet; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static <T extends FieldDescriptorLite<T>> void replaceBuilders( |
|
|
|
|
SmallSortedMap<T, Object> fieldMap) { |
|
|
|
|
for (int i = 0; i < fieldMap.getNumArrayEntries(); i++) { |
|
|
|
|
replaceBuilders(fieldMap.getArrayEntryAt(i)); |
|
|
|
|
} |
|
|
|
|
for (Map.Entry<T, Object> entry : fieldMap.getOverflowEntries()) { |
|
|
|
|
replaceBuilders(entry); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static <T extends FieldDescriptorLite<T>> void replaceBuilders( |
|
|
|
|
Map.Entry<T, Object> entry) { |
|
|
|
|
entry.setValue(replaceBuilders(entry.getKey(), entry.getValue())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static <T extends FieldDescriptorLite<T>> Object replaceBuilders( |
|
|
|
|
T descriptor, Object value) { |
|
|
|
|
if (value == null) { |
|
|
|
|
return value; |
|
|
|
|
} |
|
|
|
|
if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { |
|
|
|
|
if (descriptor.isRepeated()) { |
|
|
|
|
if (!(value instanceof List)) { |
|
|
|
|
throw new IllegalStateException( |
|
|
|
|
"Repeated field should contains a List but actually contains type: " |
|
|
|
|
+ value.getClass()); |
|
|
|
|
} |
|
|
|
|
@SuppressWarnings("unchecked") // We just check that value is an instance of List above.
|
|
|
|
|
List<Object> list = (List<Object>) value; |
|
|
|
|
for (int i = 0; i < list.size(); i++) { |
|
|
|
|
Object oldElement = list.get(i); |
|
|
|
|
Object newElement = replaceBuilder(oldElement); |
|
|
|
|
if (newElement != oldElement) { |
|
|
|
|
// If the list contains a Message.Builder, then make a copy of that list and then
|
|
|
|
|
// modify the Message.Builder into a Message and return the new list. This way, the
|
|
|
|
|
// existing Message.Builder will still be able to modify the inner fields of the
|
|
|
|
|
// original FieldSet.Builder.
|
|
|
|
|
if (list == value) { |
|
|
|
|
list = new ArrayList<>(list); |
|
|
|
|
} |
|
|
|
|
list.set(i, newElement); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return list; |
|
|
|
|
} else { |
|
|
|
|
return replaceBuilder(value); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return value; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static Object replaceBuilder(Object value) { |
|
|
|
|
return (value instanceof MessageLite.Builder) ? ((MessageLite.Builder) value).build() : value; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Returns a new Builder using the fields from {@code fieldSet}. */ |
|
|
|
|
public static <T extends FieldDescriptorLite<T>> Builder<T> fromFieldSet(FieldSet<T> fieldSet) { |
|
|
|
|
Builder<T> builder = new Builder<T>(cloneAllFieldsMap(fieldSet.fields, /* copyList */ true)); |
|
|
|
|
builder.hasLazyField = fieldSet.hasLazyField; |
|
|
|
|
return builder; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// =================================================================
|
|
|
|
|
|
|
|
|
|
/** Get a simple map containing all the fields. */ |
|
|
|
|
public Map<T, Object> getAllFields() { |
|
|
|
|
if (hasLazyField) { |
|
|
|
|
SmallSortedMap<T, Object> result = cloneAllFieldsMap(fields, /* copyList */ false); |
|
|
|
|
if (fields.isImmutable()) { |
|
|
|
|
result.makeImmutable(); |
|
|
|
|
} else { |
|
|
|
|
replaceBuilders(result); |
|
|
|
|
} |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
return fields.isImmutable() ? fields : Collections.unmodifiableMap(fields); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Useful for implementing {@link Message#hasField(Descriptors.FieldDescriptor)}. */ |
|
|
|
|
public boolean hasField(final T descriptor) { |
|
|
|
|
if (descriptor.isRepeated()) { |
|
|
|
|
throw new IllegalArgumentException("hasField() can only be called on non-repeated fields."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return fields.get(descriptor) != null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Useful for implementing {@link Message#getField(Descriptors.FieldDescriptor)}. This method |
|
|
|
|
* returns {@code null} if the field is not set; in this case it is up to the caller to fetch |
|
|
|
|
* the field's default value. |
|
|
|
|
*/ |
|
|
|
|
public Object getField(final T descriptor) { |
|
|
|
|
Object value = getFieldAllowBuilders(descriptor); |
|
|
|
|
return replaceBuilders(descriptor, value); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Same as {@link #getField(F)}, but allow a {@link MessageLite.Builder} to be returned. */ |
|
|
|
|
Object getFieldAllowBuilders(final T descriptor) { |
|
|
|
|
Object o = fields.get(descriptor); |
|
|
|
|
if (o instanceof LazyField) { |
|
|
|
|
return ((LazyField) o).getValue(); |
|
|
|
|
} |
|
|
|
|
return o; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void ensureIsMutable() { |
|
|
|
|
if (!isMutable) { |
|
|
|
|
fields = cloneAllFieldsMap(fields, /* copyList */ true); |
|
|
|
|
isMutable = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Useful for implementing {@link Message.Builder#setField(Descriptors.FieldDescriptor, |
|
|
|
|
* Object)}. |
|
|
|
|
*/ |
|
|
|
|
@SuppressWarnings({"unchecked", "rawtypes"}) |
|
|
|
|
public void setField(final T descriptor, Object value) { |
|
|
|
|
ensureIsMutable(); |
|
|
|
|
if (descriptor.isRepeated()) { |
|
|
|
|
if (!(value instanceof List)) { |
|
|
|
|
throw new IllegalArgumentException( |
|
|
|
|
"Wrong object type used with protocol message reflection."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Wrap the contents in a new list so that the caller cannot change
|
|
|
|
|
// the list's contents after setting it.
|
|
|
|
|
final List newList = new ArrayList(); |
|
|
|
|
newList.addAll((List) value); |
|
|
|
|
for (final Object element : newList) { |
|
|
|
|
verifyType(descriptor.getLiteType(), element); |
|
|
|
|
hasNestedBuilders = hasNestedBuilders || element instanceof MessageLite.Builder; |
|
|
|
|
} |
|
|
|
|
value = newList; |
|
|
|
|
} else { |
|
|
|
|
verifyType(descriptor.getLiteType(), value); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (value instanceof LazyField) { |
|
|
|
|
hasLazyField = true; |
|
|
|
|
} |
|
|
|
|
hasNestedBuilders = hasNestedBuilders || value instanceof MessageLite.Builder; |
|
|
|
|
|
|
|
|
|
fields.put(descriptor, value); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Useful for implementing {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}. */ |
|
|
|
|
public void clearField(final T descriptor) { |
|
|
|
|
ensureIsMutable(); |
|
|
|
|
fields.remove(descriptor); |
|
|
|
|
if (fields.isEmpty()) { |
|
|
|
|
hasLazyField = false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Useful for implementing {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}. |
|
|
|
|
*/ |
|
|
|
|
public int getRepeatedFieldCount(final T descriptor) { |
|
|
|
|
if (!descriptor.isRepeated()) { |
|
|
|
|
throw new IllegalArgumentException( |
|
|
|
|
"getRepeatedField() can only be called on repeated fields."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
final Object value = getField(descriptor); |
|
|
|
|
if (value == null) { |
|
|
|
|
return 0; |
|
|
|
|
} else { |
|
|
|
|
return ((List<?>) value).size(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Useful for implementing {@link Message#getRepeatedField(Descriptors.FieldDescriptor, int)}. |
|
|
|
|
*/ |
|
|
|
|
public Object getRepeatedField(final T descriptor, final int index) { |
|
|
|
|
if (hasNestedBuilders) { |
|
|
|
|
ensureIsMutable(); |
|
|
|
|
} |
|
|
|
|
Object value = getRepeatedFieldAllowBuilders(descriptor, index); |
|
|
|
|
return replaceBuilder(value); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Same as {@link #getRepeatedField(F, int)}, but allow a {@link MessageLite.Builder} to be |
|
|
|
|
* returned. |
|
|
|
|
*/ |
|
|
|
|
Object getRepeatedFieldAllowBuilders(final T descriptor, final int index) { |
|
|
|
|
if (!descriptor.isRepeated()) { |
|
|
|
|
throw new IllegalArgumentException( |
|
|
|
|
"getRepeatedField() can only be called on repeated fields."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
final Object value = getFieldAllowBuilders(descriptor); |
|
|
|
|
|
|
|
|
|
if (value == null) { |
|
|
|
|
throw new IndexOutOfBoundsException(); |
|
|
|
|
} else { |
|
|
|
|
return ((List<?>) value).get(index); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Useful for implementing {@link Message.Builder#setRepeatedField(Descriptors.FieldDescriptor, |
|
|
|
|
* int, Object)}. |
|
|
|
|
*/ |
|
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
|
public void setRepeatedField(final T descriptor, final int index, final Object value) { |
|
|
|
|
ensureIsMutable(); |
|
|
|
|
if (!descriptor.isRepeated()) { |
|
|
|
|
throw new IllegalArgumentException( |
|
|
|
|
"getRepeatedField() can only be called on repeated fields."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
hasNestedBuilders = hasNestedBuilders || value instanceof MessageLite.Builder; |
|
|
|
|
|
|
|
|
|
final Object list = getField(descriptor); |
|
|
|
|
if (list == null) { |
|
|
|
|
throw new IndexOutOfBoundsException(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
verifyType(descriptor.getLiteType(), value); |
|
|
|
|
((List<Object>) list).set(index, value); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Useful for implementing {@link Message.Builder#addRepeatedField(Descriptors.FieldDescriptor, |
|
|
|
|
* Object)}. |
|
|
|
|
*/ |
|
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
|
public void addRepeatedField(final T descriptor, final Object value) { |
|
|
|
|
ensureIsMutable(); |
|
|
|
|
if (!descriptor.isRepeated()) { |
|
|
|
|
throw new IllegalArgumentException( |
|
|
|
|
"addRepeatedField() can only be called on repeated fields."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
hasNestedBuilders = hasNestedBuilders || value instanceof MessageLite.Builder; |
|
|
|
|
|
|
|
|
|
verifyType(descriptor.getLiteType(), value); |
|
|
|
|
|
|
|
|
|
final Object existingValue = getField(descriptor); |
|
|
|
|
List<Object> list; |
|
|
|
|
if (existingValue == null) { |
|
|
|
|
list = new ArrayList<>(); |
|
|
|
|
fields.put(descriptor, list); |
|
|
|
|
} else { |
|
|
|
|
list = (List<Object>) existingValue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
list.add(value); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Verifies that the given object is of the correct type to be a valid value for the given |
|
|
|
|
* field. (For repeated fields, this checks if the object is the right type to be one element of |
|
|
|
|
* the field.) |
|
|
|
|
* |
|
|
|
|
* @throws IllegalArgumentException The value is not of the right type. |
|
|
|
|
*/ |
|
|
|
|
private static void verifyType(final WireFormat.FieldType type, final Object value) { |
|
|
|
|
if (!FieldSet.isValidType(type, value)) { |
|
|
|
|
// Builder can accept Message.Builder values even though FieldSet will reject.
|
|
|
|
|
if (type.getJavaType() == WireFormat.JavaType.MESSAGE |
|
|
|
|
&& value instanceof MessageLite.Builder) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
throw new IllegalArgumentException( |
|
|
|
|
"Wrong object type used with protocol message reflection."); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* See {@link Message#isInitialized()}. Note: Since {@code FieldSet} itself does not have any |
|
|
|
|
* way of knowing about required fields that aren't actually present in the set, it is up to the |
|
|
|
|
* caller to check that all required fields are present. |
|
|
|
|
*/ |
|
|
|
|
public boolean isInitialized() { |
|
|
|
|
for (int i = 0; i < fields.getNumArrayEntries(); i++) { |
|
|
|
|
if (!FieldSet.isInitialized(fields.getArrayEntryAt(i))) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
for (final Map.Entry<T, Object> entry : fields.getOverflowEntries()) { |
|
|
|
|
if (!FieldSet.isInitialized(entry)) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Like {@link Message.Builder#mergeFrom(Message)}, but merges from another {@link FieldSet}. |
|
|
|
|
*/ |
|
|
|
|
public void mergeFrom(final FieldSet<T> other) { |
|
|
|
|
ensureIsMutable(); |
|
|
|
|
for (int i = 0; i < other.fields.getNumArrayEntries(); i++) { |
|
|
|
|
mergeFromField(other.fields.getArrayEntryAt(i)); |
|
|
|
|
} |
|
|
|
|
for (final Map.Entry<T, Object> entry : other.fields.getOverflowEntries()) { |
|
|
|
|
mergeFromField(entry); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@SuppressWarnings({"unchecked", "rawtypes"}) |
|
|
|
|
private void mergeFromField(final Map.Entry<T, Object> entry) { |
|
|
|
|
final T descriptor = entry.getKey(); |
|
|
|
|
Object otherValue = entry.getValue(); |
|
|
|
|
if (otherValue instanceof LazyField) { |
|
|
|
|
otherValue = ((LazyField) otherValue).getValue(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (descriptor.isRepeated()) { |
|
|
|
|
Object value = getField(descriptor); |
|
|
|
|
if (value == null) { |
|
|
|
|
value = new ArrayList(); |
|
|
|
|
} |
|
|
|
|
for (Object element : (List) otherValue) { |
|
|
|
|
((List) value).add(FieldSet.cloneIfMutable(element)); |
|
|
|
|
} |
|
|
|
|
fields.put(descriptor, value); |
|
|
|
|
} else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { |
|
|
|
|
Object value = getField(descriptor); |
|
|
|
|
if (value == null) { |
|
|
|
|
fields.put(descriptor, FieldSet.cloneIfMutable(otherValue)); |
|
|
|
|
} else { |
|
|
|
|
// Merge the messages.
|
|
|
|
|
if (value instanceof MessageLite.Builder) { |
|
|
|
|
descriptor.internalMergeFrom((MessageLite.Builder) value, (MessageLite) otherValue); |
|
|
|
|
} else { |
|
|
|
|
value = |
|
|
|
|
descriptor |
|
|
|
|
.internalMergeFrom(((MessageLite) value).toBuilder(), (MessageLite) otherValue) |
|
|
|
|
.build(); |
|
|
|
|
fields.put(descriptor, value); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
fields.put(descriptor, cloneIfMutable(otherValue)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|