|
@@ -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));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|