File protobuf-7bff8393cab939bfbb9b5c69b3fe76b4d83c41ee.patch of Package protobuf.28337
From 7bff8393cab939bfbb9b5c69b3fe76b4d83c41ee Mon Sep 17 00:00:00 2001
From: Paul Yang <TeBoring@users.noreply.github.com>
Date: Fri, 19 Jul 2019 14:49:01 -0700
Subject: [PATCH] Down Integrate to GitHub (#6414)
* Down integrate to GitHub
* Fix broken tests
---
Makefile.am | 2
java/core/src/main/java/com/google/protobuf/FieldSet.java | 573 ++++++++--
java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java | 110 +
java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java | 3
java/core/src/main/java/com/google/protobuf/TextFormat.java | 196 +++
java/core/src/main/java/com/google/protobuf/TypeRegistry.java | 160 ++
java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java | 154 ++
java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java | 6
java/core/src/test/java/com/google/protobuf/TextFormatTest.java | 191 +++
java/core/src/test/java/com/google/protobuf/TypeRegistryTest.java | 70 +
java/core/src/test/proto/com/google/protobuf/test_extra_interfaces.proto | 2
java/lite/pom.xml | 1
java/util/src/main/java/com/google/protobuf/util/JsonFormat.java | 140 +-
java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java | 51
src/google/protobuf/compiler/java/java_context.cc | 1
src/google/protobuf/compiler/java/java_doc_comment.cc | 80 -
src/google/protobuf/compiler/java/java_doc_comment.h | 42
src/google/protobuf/compiler/java/java_enum.cc | 11
src/google/protobuf/compiler/java/java_enum_field.cc | 4
src/google/protobuf/compiler/java/java_string_field_lite.cc | 7
src/google/protobuf/compiler/js/js_generator.cc | 138 +-
21 files changed, 1645 insertions(+), 297 deletions(-)
create mode 100755 java/core/src/main/java/com/google/protobuf/TypeRegistry.java
create mode 100755 java/core/src/test/java/com/google/protobuf/TypeRegistryTest.java
--- a/Makefile.am
+++ b/Makefile.am
@@ -339,6 +339,7 @@ java_EXTRA_DIST=
java/core/src/main/java/com/google/protobuf/TextFormatEscaper.java \
java/core/src/main/java/com/google/protobuf/TextFormatParseInfoTree.java \
java/core/src/main/java/com/google/protobuf/TextFormatParseLocation.java \
+ java/core/src/main/java/com/google/protobuf/TypeRegistry.java \
java/core/src/main/java/com/google/protobuf/UninitializedMessageException.java \
java/core/src/main/java/com/google/protobuf/UnknownFieldSchema.java \
java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java \
@@ -435,6 +436,7 @@ java_EXTRA_DIST=
java/core/src/test/java/com/google/protobuf/TextFormatParseInfoTreeTest.java \
java/core/src/test/java/com/google/protobuf/TextFormatParseLocationTest.java \
java/core/src/test/java/com/google/protobuf/TextFormatTest.java \
+ java/core/src/test/java/com/google/protobuf/TypeRegistryTest.java \
java/core/src/test/java/com/google/protobuf/UnknownEnumValueTest.java \
java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java \
java/core/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java \
--- a/java/core/src/main/java/com/google/protobuf/FieldSet.java
+++ b/java/core/src/main/java/com/google/protobuf/FieldSet.java
@@ -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());
- }
- for (Map.Entry<FieldDescriptorType, Object> entry : fields.getOverflowEntries()) {
- 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<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));
+ }
+ }
+ }
}
--- a/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java
+++ b/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java
@@ -1363,7 +1363,7 @@ public abstract class GeneratedMessageV3
extends Builder<BuilderType>
implements ExtendableMessageOrBuilder<MessageType> {
- private FieldSet<FieldDescriptor> extensions = FieldSet.emptySet();
+ private FieldSet.Builder<FieldDescriptor> extensions;
protected ExtendableBuilder() {}
@@ -1374,18 +1374,18 @@ public abstract class GeneratedMessageV3
// For immutable message conversion.
void internalSetExtensionSet(FieldSet<FieldDescriptor> extensions) {
- this.extensions = extensions;
+ this.extensions = FieldSet.Builder.fromFieldSet(extensions);
}
@Override
public BuilderType clear() {
- extensions = FieldSet.emptySet();
+ extensions = null;
return super.clear();
}
private void ensureExtensionsIsMutable() {
- if (extensions.isImmutable()) {
- extensions = extensions.clone();
+ if (extensions == null) {
+ extensions = FieldSet.newBuilder();
}
}
@@ -1408,7 +1408,7 @@ public abstract class GeneratedMessageV3
Extension<MessageType, Type> extension = checkNotLite(extensionLite);
verifyExtensionContainingType(extension);
- return extensions.hasField(extension.getDescriptor());
+ return extensions == null ? false : extensions.hasField(extension.getDescriptor());
}
/** Get the number of elements in a repeated extension. */
@@ -1419,7 +1419,7 @@ public abstract class GeneratedMessageV3
verifyExtensionContainingType(extension);
final FieldDescriptor descriptor = extension.getDescriptor();
- return extensions.getRepeatedFieldCount(descriptor);
+ return extensions == null ? 0 : extensions.getRepeatedFieldCount(descriptor);
}
/** Get the value of an extension. */
@@ -1429,7 +1429,7 @@ public abstract class GeneratedMessageV3
verifyExtensionContainingType(extension);
FieldDescriptor descriptor = extension.getDescriptor();
- final Object value = extensions.getField(descriptor);
+ final Object value = extensions == null ? null : extensions.getField(descriptor);
if (value == null) {
if (descriptor.isRepeated()) {
return (Type) Collections.emptyList();
@@ -1453,8 +1453,11 @@ public abstract class GeneratedMessageV3
verifyExtensionContainingType(extension);
FieldDescriptor descriptor = extension.getDescriptor();
- return (Type) extension.singularFromReflectionType(
- extensions.getRepeatedField(descriptor, index));
+ if (extensions == null) {
+ throw new IndexOutOfBoundsException();
+ }
+ return (Type)
+ extension.singularFromReflectionType(extensions.getRepeatedField(descriptor, index));
}
/** Set the value of an extension. */
@@ -1605,7 +1608,7 @@ public abstract class GeneratedMessageV3
/** Called by subclasses to check if all extensions are initialized. */
protected boolean extensionsAreInitialized() {
- return extensions.isInitialized();
+ return extensions == null ? true : extensions.isInitialized();
}
/**
@@ -1613,8 +1616,9 @@ public abstract class GeneratedMessageV3
* building the message.
*/
private FieldSet<FieldDescriptor> buildExtensions() {
- extensions.makeImmutable();
- return extensions;
+ return extensions == null
+ ? (FieldSet<FieldDescriptor>) FieldSet.emptySet()
+ : extensions.build();
}
@Override
@@ -1628,7 +1632,9 @@ public abstract class GeneratedMessageV3
@Override
public Map<FieldDescriptor, Object> getAllFields() {
final Map<FieldDescriptor, Object> result = super.getAllFieldsMutable();
- result.putAll(extensions.getAllFields());
+ if (extensions != null) {
+ result.putAll(extensions.getAllFields());
+ }
return Collections.unmodifiableMap(result);
}
@@ -1636,7 +1642,7 @@ public abstract class GeneratedMessageV3
public Object getField(final FieldDescriptor field) {
if (field.isExtension()) {
verifyContainingType(field);
- final Object value = extensions.getField(field);
+ final Object value = extensions == null ? null : extensions.getField(field);
if (value == null) {
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
// Lacking an ExtensionRegistry, we have no way to determine the
@@ -1654,10 +1660,43 @@ public abstract class GeneratedMessageV3
}
@Override
+ public Message.Builder getFieldBuilder(final FieldDescriptor field) {
+ if (field.isExtension()) {
+ verifyContainingType(field);
+ if (field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
+ throw new UnsupportedOperationException(
+ "getFieldBuilder() called on a non-Message type.");
+ }
+ ensureExtensionsIsMutable();
+ final Object value = extensions.getFieldAllowBuilders(field);
+ if (value == null) {
+ Message.Builder builder = DynamicMessage.newBuilder(field.getMessageType());
+ extensions.setField(field, builder);
+ onChanged();
+ return builder;
+ } else {
+ if (value instanceof Message.Builder) {
+ return (Message.Builder) value;
+ } else if (value instanceof Message) {
+ Message.Builder builder = ((Message) value).toBuilder();
+ extensions.setField(field, builder);
+ onChanged();
+ return builder;
+ } else {
+ throw new UnsupportedOperationException(
+ "getRepeatedFieldBuilder() called on a non-Message type.");
+ }
+ }
+ } else {
+ return super.getFieldBuilder(field);
+ }
+ }
+
+ @Override
public int getRepeatedFieldCount(final FieldDescriptor field) {
if (field.isExtension()) {
verifyContainingType(field);
- return extensions.getRepeatedFieldCount(field);
+ return extensions == null ? 0 : extensions.getRepeatedFieldCount(field);
} else {
return super.getRepeatedFieldCount(field);
}
@@ -1668,6 +1707,9 @@ public abstract class GeneratedMessageV3
final int index) {
if (field.isExtension()) {
verifyContainingType(field);
+ if (extensions == null) {
+ throw new IndexOutOfBoundsException();
+ }
return extensions.getRepeatedField(field, index);
} else {
return super.getRepeatedField(field, index);
@@ -1675,10 +1717,36 @@ public abstract class GeneratedMessageV3
}
@Override
+ public Message.Builder getRepeatedFieldBuilder(final FieldDescriptor field, final int index) {
+ if (field.isExtension()) {
+ verifyContainingType(field);
+ ensureExtensionsIsMutable();
+ if (field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
+ throw new UnsupportedOperationException(
+ "getRepeatedFieldBuilder() called on a non-Message type.");
+ }
+ final Object value = extensions.getRepeatedFieldAllowBuilders(field, index);
+ if (value instanceof Message.Builder) {
+ return (Message.Builder) value;
+ } else if (value instanceof Message) {
+ Message.Builder builder = ((Message) value).toBuilder();
+ extensions.setRepeatedField(field, index, builder);
+ onChanged();
+ return builder;
+ } else {
+ throw new UnsupportedOperationException(
+ "getRepeatedFieldBuilder() called on a non-Message type.");
+ }
+ } else {
+ return super.getRepeatedFieldBuilder(field, index);
+ }
+ }
+
+ @Override
public boolean hasField(final FieldDescriptor field) {
if (field.isExtension()) {
verifyContainingType(field);
- return extensions.hasField(field);
+ return extensions == null ? false : extensions.hasField(field);
} else {
return super.hasField(field);
}
@@ -1749,9 +1817,11 @@ public abstract class GeneratedMessageV3
}
protected final void mergeExtensionFields(final ExtendableMessage other) {
- ensureExtensionsIsMutable();
- extensions.mergeFrom(other.extensions);
- onChanged();
+ if (other.extensions != null) {
+ ensureExtensionsIsMutable();
+ extensions.mergeFrom(other.extensions);
+ onChanged();
+ }
}
private void verifyContainingType(final FieldDescriptor field) {
--- a/java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java
@@ -32,9 +32,10 @@ package com.google.protobuf;
import com.google.protobuf.Internal.ProtobufList;
import java.util.Arrays;
+import java.util.RandomAccess;
/** Implements {@link ProtobufList} for non-primitive and {@link String} types. */
-final class ProtobufArrayList<E> extends AbstractProtobufList<E> {
+final class ProtobufArrayList<E> extends AbstractProtobufList<E> implements RandomAccess {
private static final ProtobufArrayList<Object> EMPTY_LIST =
new ProtobufArrayList<Object>(new Object[0], 0);
--- a/java/core/src/main/java/com/google/protobuf/TextFormat.java
+++ b/java/core/src/main/java/com/google/protobuf/TextFormat.java
@@ -113,8 +113,8 @@ public final class TextFormat {
}
/**
- * Generates a human readable form of the field, useful for debugging and other purposes, with no
- * newline characters.
+ * Generates a human readable form of the field, useful for debugging and other purposes, with
+ * no newline characters.
*
* @deprecated Use {@code printer().shortDebugString(FieldDescriptor, Object)}
*/
@@ -122,10 +122,10 @@ public final class TextFormat {
public static String shortDebugString(final FieldDescriptor field, final Object value) {
return printer().shortDebugString(field, value);
}
-
+ //
/**
- * Generates a human readable form of the unknown fields, useful for debugging and other purposes,
- * with no newline characters.
+ * Generates a human readable form of the unknown fields, useful for debugging and other
+ * purposes, with no newline characters.
*
* @deprecated Use {@code printer().shortDebugString(UnknownFieldSet)}
*/
@@ -166,8 +166,8 @@ public final class TextFormat {
}
/**
- * Same as {@code printToString()}, except that non-ASCII characters in string type fields are not
- * escaped in backslash+octals.
+ * Same as {@code printToString()}, except that non-ASCII characters in string type fields are
+ * not escaped in backslash+octals.
*
* @deprecated Use {@code printer().escapingNonAscii(false).printToString(UnknownFieldSet)}
*/
@@ -175,20 +175,21 @@ public final class TextFormat {
public static String printToUnicodeString(final UnknownFieldSet fields) {
return printer().escapingNonAscii(false).printToString(fields);
}
-
+ //
/** @deprecated Use {@code printer().printField(FieldDescriptor, Object, Appendable)} */
@Deprecated
public static void printField(
- final FieldDescriptor field, final Object value, final Appendable output) throws IOException {
+ final FieldDescriptor field, final Object value, final Appendable output)
+ throws IOException {
printer().printField(field, value, output);
}
-
+ //
/** @deprecated Use {@code printer().printFieldToString(FieldDescriptor, Object)} */
@Deprecated
public static String printFieldToString(final FieldDescriptor field, final Object value) {
return printer().printFieldToString(field, value);
}
-
+ //
/**
* Outputs a unicode textual representation of the value of given field value.
*
@@ -205,7 +206,8 @@ public final class TextFormat {
*/
@Deprecated
public static void printUnicodeFieldValue(
- final FieldDescriptor field, final Object value, final Appendable output) throws IOException {
+ final FieldDescriptor field, final Object value, final Appendable output)
+ throws IOException {
printer().escapingNonAscii(false).printFieldValue(field, value, output);
}
@@ -285,13 +287,16 @@ public final class TextFormat {
public static final class Printer {
// Printer instance which escapes non-ASCII characters.
- private static final Printer DEFAULT = new Printer(true);
+ private static final Printer DEFAULT = new Printer(true, TypeRegistry.getEmptyTypeRegistry());
/** Whether to escape non ASCII characters with backslash and octal. */
private final boolean escapeNonAscii;
- private Printer(boolean escapeNonAscii) {
+ private final TypeRegistry typeRegistry;
+
+ private Printer(boolean escapeNonAscii, TypeRegistry typeRegistry) {
this.escapeNonAscii = escapeNonAscii;
+ this.typeRegistry = typeRegistry;
}
/**
@@ -304,7 +309,20 @@ public final class TextFormat {
* with the escape mode set to the given parameter.
*/
public Printer escapingNonAscii(boolean escapeNonAscii) {
- return new Printer(escapeNonAscii);
+ return new Printer(escapeNonAscii, typeRegistry);
+ }
+
+ /**
+ * Creates a new {@link Printer} using the given typeRegistry. The new Printer clones all other
+ * configurations from the current {@link Printer}.
+ *
+ * @throws IllegalArgumentException if a registry is already set.
+ */
+ public Printer usingTypeRegistry(TypeRegistry typeRegistry) {
+ if (this.typeRegistry != TypeRegistry.getEmptyTypeRegistry()) {
+ throw new IllegalArgumentException("Only one typeRegistry is allowed.");
+ }
+ return new Printer(escapeNonAscii, typeRegistry);
}
/**
@@ -323,9 +341,66 @@ public final class TextFormat {
private void print(final MessageOrBuilder message, final TextGenerator generator)
throws IOException {
+ if (message.getDescriptorForType().getFullName().equals("google.protobuf.Any")
+ && printAny(message, generator)) {
+ return;
+ }
printMessage(message, generator);
}
+ /**
+ * Attempt to print the 'google.protobuf.Any' message in a human-friendly format. Returns false
+ * if the message isn't a valid 'google.protobuf.Any' message (in which case the message should
+ * be rendered just like a regular message to help debugging).
+ */
+ private boolean printAny(final MessageOrBuilder message, final TextGenerator generator)
+ throws IOException {
+ Descriptor messageType = message.getDescriptorForType();
+ FieldDescriptor typeUrlField = messageType.findFieldByNumber(1);
+ FieldDescriptor valueField = messageType.findFieldByNumber(2);
+ if (typeUrlField == null
+ || typeUrlField.getType() != FieldDescriptor.Type.STRING
+ || valueField == null
+ || valueField.getType() != FieldDescriptor.Type.BYTES) {
+ // The message may look like an Any but isn't actually an Any message (might happen if the
+ // user tries to use DynamicMessage to construct an Any from incomplete Descriptor).
+ return false;
+ }
+ String typeUrl = (String) message.getField(typeUrlField);
+ // If type_url is not set, we will not be able to decode the content of the value, so just
+ // print out the Any like a regular message.
+ if (typeUrl.isEmpty()) {
+ return false;
+ }
+ Object value = message.getField(valueField);
+
+ Message.Builder contentBuilder = null;
+ try {
+ Descriptor contentType = typeRegistry.getDescriptorForTypeUrl(typeUrl);
+ if (contentType == null) {
+ return false;
+ }
+ contentBuilder = DynamicMessage.getDefaultInstance(contentType).newBuilderForType();
+ contentBuilder.mergeFrom((ByteString) value);
+ } catch (InvalidProtocolBufferException e) {
+ // The value of Any is malformed. We cannot print it out nicely, so fallback to printing out
+ // the type_url and value as bytes. Note that we fail open here to be consistent with
+ // text_format.cc, and also to allow a way for users to inspect the content of the broken
+ // message.
+ return false;
+ }
+ generator.print("[");
+ generator.print(typeUrl);
+ generator.print("] {");
+ generator.eol();
+ generator.indent();
+ print(contentBuilder, generator);
+ generator.outdent();
+ generator.print("}");
+ generator.eol();
+ return true;
+ }
+
public String printFieldToString(final FieldDescriptor field, final Object value) {
try {
final StringBuilder text = new StringBuilder();
@@ -1382,6 +1457,7 @@ public final class TextFormat {
FORBID_SINGULAR_OVERWRITES
}
+ private final TypeRegistry typeRegistry;
private final boolean allowUnknownFields;
private final boolean allowUnknownEnumValues;
private final boolean allowUnknownExtensions;
@@ -1389,11 +1465,13 @@ public final class TextFormat {
private TextFormatParseInfoTree.Builder parseInfoTreeBuilder;
private Parser(
+ TypeRegistry typeRegistry,
boolean allowUnknownFields,
boolean allowUnknownEnumValues,
boolean allowUnknownExtensions,
SingularOverwritePolicy singularOverwritePolicy,
TextFormatParseInfoTree.Builder parseInfoTreeBuilder) {
+ this.typeRegistry = typeRegistry;
this.allowUnknownFields = allowUnknownFields;
this.allowUnknownEnumValues = allowUnknownEnumValues;
this.allowUnknownExtensions = allowUnknownExtensions;
@@ -1414,6 +1492,18 @@ public final class TextFormat {
private SingularOverwritePolicy singularOverwritePolicy =
SingularOverwritePolicy.ALLOW_SINGULAR_OVERWRITES;
private TextFormatParseInfoTree.Builder parseInfoTreeBuilder = null;
+ private TypeRegistry typeRegistry = TypeRegistry.getEmptyTypeRegistry();
+
+ /**
+ * Sets the TypeRegistry for resolving Any. If this is not set, TextFormat will not be able to
+ * parse Any unless Any is write as bytes.
+ *
+ * @throws IllegalArgumentException if a registry is already set.
+ */
+ public Builder setTypeRegistry(TypeRegistry typeRegistry) {
+ this.typeRegistry = typeRegistry;
+ return this;
+ }
/**
* Set whether this parser will allow unknown fields. By default, an exception is thrown if an
@@ -1452,6 +1542,7 @@ public final class TextFormat {
public Parser build() {
return new Parser(
+ typeRegistry,
allowUnknownFields,
allowUnknownEnumValues,
allowUnknownExtensions,
@@ -1834,6 +1925,14 @@ public final class TextFormat {
endToken = "}";
}
+ // Try to parse human readable format of Any in the form: [type_url]: { ... }
+ if (field.getMessageType().getFullName().equals("google.protobuf.Any")
+ && tokenizer.tryConsume("[")) {
+ value =
+ consumeAnyFieldValue(
+ tokenizer, extensionRegistry, field, parseTreeBuilder, unknownFields);
+ tokenizer.consume(endToken);
+ } else {
Message defaultInstance = (extension == null) ? null : extension.defaultInstance;
MessageReflection.MergeTarget subField =
target.newMergeTargetForField(field, defaultInstance);
@@ -1846,6 +1945,8 @@ public final class TextFormat {
}
value = subField.finish();
+ }
+
} else {
switch (field.getType()) {
case INT32:
@@ -1951,6 +2052,71 @@ public final class TextFormat {
}
}
+ private Object consumeAnyFieldValue(
+ final Tokenizer tokenizer,
+ final ExtensionRegistry extensionRegistry,
+ final FieldDescriptor field,
+ final TextFormatParseInfoTree.Builder parseTreeBuilder,
+ List<UnknownField> unknownFields)
+ throws ParseException {
+ // Try to parse human readable format of Any in the form: [type_url]: { ... }
+ StringBuilder typeUrlBuilder = new StringBuilder();
+ // Parse the type_url inside [].
+ while (true) {
+ typeUrlBuilder.append(tokenizer.consumeIdentifier());
+ if (tokenizer.tryConsume("]")) {
+ break;
+ }
+ if (tokenizer.tryConsume("/")) {
+ typeUrlBuilder.append("/");
+ } else if (tokenizer.tryConsume(".")) {
+ typeUrlBuilder.append(".");
+ } else {
+ throw tokenizer.parseExceptionPreviousToken("Expected a valid type URL.");
+ }
+ }
+ tokenizer.tryConsume(":");
+ final String anyEndToken;
+ if (tokenizer.tryConsume("<")) {
+ anyEndToken = ">";
+ } else {
+ tokenizer.consume("{");
+ anyEndToken = "}";
+ }
+ String typeUrl = typeUrlBuilder.toString();
+ Descriptor contentType = null;
+ try {
+ contentType = typeRegistry.getDescriptorForTypeUrl(typeUrl);
+ } catch (InvalidProtocolBufferException e) {
+ throw tokenizer.parseException("Invalid valid type URL. Found: " + typeUrl);
+ }
+ if (contentType == null) {
+ throw tokenizer.parseException(
+ "Unable to parse Any of type: "
+ + typeUrl
+ + ". Please make sure that the TypeRegistry contains the descriptors for the given"
+ + " types.");
+ }
+ Message.Builder contentBuilder =
+ DynamicMessage.getDefaultInstance(contentType).newBuilderForType();
+ MessageReflection.BuilderAdapter contentTarget =
+ new MessageReflection.BuilderAdapter(contentBuilder);
+ while (!tokenizer.tryConsume(anyEndToken)) {
+ mergeField(tokenizer, extensionRegistry, contentTarget, parseTreeBuilder, unknownFields);
+ }
+
+ // Serialize the content and put it back into an Any. Note that we can't depend on Any here
+ // because of a cyclic dependency (java_proto_library for any_java_proto depends on the
+ // protobuf_impl), so we need to construct the Any using proto reflection.
+ Descriptor anyDescriptor = field.getMessageType();
+ Message.Builder anyBuilder =
+ DynamicMessage.getDefaultInstance(anyDescriptor).newBuilderForType();
+ anyBuilder.setField(anyDescriptor.findFieldByName("type_url"), typeUrlBuilder.toString());
+ anyBuilder.setField(
+ anyDescriptor.findFieldByName("value"), contentBuilder.build().toByteString());
+
+ return anyBuilder.build();
+ }
/** Skips the next field including the field's name and value. */
private void skipField(Tokenizer tokenizer) throws ParseException {
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/TypeRegistry.java
@@ -0,0 +1,160 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import com.google.protobuf.Descriptors.Descriptor;
+import com.google.protobuf.Descriptors.FileDescriptor;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Logger;
+
+/**
+ * A TypeRegistry is used to resolve Any messages. You must provide a TypeRegistry containing all
+ * message types used in Any message fields.
+ */
+public class TypeRegistry {
+ private static final Logger logger = Logger.getLogger(TypeRegistry.class.getName());
+
+ private static class EmptyTypeRegistryHolder {
+ private static final TypeRegistry EMPTY =
+ new TypeRegistry(Collections.<String, Descriptor>emptyMap());
+ }
+
+ public static TypeRegistry getEmptyTypeRegistry() {
+ return EmptyTypeRegistryHolder.EMPTY;
+ }
+
+
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ /**
+ * Find a type by its full name. Returns null if it cannot be found in this {@link TypeRegistry}.
+ */
+ public Descriptor find(String name) {
+ return types.get(name);
+ }
+
+ /**
+ * Find a type by its typeUrl. Returns null if it cannot be found in this {@link TypeRegistry}.
+ */
+ /* @Nullable */
+ public final Descriptor getDescriptorForTypeUrl(String typeUrl)
+ throws InvalidProtocolBufferException {
+ return find(getTypeName(typeUrl));
+ }
+
+ private final Map<String, Descriptor> types;
+
+ TypeRegistry(Map<String, Descriptor> types) {
+ this.types = types;
+ }
+
+ private static String getTypeName(String typeUrl) throws InvalidProtocolBufferException {
+ String[] parts = typeUrl.split("/");
+ if (parts.length == 1) {
+ throw new InvalidProtocolBufferException("Invalid type url found: " + typeUrl);
+ }
+ return parts[parts.length - 1];
+ }
+
+ /** A Builder is used to build {@link TypeRegistry}. */
+ public static final class Builder {
+ private Builder() {}
+
+ /**
+ * Adds a message type and all types defined in the same .proto file as well as all transitively
+ * imported .proto files to this {@link Builder}.
+ */
+ public Builder add(Descriptor messageType) {
+ if (types == null) {
+ throw new IllegalStateException("A TypeRegistry.Builder can only be used once.");
+ }
+ addFile(messageType.getFile());
+ return this;
+ }
+
+ /**
+ * Adds message types and all types defined in the same .proto file as well as all transitively
+ * imported .proto files to this {@link Builder}.
+ */
+ public Builder add(Iterable<Descriptor> messageTypes) {
+ if (types == null) {
+ throw new IllegalStateException("A TypeRegistry.Builder can only be used once.");
+ }
+ for (Descriptor type : messageTypes) {
+ addFile(type.getFile());
+ }
+ return this;
+ }
+
+ /** Builds a {@link TypeRegistry}. This method can only be called once for one Builder. */
+ public TypeRegistry build() {
+ TypeRegistry result = new TypeRegistry(types);
+ // Make sure the built {@link TypeRegistry} is immutable.
+ types = null;
+ return result;
+ }
+
+ private void addFile(FileDescriptor file) {
+ // Skip the file if it's already added.
+ if (!files.add(file.getFullName())) {
+ return;
+ }
+ for (FileDescriptor dependency : file.getDependencies()) {
+ addFile(dependency);
+ }
+ for (Descriptor message : file.getMessageTypes()) {
+ addMessage(message);
+ }
+ }
+
+ private void addMessage(Descriptor message) {
+ for (Descriptor nestedType : message.getNestedTypes()) {
+ addMessage(nestedType);
+ }
+
+ if (types.containsKey(message.getFullName())) {
+ logger.warning("Type " + message.getFullName() + " is added multiple times.");
+ return;
+ }
+
+ types.put(message.getFullName(), message);
+ }
+
+ private final Set<String> files = new HashSet<>();
+ private Map<String, Descriptor> types = new HashMap<>();
+ }
+}
--- a/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java
+++ b/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java
@@ -115,6 +115,160 @@ public class GeneratedMessageTest extend
GeneratedMessageV3.setAlwaysUseFieldBuildersForTesting(false);
}
+ public void testGetFieldBuilderForExtensionField() {
+ TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
+ Message.Builder fieldBuilder =
+ builder.getFieldBuilder(UnittestProto.optionalNestedMessageExtension.getDescriptor());
+ int expected = 7432;
+ FieldDescriptor field =
+ NestedMessage.getDescriptor().findFieldByNumber(NestedMessage.BB_FIELD_NUMBER);
+ fieldBuilder.setField(field, expected);
+ assertEquals(
+ expected,
+ builder.build().getExtension(UnittestProto.optionalNestedMessageExtension).getBb());
+
+ // fieldBuilder still updates the builder after builder build() has been called.
+ expected += 100;
+ fieldBuilder.setField(field, expected);
+ assertEquals(
+ expected,
+ builder.build().getExtension(UnittestProto.optionalNestedMessageExtension).getBb());
+ }
+
+ public void testGetFieldBuilderWithExistingMessage() {
+ TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
+ builder.setExtension(
+ UnittestProto.optionalNestedMessageExtension,
+ NestedMessage.newBuilder().setBb(123).build());
+ Message.Builder fieldBuilder =
+ builder.getFieldBuilder(UnittestProto.optionalNestedMessageExtension.getDescriptor());
+ int expected = 7432;
+ FieldDescriptor field =
+ NestedMessage.getDescriptor().findFieldByNumber(NestedMessage.BB_FIELD_NUMBER);
+ fieldBuilder.setField(field, expected);
+ assertEquals(
+ expected,
+ builder.build().getExtension(UnittestProto.optionalNestedMessageExtension).getBb());
+
+ // fieldBuilder still updates the builder after builder build() has been called.
+ expected += 100;
+ fieldBuilder.setField(field, expected);
+ assertEquals(
+ expected,
+ builder.build().getExtension(UnittestProto.optionalNestedMessageExtension).getBb());
+ }
+
+ public void testGetFieldBuilderWithExistingBuilder() {
+ TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
+ NestedMessage.Builder nestedMessageBuilder = NestedMessage.newBuilder().setBb(123);
+ builder.setField(
+ UnittestProto.optionalNestedMessageExtension.getDescriptor(), nestedMessageBuilder);
+ Message.Builder fieldBuilder =
+ builder.getFieldBuilder(UnittestProto.optionalNestedMessageExtension.getDescriptor());
+ int expected = 7432;
+ FieldDescriptor field =
+ NestedMessage.getDescriptor().findFieldByNumber(NestedMessage.BB_FIELD_NUMBER);
+ fieldBuilder.setField(field, expected);
+ assertEquals(
+ expected,
+ builder.build().getExtension(UnittestProto.optionalNestedMessageExtension).getBb());
+
+ // Existing nestedMessageBuilder will also update builder.
+ expected += 100;
+ nestedMessageBuilder.setBb(expected);
+ assertEquals(
+ expected,
+ builder.build().getExtension(UnittestProto.optionalNestedMessageExtension).getBb());
+
+ // fieldBuilder still updates the builder.
+ expected += 100;
+ fieldBuilder.setField(field, expected);
+ assertEquals(
+ expected,
+ builder.build().getExtension(UnittestProto.optionalNestedMessageExtension).getBb());
+ }
+
+ public void testGetRepeatedFieldBuilderForExtensionField() {
+ TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
+ builder.addExtension(
+ UnittestProto.repeatedNestedMessageExtension,
+ NestedMessage.newBuilder().setBb(123).build());
+ Message.Builder fieldBuilder =
+ builder.getRepeatedFieldBuilder(
+ UnittestProto.repeatedNestedMessageExtension.getDescriptor(), 0);
+ int expected = 7432;
+ FieldDescriptor field =
+ NestedMessage.getDescriptor().findFieldByNumber(NestedMessage.BB_FIELD_NUMBER);
+ fieldBuilder.setField(field, expected);
+ assertEquals(
+ expected,
+ builder.build().getExtension(UnittestProto.repeatedNestedMessageExtension, 0).getBb());
+
+ // fieldBuilder still updates the builder after builder build() has been called.
+ expected += 100;
+ fieldBuilder.setField(field, expected);
+ assertEquals(
+ expected,
+ builder.build().getExtension(UnittestProto.repeatedNestedMessageExtension, 0).getBb());
+ }
+
+ public void testGetRepeatedFieldBuilderForExistingBuilder() {
+ TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
+ NestedMessage.Builder nestedMessageBuilder = NestedMessage.newBuilder().setBb(123);
+ builder.addRepeatedField(
+ UnittestProto.repeatedNestedMessageExtension.getDescriptor(), nestedMessageBuilder);
+ Message.Builder fieldBuilder =
+ builder.getRepeatedFieldBuilder(
+ UnittestProto.repeatedNestedMessageExtension.getDescriptor(), 0);
+ int expected = 7432;
+ FieldDescriptor field =
+ NestedMessage.getDescriptor().findFieldByNumber(NestedMessage.BB_FIELD_NUMBER);
+ fieldBuilder.setField(field, expected);
+ assertEquals(
+ expected,
+ builder.build().getExtension(UnittestProto.repeatedNestedMessageExtension, 0).getBb());
+
+ // Existing nestedMessageBuilder will also update builder.
+ expected += 100;
+ nestedMessageBuilder.setBb(expected);
+ assertEquals(
+ expected,
+ builder.build().getExtension(UnittestProto.repeatedNestedMessageExtension, 0).getBb());
+
+ // fieldBuilder still updates the builder.
+ expected += 100;
+ fieldBuilder.setField(field, expected);
+ assertEquals(
+ expected,
+ builder.build().getExtension(UnittestProto.repeatedNestedMessageExtension, 0).getBb());
+ }
+
+ public void testGetExtensionFieldOutOfBound() {
+ TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
+ try {
+ builder.getRepeatedField(UnittestProto.repeatedNestedMessageExtension.getDescriptor(), 0);
+ fail("Expected IndexOutOfBoundsException to be thrown");
+ } catch (IndexOutOfBoundsException expected) {
+ }
+ try {
+ builder.getExtension(UnittestProto.repeatedNestedMessageExtension, 0);
+ fail("Expected IndexOutOfBoundsException to be thrown");
+ } catch (IndexOutOfBoundsException expected) {
+ }
+ TestAllExtensions extensionsMessage = builder.build();
+ try {
+ extensionsMessage.getRepeatedField(
+ UnittestProto.repeatedNestedMessageExtension.getDescriptor(), 0);
+ fail("Expected IndexOutOfBoundsException to be thrown");
+ } catch (IndexOutOfBoundsException expected) {
+ }
+ try {
+ extensionsMessage.getExtension(UnittestProto.repeatedNestedMessageExtension, 0);
+ fail("Expected IndexOutOfBoundsException to be thrown");
+ } catch (IndexOutOfBoundsException expected) {
+ }
+ }
+
public void testDefaultInstance() throws Exception {
assertSame(
TestAllTypes.getDefaultInstance(),
--- a/java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java
+++ b/java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java
@@ -30,6 +30,7 @@
package com.google.protobuf;
+import protobuf_unittest.Engine;
import protobuf_unittest.Vehicle;
import protobuf_unittest.Wheel;
import java.util.ArrayList;
@@ -64,7 +65,7 @@ public class NestedBuildersTest extends
for (int i = 0; i < 4; i++) {
vehicleBuilder.getWheelBuilder(i).setRadius(5).setWidth(i + 10);
}
- vehicleBuilder.getEngineBuilder().setLiters(20);
+ Engine.Builder engineBuilder = vehicleBuilder.getEngineBuilder().setLiters(20);
vehicle = vehicleBuilder.build();
for (int i = 0; i < 4; i++) {
@@ -74,6 +75,9 @@ public class NestedBuildersTest extends
}
assertEquals(20, vehicle.getEngine().getLiters());
assertTrue(vehicle.hasEngine());
+
+ engineBuilder.setLiters(50);
+ assertEquals(50, vehicleBuilder.getEngine().getLiters());
}
public void testMessagesAreCached() {
--- a/java/core/src/test/java/com/google/protobuf/TextFormatTest.java
+++ b/java/core/src/test/java/com/google/protobuf/TextFormatTest.java
@@ -33,9 +33,14 @@ package com.google.protobuf;
import static com.google.protobuf.TestUtil.TEST_REQUIRED_INITIALIZED;
import static com.google.protobuf.TestUtil.TEST_REQUIRED_UNINITIALIZED;
+import com.google.protobuf.DescriptorProtos.DescriptorProto;
+import com.google.protobuf.DescriptorProtos.FieldDescriptorProto;
+import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
+import com.google.protobuf.Descriptors.FileDescriptor;
import com.google.protobuf.TextFormat.Parser.SingularOverwritePolicy;
+import any_test.AnyTestProto.TestAny;
import map_test.MapTestProto.TestMap;
import protobuf_unittest.UnittestMset.TestMessageSetExtension1;
import protobuf_unittest.UnittestMset.TestMessageSetExtension2;
@@ -48,6 +53,7 @@ import protobuf_unittest.UnittestProto.T
import protobuf_unittest.UnittestProto.TestRequired;
import proto2_wireformat_unittest.UnittestMsetWireFormat.TestMessageSet;
import java.io.StringReader;
+import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;
import junit.framework.TestCase;
@@ -506,6 +512,191 @@ public class TextFormatTest extends Test
assertEquals(2, builder.getOptionalInt64());
}
+ public void testPrintAny_customBuiltTypeRegistry() throws Exception {
+ TestAny testAny =
+ TestAny.newBuilder()
+ .setValue(
+ Any.newBuilder()
+ .setTypeUrl("type.googleapis.com/" + TestAllTypes.getDescriptor().getFullName())
+ .setValue(
+ TestAllTypes.newBuilder().setOptionalInt32(12345).build().toByteString())
+ .build())
+ .build();
+ String actual =
+ TextFormat.printer()
+ .usingTypeRegistry(TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build())
+ .printToString(testAny);
+ String expected =
+ "value {\n"
+ + " [type.googleapis.com/protobuf_unittest.TestAllTypes] {\n"
+ + " optional_int32: 12345\n"
+ + " }\n"
+ + "}\n";
+ assertEquals(expected, actual);
+ }
+
+ private static Descriptor createDescriptorForAny(FieldDescriptorProto... fields)
+ throws Exception {
+ FileDescriptor fileDescriptor =
+ FileDescriptor.buildFrom(
+ FileDescriptorProto.newBuilder()
+ .setName("any.proto")
+ .setPackage("google.protobuf")
+ .setSyntax("proto3")
+ .addMessageType(
+ DescriptorProto.newBuilder()
+ .setName("Any")
+ .addAllField(Arrays.asList(fields)))
+ .build(),
+ new FileDescriptor[0]);
+ return fileDescriptor.getMessageTypes().get(0);
+ }
+
+ public void testPrintAny_anyWithDynamicMessage() throws Exception {
+ Descriptor descriptor =
+ createDescriptorForAny(
+ FieldDescriptorProto.newBuilder()
+ .setName("type_url")
+ .setNumber(1)
+ .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
+ .setType(FieldDescriptorProto.Type.TYPE_STRING)
+ .build(),
+ FieldDescriptorProto.newBuilder()
+ .setName("value")
+ .setNumber(2)
+ .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
+ .setType(FieldDescriptorProto.Type.TYPE_BYTES)
+ .build());
+ DynamicMessage testAny =
+ DynamicMessage.newBuilder(descriptor)
+ .setField(
+ descriptor.findFieldByNumber(1),
+ "type.googleapis.com/" + TestAllTypes.getDescriptor().getFullName())
+ .setField(
+ descriptor.findFieldByNumber(2),
+ TestAllTypes.newBuilder().setOptionalInt32(12345).build().toByteString())
+ .build();
+ String actual =
+ TextFormat.printer()
+ .usingTypeRegistry(TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build())
+ .printToString(testAny);
+ String expected =
+ "[type.googleapis.com/protobuf_unittest.TestAllTypes] {\n"
+ + " optional_int32: 12345\n"
+ + "}\n";
+ assertEquals(expected, actual);
+ }
+
+ public void testPrintAny_anyFromWithNoValueField() throws Exception {
+ Descriptor descriptor =
+ createDescriptorForAny(
+ FieldDescriptorProto.newBuilder()
+ .setName("type_url")
+ .setNumber(1)
+ .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
+ .setType(FieldDescriptorProto.Type.TYPE_STRING)
+ .build());
+ DynamicMessage testAny =
+ DynamicMessage.newBuilder(descriptor)
+ .setField(
+ descriptor.findFieldByNumber(1),
+ "type.googleapis.com/" + TestAllTypes.getDescriptor().getFullName())
+ .build();
+ String actual =
+ TextFormat.printer()
+ .usingTypeRegistry(TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build())
+ .printToString(testAny);
+ String expected = "type_url: \"type.googleapis.com/protobuf_unittest.TestAllTypes\"\n";
+ assertEquals(expected, actual);
+ }
+
+ public void testPrintAny_anyFromWithNoTypeUrlField() throws Exception {
+ Descriptor descriptor =
+ createDescriptorForAny(
+ FieldDescriptorProto.newBuilder()
+ .setName("value")
+ .setNumber(2)
+ .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
+ .setType(FieldDescriptorProto.Type.TYPE_BYTES)
+ .build());
+ DynamicMessage testAny =
+ DynamicMessage.newBuilder(descriptor)
+ .setField(
+ descriptor.findFieldByNumber(2),
+ TestAllTypes.newBuilder().setOptionalInt32(12345).build().toByteString())
+ .build();
+ String actual =
+ TextFormat.printer()
+ .usingTypeRegistry(TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build())
+ .printToString(testAny);
+ String expected = "value: \"\\b\\271`\"\n";
+ assertEquals(expected, actual);
+ }
+
+ public void testPrintAny_anyWithInvalidFieldType() throws Exception {
+ Descriptor descriptor =
+ createDescriptorForAny(
+ FieldDescriptorProto.newBuilder()
+ .setName("type_url")
+ .setNumber(1)
+ .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
+ .setType(FieldDescriptorProto.Type.TYPE_STRING)
+ .build(),
+ FieldDescriptorProto.newBuilder()
+ .setName("value")
+ .setNumber(2)
+ .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
+ .setType(FieldDescriptorProto.Type.TYPE_STRING)
+ .build());
+ DynamicMessage testAny =
+ DynamicMessage.newBuilder(descriptor)
+ .setField(
+ descriptor.findFieldByNumber(1),
+ "type.googleapis.com/" + TestAllTypes.getDescriptor().getFullName())
+ .setField(descriptor.findFieldByNumber(2), "test")
+ .build();
+ String actual =
+ TextFormat.printer()
+ .usingTypeRegistry(TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build())
+ .printToString(testAny);
+ String expected =
+ "type_url: \"type.googleapis.com/protobuf_unittest.TestAllTypes\"\n" + "value: \"test\"\n";
+ assertEquals(expected, actual);
+ }
+
+
+ public void testMergeAny_customBuiltTypeRegistry() throws Exception {
+ TestAny.Builder builder = TestAny.newBuilder();
+ TextFormat.Parser.newBuilder()
+ .setTypeRegistry(TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build())
+ .build()
+ .merge(
+ "value: {\n"
+ + "[type.googleapis.com/protobuf_unittest.TestAllTypes] {\n"
+ + "optional_int32: 12345\n"
+ + "optional_nested_message {\n"
+ + " bb: 123\n"
+ + "}\n"
+ + "}\n"
+ + "}",
+ builder);
+ assertEquals(
+ TestAny.newBuilder()
+ .setValue(
+ Any.newBuilder()
+ .setTypeUrl("type.googleapis.com/" + TestAllTypes.getDescriptor().getFullName())
+ .setValue(
+ TestAllTypes.newBuilder()
+ .setOptionalInt32(12345)
+ .setOptionalNestedMessage(
+ TestAllTypes.NestedMessage.newBuilder().setBb(123))
+ .build()
+ .toByteString())
+ .build())
+ .build(),
+ builder.build());
+ }
+
private void assertParseError(String error, String text) {
// Test merge().
--- /dev/null
+++ b/java/core/src/test/java/com/google/protobuf/TypeRegistryTest.java
@@ -0,0 +1,70 @@
+// 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 static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import com.google.protobuf.Descriptors.Descriptor;
+import protobuf_unittest.UnittestProto;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class TypeRegistryTest {
+
+ @Test
+ public void findDescriptorByFullName() throws Exception {
+ Descriptor descriptor = UnittestProto.TestAllTypes.getDescriptor();
+ assertNull(TypeRegistry.getEmptyTypeRegistry().find(descriptor.getFullName()));
+
+ assertSame(
+ descriptor,
+ TypeRegistry.newBuilder().add(descriptor).build().find(descriptor.getFullName()));
+ }
+
+ @Test
+ public void findDescriptorByTypeUrl() throws Exception {
+ Descriptor descriptor = UnittestProto.TestAllTypes.getDescriptor();
+ assertNull(
+ TypeRegistry.getEmptyTypeRegistry()
+ .getDescriptorForTypeUrl("type.googleapis.com/" + descriptor.getFullName()));
+
+ assertSame(
+ descriptor,
+ TypeRegistry.newBuilder()
+ .add(descriptor)
+ .build()
+ .getDescriptorForTypeUrl("type.googleapis.com/" + descriptor.getFullName()));
+ }
+
+}
--- a/java/core/src/test/proto/com/google/protobuf/test_extra_interfaces.proto
+++ b/java/core/src/test/proto/com/google/protobuf/test_extra_interfaces.proto
@@ -36,8 +36,6 @@ package protobuf_unittest;
message Proto1 {
option experimental_java_message_interface =
"com.google.protobuf.ExtraInterfaces.HasBoolValue";
- option experimental_java_interface_extends =
- "com.google.protobuf.ExtraInterfaces.HasByteValue";
option experimental_java_message_interface =
"com.google.protobuf.ExtraInterfaces.HasStringValue<Proto1>";
option experimental_java_builder_interface =
--- a/java/lite/pom.xml
+++ b/java/lite/pom.xml
@@ -224,6 +224,7 @@
<exclude>TextFormatParseLocationTest.java</exclude>
<exclude>TextFormatTest.java</exclude>
<exclude>TestUtil.java</exclude>
+ <exclude>TypeRegistryTest.java</exclude>
<exclude>UnknownEnumValueTest.java</exclude>
<exclude>UnknownFieldSetLiteTest.java</exclude>
<exclude>UnknownFieldSetTest.java</exclude>
--- a/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
+++ b/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
@@ -108,15 +108,22 @@ public class JsonFormat {
*/
public static Printer printer() {
return new Printer(
- TypeRegistry.getEmptyTypeRegistry(), false, Collections.<FieldDescriptor>emptySet(),
- false, false, false, false);
+ com.google.protobuf.TypeRegistry.getEmptyTypeRegistry(),
+ TypeRegistry.getEmptyTypeRegistry(),
+ /* alwaysOutputDefaultValueFields */ false,
+ /* includingDefaultValueFields */ Collections.<FieldDescriptor>emptySet(),
+ /* preservingProtoFieldNames */ false,
+ /* omittingInsignificantWhitespace */ false,
+ /* printingEnumsAsInts */ false,
+ /* sortingMapKeys */ false);
}
/**
* A Printer converts protobuf message to JSON format.
*/
public static class Printer {
- private final TypeRegistry registry;
+ private final com.google.protobuf.TypeRegistry registry;
+ private final TypeRegistry oldRegistry;
// NOTE: There are 3 states for these *defaultValueFields variables:
// 1) Default - alwaysOutput is false & including is empty set. Fields only output if they are
// set to non-default values.
@@ -133,7 +140,8 @@ public class JsonFormat {
private final boolean sortingMapKeys;
private Printer(
- TypeRegistry registry,
+ com.google.protobuf.TypeRegistry registry,
+ TypeRegistry oldRegistry,
boolean alwaysOutputDefaultValueFields,
Set<FieldDescriptor> includingDefaultValueFields,
boolean preservingProtoFieldNames,
@@ -141,6 +149,7 @@ public class JsonFormat {
boolean printingEnumsAsInts,
boolean sortingMapKeys) {
this.registry = registry;
+ this.oldRegistry = oldRegistry;
this.alwaysOutputDefaultValueFields = alwaysOutputDefaultValueFields;
this.includingDefaultValueFields = includingDefaultValueFields;
this.preservingProtoFieldNames = preservingProtoFieldNames;
@@ -150,17 +159,41 @@ public class JsonFormat {
}
/**
- * Creates a new {@link Printer} using the given registry. The new Printer
- * clones all other configurations from the current {@link Printer}.
+ * Creates a new {@link Printer} using the given registry. The new Printer clones all other
+ * configurations from the current {@link Printer}.
*
* @throws IllegalArgumentException if a registry is already set.
*/
- public Printer usingTypeRegistry(TypeRegistry registry) {
- if (this.registry != TypeRegistry.getEmptyTypeRegistry()) {
+ public Printer usingTypeRegistry(TypeRegistry oldRegistry) {
+ if (this.oldRegistry != TypeRegistry.getEmptyTypeRegistry()
+ || this.registry != com.google.protobuf.TypeRegistry.getEmptyTypeRegistry()) {
+ throw new IllegalArgumentException("Only one registry is allowed.");
+ }
+ return new Printer(
+ com.google.protobuf.TypeRegistry.getEmptyTypeRegistry(),
+ oldRegistry,
+ alwaysOutputDefaultValueFields,
+ includingDefaultValueFields,
+ preservingProtoFieldNames,
+ omittingInsignificantWhitespace,
+ printingEnumsAsInts,
+ sortingMapKeys);
+ }
+
+ /**
+ * Creates a new {@link Printer} using the given registry. The new Printer clones all other
+ * configurations from the current {@link Printer}.
+ *
+ * @throws IllegalArgumentException if a registry is already set.
+ */
+ public Printer usingTypeRegistry(com.google.protobuf.TypeRegistry registry) {
+ if (this.oldRegistry != TypeRegistry.getEmptyTypeRegistry()
+ || this.registry != com.google.protobuf.TypeRegistry.getEmptyTypeRegistry()) {
throw new IllegalArgumentException("Only one registry is allowed.");
}
return new Printer(
registry,
+ oldRegistry,
alwaysOutputDefaultValueFields,
includingDefaultValueFields,
preservingProtoFieldNames,
@@ -179,6 +212,7 @@ public class JsonFormat {
checkUnsetIncludingDefaultValueFields();
return new Printer(
registry,
+ oldRegistry,
true,
Collections.<FieldDescriptor>emptySet(),
preservingProtoFieldNames,
@@ -197,6 +231,7 @@ public class JsonFormat {
checkUnsetPrintingEnumsAsInts();
return new Printer(
registry,
+ oldRegistry,
alwaysOutputDefaultValueFields,
Collections.<FieldDescriptor>emptySet(),
preservingProtoFieldNames,
@@ -226,6 +261,7 @@ public class JsonFormat {
checkUnsetIncludingDefaultValueFields();
return new Printer(
registry,
+ oldRegistry,
false,
Collections.unmodifiableSet(new HashSet<>(fieldsToAlwaysOutput)),
preservingProtoFieldNames,
@@ -250,6 +286,7 @@ public class JsonFormat {
public Printer preservingProtoFieldNames() {
return new Printer(
registry,
+ oldRegistry,
alwaysOutputDefaultValueFields,
includingDefaultValueFields,
true,
@@ -279,6 +316,7 @@ public class JsonFormat {
public Printer omittingInsignificantWhitespace() {
return new Printer(
registry,
+ oldRegistry,
alwaysOutputDefaultValueFields,
includingDefaultValueFields,
preservingProtoFieldNames,
@@ -302,6 +340,7 @@ public class JsonFormat {
public Printer sortingMapKeys() {
return new Printer(
registry,
+ oldRegistry,
alwaysOutputDefaultValueFields,
includingDefaultValueFields,
preservingProtoFieldNames,
@@ -322,6 +361,7 @@ public class JsonFormat {
// mobile.
new PrinterImpl(
registry,
+ oldRegistry,
alwaysOutputDefaultValueFields,
includingDefaultValueFields,
preservingProtoFieldNames,
@@ -354,37 +394,66 @@ public class JsonFormat {
* Creates a {@link Parser} with default configuration.
*/
public static Parser parser() {
- return new Parser(TypeRegistry.getEmptyTypeRegistry(), false, Parser.DEFAULT_RECURSION_LIMIT);
+ return new Parser(
+ com.google.protobuf.TypeRegistry.getEmptyTypeRegistry(),
+ TypeRegistry.getEmptyTypeRegistry(),
+ false,
+ Parser.DEFAULT_RECURSION_LIMIT);
}
/**
* A Parser parses JSON to protobuf message.
*/
public static class Parser {
- private final TypeRegistry registry;
+ private final com.google.protobuf.TypeRegistry registry;
+ private final TypeRegistry oldRegistry;
private final boolean ignoringUnknownFields;
private final int recursionLimit;
// The default parsing recursion limit is aligned with the proto binary parser.
private static final int DEFAULT_RECURSION_LIMIT = 100;
- private Parser(TypeRegistry registry, boolean ignoreUnknownFields, int recursionLimit) {
+ private Parser(
+ com.google.protobuf.TypeRegistry registry,
+ TypeRegistry oldRegistry,
+ boolean ignoreUnknownFields,
+ int recursionLimit) {
this.registry = registry;
+ this.oldRegistry = oldRegistry;
this.ignoringUnknownFields = ignoreUnknownFields;
this.recursionLimit = recursionLimit;
}
/**
- * Creates a new {@link Parser} using the given registry. The new Parser
- * clones all other configurations from this Parser.
+ * Creates a new {@link Parser} using the given registry. The new Parser clones all other
+ * configurations from this Parser.
*
* @throws IllegalArgumentException if a registry is already set.
*/
- public Parser usingTypeRegistry(TypeRegistry registry) {
- if (this.registry != TypeRegistry.getEmptyTypeRegistry()) {
+ public Parser usingTypeRegistry(TypeRegistry oldRegistry) {
+ if (this.oldRegistry != TypeRegistry.getEmptyTypeRegistry()
+ || this.registry != com.google.protobuf.TypeRegistry.getEmptyTypeRegistry()) {
throw new IllegalArgumentException("Only one registry is allowed.");
}
- return new Parser(registry, ignoringUnknownFields, recursionLimit);
+ return new Parser(
+ com.google.protobuf.TypeRegistry.getEmptyTypeRegistry(),
+ oldRegistry,
+ ignoringUnknownFields,
+ recursionLimit);
+ }
+
+ /**
+ * Creates a new {@link Parser} using the given registry. The new Parser clones all other
+ * configurations from this Parser.
+ *
+ * @throws IllegalArgumentException if a registry is already set.
+ */
+ public Parser usingTypeRegistry(com.google.protobuf.TypeRegistry registry) {
+ if (this.oldRegistry != TypeRegistry.getEmptyTypeRegistry()
+ || this.registry != com.google.protobuf.TypeRegistry.getEmptyTypeRegistry()) {
+ throw new IllegalArgumentException("Only one registry is allowed.");
+ }
+ return new Parser(registry, oldRegistry, ignoringUnknownFields, recursionLimit);
}
/**
@@ -392,7 +461,7 @@ public class JsonFormat {
* encountered. The new Parser clones all other configurations from this Parser.
*/
public Parser ignoringUnknownFields() {
- return new Parser(this.registry, true, recursionLimit);
+ return new Parser(this.registry, oldRegistry, true, recursionLimit);
}
/**
@@ -404,7 +473,8 @@ public class JsonFormat {
public void merge(String json, Message.Builder builder) throws InvalidProtocolBufferException {
// TODO(xiaofeng): Investigate the allocation overhead and optimize for
// mobile.
- new ParserImpl(registry, ignoringUnknownFields, recursionLimit).merge(json, builder);
+ new ParserImpl(registry, oldRegistry, ignoringUnknownFields, recursionLimit)
+ .merge(json, builder);
}
/**
@@ -417,12 +487,13 @@ public class JsonFormat {
public void merge(Reader json, Message.Builder builder) throws IOException {
// TODO(xiaofeng): Investigate the allocation overhead and optimize for
// mobile.
- new ParserImpl(registry, ignoringUnknownFields, recursionLimit).merge(json, builder);
+ new ParserImpl(registry, oldRegistry, ignoringUnknownFields, recursionLimit)
+ .merge(json, builder);
}
// For testing only.
Parser usingRecursionLimit(int recursionLimit) {
- return new Parser(registry, ignoringUnknownFields, recursionLimit);
+ return new Parser(registry, oldRegistry, ignoringUnknownFields, recursionLimit);
}
}
@@ -478,7 +549,7 @@ public class JsonFormat {
@CanIgnoreReturnValue
public Builder add(Descriptor messageType) {
if (types == null) {
- throw new IllegalStateException("A TypeRegistry.Builer can only be used once.");
+ throw new IllegalStateException("A TypeRegistry.Builder can only be used once.");
}
addFile(messageType.getFile());
return this;
@@ -641,7 +712,8 @@ public class JsonFormat {
* A Printer converts protobuf messages to JSON format.
*/
private static final class PrinterImpl {
- private final TypeRegistry registry;
+ private final com.google.protobuf.TypeRegistry registry;
+ private final TypeRegistry oldRegistry;
private final boolean alwaysOutputDefaultValueFields;
private final Set<FieldDescriptor> includingDefaultValueFields;
private final boolean preservingProtoFieldNames;
@@ -658,7 +730,8 @@ public class JsonFormat {
}
PrinterImpl(
- TypeRegistry registry,
+ com.google.protobuf.TypeRegistry registry,
+ TypeRegistry oldRegistry,
boolean alwaysOutputDefaultValueFields,
Set<FieldDescriptor> includingDefaultValueFields,
boolean preservingProtoFieldNames,
@@ -667,6 +740,7 @@ public class JsonFormat {
boolean printingEnumsAsInts,
boolean sortingMapKeys) {
this.registry = registry;
+ this.oldRegistry = oldRegistry;
this.alwaysOutputDefaultValueFields = alwaysOutputDefaultValueFields;
this.includingDefaultValueFields = includingDefaultValueFields;
this.preservingProtoFieldNames = preservingProtoFieldNames;
@@ -807,7 +881,10 @@ public class JsonFormat {
String typeUrl = (String) message.getField(typeUrlField);
Descriptor type = registry.getDescriptorForTypeUrl(typeUrl);
if (type == null) {
- throw new InvalidProtocolBufferException("Cannot find type for url: " + typeUrl);
+ type = oldRegistry.getDescriptorForTypeUrl(typeUrl);
+ if (type == null) {
+ throw new InvalidProtocolBufferException("Cannot find type for url: " + typeUrl);
+ }
}
ByteString content = (ByteString) message.getField(valueField);
Message contentMessage =
@@ -1218,14 +1295,20 @@ public class JsonFormat {
}
private static class ParserImpl {
- private final TypeRegistry registry;
+ private final com.google.protobuf.TypeRegistry registry;
+ private final TypeRegistry oldRegistry;
private final JsonParser jsonParser;
private final boolean ignoringUnknownFields;
private final int recursionLimit;
private int currentDepth;
- ParserImpl(TypeRegistry registry, boolean ignoreUnknownFields, int recursionLimit) {
+ ParserImpl(
+ com.google.protobuf.TypeRegistry registry,
+ TypeRegistry oldRegistry,
+ boolean ignoreUnknownFields,
+ int recursionLimit) {
this.registry = registry;
+ this.oldRegistry = oldRegistry;
this.ignoringUnknownFields = ignoreUnknownFields;
this.jsonParser = new JsonParser();
this.recursionLimit = recursionLimit;
@@ -1448,7 +1531,10 @@ public class JsonFormat {
String typeUrl = typeUrlElement.getAsString();
Descriptor contentType = registry.getDescriptorForTypeUrl(typeUrl);
if (contentType == null) {
- throw new InvalidProtocolBufferException("Cannot resolve type: " + typeUrl);
+ contentType = oldRegistry.getDescriptorForTypeUrl(typeUrl);
+ if (contentType == null) {
+ throw new InvalidProtocolBufferException("Cannot resolve type: " + typeUrl);
+ }
}
builder.setField(typeUrlField, typeUrl);
Message.Builder contentBuilder =
--- a/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
+++ b/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
@@ -152,6 +152,16 @@ public class JsonFormatTest extends Test
assertEquals(message.toString(), parsedMessage.toString());
}
+ private void assertRoundTripEquals(Message message, com.google.protobuf.TypeRegistry registry)
+ throws Exception {
+ JsonFormat.Printer printer = JsonFormat.printer().usingTypeRegistry(registry);
+ JsonFormat.Parser parser = JsonFormat.parser().usingTypeRegistry(registry);
+ Message.Builder builder = message.newBuilderForType();
+ parser.merge(printer.print(message), builder);
+ Message parsedMessage = builder.build();
+ assertEquals(message.toString(), parsedMessage.toString());
+ }
+
private String toJsonString(Message message) throws IOException {
return JsonFormat.printer().print(message);
}
@@ -850,6 +860,45 @@ public class JsonFormatTest extends Test
}
+ public void testAnyFieldsWithCustomAddedTypeRegistry() throws Exception {
+ TestAllTypes content = TestAllTypes.newBuilder().setOptionalInt32(1234).build();
+ TestAny message = TestAny.newBuilder().setAnyValue(Any.pack(content)).build();
+
+ com.google.protobuf.TypeRegistry registry =
+ com.google.protobuf.TypeRegistry.newBuilder().add(content.getDescriptorForType()).build();
+ JsonFormat.Printer printer = JsonFormat.printer().usingTypeRegistry(registry);
+
+ assertEquals(
+ "{\n"
+ + " \"anyValue\": {\n"
+ + " \"@type\": \"type.googleapis.com/json_test.TestAllTypes\",\n"
+ + " \"optionalInt32\": 1234\n"
+ + " }\n"
+ + "}",
+ printer.print(message));
+ assertRoundTripEquals(message, registry);
+
+ TestAny messageWithDefaultAnyValue =
+ TestAny.newBuilder().setAnyValue(Any.getDefaultInstance()).build();
+ assertEquals("{\n" + " \"anyValue\": {}\n" + "}", printer.print(messageWithDefaultAnyValue));
+ assertRoundTripEquals(messageWithDefaultAnyValue, registry);
+
+ // Well-known types have a special formatting when embedded in Any.
+ //
+ // 1. Any in Any.
+ Any anyMessage = Any.pack(Any.pack(content));
+ assertEquals(
+ "{\n"
+ + " \"@type\": \"type.googleapis.com/google.protobuf.Any\",\n"
+ + " \"value\": {\n"
+ + " \"@type\": \"type.googleapis.com/json_test.TestAllTypes\",\n"
+ + " \"optionalInt32\": 1234\n"
+ + " }\n"
+ + "}",
+ printer.print(anyMessage));
+ assertRoundTripEquals(anyMessage, registry);
+ }
+
public void testAnyFields() throws Exception {
TestAllTypes content = TestAllTypes.newBuilder().setOptionalInt32(1234).build();
TestAny message = TestAny.newBuilder().setAnyValue(Any.pack(content)).build();
@@ -1136,7 +1185,7 @@ public class JsonFormatTest extends Test
Any.Builder builder = Any.newBuilder();
mergeFromJson(
"{\n"
- + " \"@type\": \"type.googleapis.com/json_test.TestAllTypes\",\n"
+ + " \"@type\": \"type.googleapis.com/json_test.UnexpectedTypes\",\n"
+ " \"optionalInt32\": 12345\n"
+ "}",
builder);
--- a/src/google/protobuf/compiler/java/java_context.cc
+++ b/src/google/protobuf/compiler/java/java_context.cc
@@ -35,7 +35,6 @@
#include <google/protobuf/compiler/java/java_name_resolver.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/stubs/strutil.h>
-
#include <google/protobuf/stubs/map_util.h>
namespace google {
--- a/src/google/protobuf/compiler/java/java_doc_comment.cc
+++ b/src/google/protobuf/compiler/java/java_doc_comment.cc
@@ -174,7 +174,8 @@ void WriteMessageDocComment(io::Printer*
void WriteFieldDocComment(io::Printer* printer, const FieldDescriptor* field) {
// We start the comment with the main body based on the comments from the
- // .proto file (if present). We then continue with the field declaration, e.g.:
+ // .proto file (if present). We then continue with the field declaration,
+ // e.g.:
// optional string foo = 5;
// And then we end with the javadoc tags if applicable.
// If the field is a group, the debug string might end with {.
@@ -185,17 +186,17 @@ void WriteFieldDocComment(io::Printer* p
printer->Print(" */\n");
}
-void WriteFieldAccessorDocComment(io::Printer* printer,
+void WriteFieldAccessorDocComment(io::Printer* printer,
const FieldDescriptor* field,
const FieldAccessorType type,
const bool builder) {
printer->Print("/**\n");
WriteDocCommentBody(printer, field);
- printer->Print(" * <code>$def$</code>\n", "def",
+ printer->Print(" * <code>$def$</code>\n", "def",
EscapeJavadoc(FirstLineOf(field->DebugString())));
switch (type) {
case HAZZER:
- printer->Print(" * @return Whether the $name$ field is set.\n", "name",
+ printer->Print(" * @return Whether the $name$ field is set.\n", "name",
field->camelcase_name());
break;
case GETTER:
@@ -211,16 +212,16 @@ void WriteFieldAccessorDocComment(io::Pr
break;
// Repeated
case LIST_COUNT:
- printer->Print(" * @return The number of $name$(s).\n", "name",
- field->camelcase_name());
+ printer->Print(" * @return The count of $name$.\n", "name",
+ field->camelcase_name());
break;
case LIST_GETTER:
- printer->Print(" * @return A list containing the $name$(s).\n", "name",
+ printer->Print(" * @return A list containing the $name$.\n", "name",
field->camelcase_name());
break;
case LIST_INDEXED_GETTER:
printer->Print(" * @param index The index of the element to return.\n");
- printer->Print(" * @return The $name$(s) at the given index.\n", "name",
+ printer->Print(" * @return The $name$ at the given index.\n", "name",
field->camelcase_name());
break;
case LIST_INDEXED_SETTER:
@@ -233,8 +234,8 @@ void WriteFieldAccessorDocComment(io::Pr
field->camelcase_name());
break;
case LIST_MULTI_ADDER:
- printer->Print(" * @param values The $name$(s) to add.\n", "name",
- field->camelcase_name());
+ printer->Print(" * @param values The $name$ to add.\n", "name",
+ field->camelcase_name());
break;
}
if (builder) {
@@ -243,25 +244,28 @@ void WriteFieldAccessorDocComment(io::Pr
printer->Print(" */\n");
}
-void WriteFieldEnumValueAccessorDocComment(io::Printer* printer,
+void WriteFieldEnumValueAccessorDocComment(io::Printer* printer,
const FieldDescriptor* field,
const FieldAccessorType type,
const bool builder) {
printer->Print("/**\n");
WriteDocCommentBody(printer, field);
- printer->Print(" * <code>$def$</code>\n", "def",
+ printer->Print(" * <code>$def$</code>\n", "def",
EscapeJavadoc(FirstLineOf(field->DebugString())));
switch (type) {
case HAZZER:
// Should never happen
break;
case GETTER:
- printer->Print(" * @return The enum value for $name$.\n", "name",
- field->camelcase_name());
+ printer->Print(
+ " * @return The enum numeric value on the wire for $name$.\n", "name",
+ field->camelcase_name());
break;
case SETTER:
- printer->Print(" * @param value The enum value for $name$ to set.\n",
- "name", field->camelcase_name());
+ printer->Print(
+ " * @param value The enum numeric value on the wire for $name$ to "
+ "set.\n",
+ "name", field->camelcase_name());
break;
case CLEARER:
// Print nothing
@@ -271,26 +275,36 @@ void WriteFieldEnumValueAccessorDocComme
// Should never happen
break;
case LIST_GETTER:
- printer->Print(" * @return A list containing the enum values for $name$(s).\n",
- "name", field->camelcase_name());
+ printer->Print(
+ " * @return A list containing the enum numeric values on the wire "
+ "for $name$.\n",
+ "name", field->camelcase_name());
break;
case LIST_INDEXED_GETTER:
printer->Print(" * @param index The index of the value to return.\n");
- printer->Print(" * @return The enum value of the $name$ at the given index.\n",
- "name", field->camelcase_name());
+ printer->Print(
+ " * @return The enum numeric value on the wire of $name$ at the "
+ "given index.\n",
+ "name", field->camelcase_name());
break;
case LIST_INDEXED_SETTER:
printer->Print(" * @param index The index of the value to return.\n");
- printer->Print(" * @param value The enum value of the $name$ to set.\n",
- "name", field->camelcase_name());
+ printer->Print(
+ " * @param value The enum numeric value on the wire for $name$ to "
+ "set.\n",
+ "name", field->camelcase_name());
break;
case LIST_ADDER:
- printer->Print(" * @param value The enum value of the $name$ to add.\n",
- "name", field->camelcase_name());
+ printer->Print(
+ " * @param value The enum numeric value on the wire for $name$ to "
+ "add.\n",
+ "name", field->camelcase_name());
break;
case LIST_MULTI_ADDER:
- printer->Print(" * @param values The enum values of the $name$(s) to add.\n",
- "name", field->camelcase_name());
+ printer->Print(
+ " * @param values The enum numeric values on the wire for $name$ to "
+ "add.\n",
+ "name", field->camelcase_name());
break;
}
if (builder) {
@@ -299,13 +313,13 @@ void WriteFieldEnumValueAccessorDocComme
printer->Print(" */\n");
}
-void WriteFieldStringBytesAccessorDocComment(io::Printer* printer,
+void WriteFieldStringBytesAccessorDocComment(io::Printer* printer,
const FieldDescriptor* field,
const FieldAccessorType type,
const bool builder) {
printer->Print("/**\n");
WriteDocCommentBody(printer, field);
- printer->Print(" * <code>$def$</code>\n", "def",
+ printer->Print(" * <code>$def$</code>\n", "def",
EscapeJavadoc(FirstLineOf(field->DebugString())));
switch (type) {
case HAZZER:
@@ -313,11 +327,11 @@ void WriteFieldStringBytesAccessorDocCom
break;
case GETTER:
printer->Print(" * @return The bytes for $name$.\n", "name",
- field->camelcase_name());
+ field->camelcase_name());
break;
case SETTER:
- printer->Print(" * @param value The bytes for $name$ to set.\n",
- "name", field->camelcase_name());
+ printer->Print(" * @param value The bytes for $name$ to set.\n", "name",
+ field->camelcase_name());
break;
case CLEARER:
// Print nothing
@@ -327,7 +341,7 @@ void WriteFieldStringBytesAccessorDocCom
// Should never happen
break;
case LIST_GETTER:
- printer->Print(" * @return A list containing the bytes for $name$(s).\n",
+ printer->Print(" * @return A list containing the bytes for $name$.\n",
"name", field->camelcase_name());
break;
case LIST_INDEXED_GETTER:
@@ -345,7 +359,7 @@ void WriteFieldStringBytesAccessorDocCom
"name", field->camelcase_name());
break;
case LIST_MULTI_ADDER:
- printer->Print(" * @param values The bytes of the $name$(s) to add.\n",
+ printer->Print(" * @param values The bytes of the $name$ to add.\n",
"name", field->camelcase_name());
break;
}
--- a/src/google/protobuf/compiler/java/java_doc_comment.h
+++ b/src/google/protobuf/compiler/java/java_doc_comment.h
@@ -53,35 +53,33 @@ namespace compiler {
namespace java {
enum FieldAccessorType {
-
- HAZZER,
- GETTER,
- SETTER,
- CLEARER,
- // Repeated
- LIST_COUNT,
- LIST_GETTER,
- LIST_INDEXED_GETTER,
- LIST_INDEXED_SETTER,
- LIST_ADDER,
- LIST_MULTI_ADDER
-
+ HAZZER,
+ GETTER,
+ SETTER,
+ CLEARER,
+ // Repeated
+ LIST_COUNT,
+ LIST_GETTER,
+ LIST_INDEXED_GETTER,
+ LIST_INDEXED_SETTER,
+ LIST_ADDER,
+ LIST_MULTI_ADDER
};
void WriteMessageDocComment(io::Printer* printer, const Descriptor* message);
void WriteFieldDocComment(io::Printer* printer, const FieldDescriptor* field);
void WriteFieldAccessorDocComment(io::Printer* printer,
- const FieldDescriptor* field,
- const FieldAccessorType type,
- const bool builder = false);
+ const FieldDescriptor* field,
+ const FieldAccessorType type,
+ const bool builder = false);
void WriteFieldEnumValueAccessorDocComment(io::Printer* printer,
- const FieldDescriptor* field,
- const FieldAccessorType type,
- const bool builder = false);
+ const FieldDescriptor* field,
+ const FieldAccessorType type,
+ const bool builder = false);
void WriteFieldStringBytesAccessorDocComment(io::Printer* printer,
- const FieldDescriptor* field,
- const FieldAccessorType type,
- const bool builder = false);
+ const FieldDescriptor* field,
+ const FieldAccessorType type,
+ const bool builder = false);
void WriteEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_);
void WriteEnumValueDocComment(io::Printer* printer,
const EnumValueDescriptor* value);
--- a/src/google/protobuf/compiler/java/java_enum.cc
+++ b/src/google/protobuf/compiler/java/java_enum.cc
@@ -44,7 +44,6 @@
#include <google/protobuf/io/printer.h>
#include <google/protobuf/stubs/strutil.h>
-
namespace google {
namespace protobuf {
namespace compiler {
@@ -175,8 +174,9 @@ void EnumGenerator::Generate(io::Printer
"}\n"
"\n"
"/**\n"
- " * @param value The number of the enum to look for.\n"
- " * @return The enum associated with the given number.\n"
+ " * @param value The numeric wire value of the corresponding enum "
+ "entry.\n"
+ " * @return The enum associated with the given numeric wire value.\n"
" * @deprecated Use {@link #forNumber(int)} instead.\n"
" */\n"
"@java.lang.Deprecated\n"
@@ -184,6 +184,11 @@ void EnumGenerator::Generate(io::Printer
" return forNumber(value);\n"
"}\n"
"\n"
+ "/**\n"
+ " * @param value The numeric wire value of the corresponding enum "
+ "entry.\n"
+ " * @return The enum associated with the given numeric wire value.\n"
+ " */\n"
"public static $classname$ forNumber(int value) {\n"
" switch (value) {\n",
"classname", descriptor_->name());
--- a/src/google/protobuf/compiler/java/java_enum_field.cc
+++ b/src/google/protobuf/compiler/java/java_enum_field.cc
@@ -46,7 +46,6 @@
#include <google/protobuf/wire_format.h>
#include <google/protobuf/stubs/strutil.h>
-
namespace google {
namespace protobuf {
namespace compiler {
@@ -220,7 +219,8 @@ void ImmutableEnumFieldGenerator::Genera
" return $name$_;\n"
"}\n");
printer->Annotate("{", "}", descriptor_);
- WriteFieldEnumValueAccessorDocComment(printer, descriptor_, SETTER);
+ WriteFieldEnumValueAccessorDocComment(printer, descriptor_, SETTER,
+ /* builder */ true);
printer->Print(variables_,
"$deprecation$public Builder "
"${$set$capitalized_name$Value$}$(int value) {\n"
--- a/src/google/protobuf/compiler/java/java_string_field_lite.cc
+++ b/src/google/protobuf/compiler/java/java_string_field_lite.cc
@@ -47,7 +47,6 @@
#include <google/protobuf/wire_format.h>
#include <google/protobuf/stubs/strutil.h>
-
namespace google {
namespace protobuf {
namespace compiler {
@@ -292,7 +291,8 @@ void ImmutableStringFieldLiteGenerator::
"}\n");
printer->Annotate("{", "}", descriptor_);
- WriteFieldStringBytesAccessorDocComment(printer, descriptor_, SETTER, true);
+ WriteFieldStringBytesAccessorDocComment(printer, descriptor_, SETTER,
+ /* builder */ true);
printer->Print(
variables_,
"$deprecation$public Builder ${$set$capitalized_name$Bytes$}$(\n"
@@ -470,7 +470,8 @@ void ImmutableStringOneofFieldLiteGenera
"}\n");
printer->Annotate("{", "}", descriptor_);
- WriteFieldStringBytesAccessorDocComment(printer, descriptor_, SETTER);
+ WriteFieldStringBytesAccessorDocComment(printer, descriptor_, SETTER,
+ /* builder */ true);
printer->Print(
variables_,
"$deprecation$public Builder ${$set$capitalized_name$Bytes$}$(\n"
--- a/src/google/protobuf/compiler/js/js_generator.cc
+++ b/src/google/protobuf/compiler/js/js_generator.cc
@@ -43,7 +43,6 @@
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/stringprintf.h>
#include <google/protobuf/stubs/strutil.h>
-
#include <google/protobuf/compiler/scc.h>
#include <google/protobuf/compiler/js/well_known_types_embed.h>
#include <google/protobuf/io/printer.h>
@@ -51,7 +50,6 @@
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/descriptor.h>
-
namespace google {
namespace protobuf {
namespace compiler {
@@ -1118,9 +1116,6 @@ std::string JSBinaryWriterMethodName(con
JSBinaryReadWriteMethodName(field, /* is_writer = */ true);
}
-std::string JSReturnClause(const FieldDescriptor* desc) {
- return "";
-}
std::string JSTypeTag(const FieldDescriptor* desc) {
switch (desc->type()) {
@@ -1156,10 +1151,6 @@ std::string JSTypeTag(const FieldDescrip
return "";
}
-std::string JSReturnDoc(const GeneratorOptions& options,
- const FieldDescriptor* desc) {
- return "";
-}
bool HasRepeatedFields(const GeneratorOptions& options,
const Descriptor* desc) {
@@ -1426,11 +1417,6 @@ bool HasFieldPresence(const GeneratorOpt
return false;
}
- if (UseBrokenPresenceSemantics(options, field)) {
- // Proto3 files with broken presence semantics have field presence.
- return true;
- }
-
return field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ||
field->containing_oneof() != NULL ||
field->file()->syntax() == FileDescriptor::SYNTAX_PROTO2;
@@ -2393,7 +2379,7 @@ void Generator::GenerateClassFieldToObje
if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 &&
// Repeated fields get initialized to their default in the constructor
// (why?), so we emit a plain getField() call for them.
- !field->is_repeated() && !UseBrokenPresenceSemantics(options, field)) {
+ !field->is_repeated()) {
// Proto3 puts all defaults (including implicit defaults) in toObject().
// But for proto2 we leave the existing semantics unchanged: unset fields
// without default are unset.
@@ -2677,30 +2663,31 @@ void Generator::GenerateClassField(const
(field->label() == FieldDescriptor::LABEL_REQUIRED ? ", 1" : ""));
printer->Annotate("gettername", field);
printer->Print(
- "/** @param {$optionaltype$} value$returndoc$ */\n"
+ "/**\n"
+ " * @param {$optionaltype$} value\n"
+ " * @return {!$class$} returns this\n"
+ "*/\n"
"$class$.prototype.$settername$ = function(value) {\n"
- " jspb.Message.set$oneoftag$$repeatedtag$WrapperField(",
+ " return jspb.Message.set$oneoftag$$repeatedtag$WrapperField(",
"optionaltype",
JSFieldTypeAnnotation(options, field,
/* is_setter_argument = */ true,
/* force_present = */ false,
/* singular_if_not_packed = */ false),
- "returndoc", JSReturnDoc(options, field), "class",
- GetMessagePath(options, field->containing_type()), "settername",
- "set" + JSGetterName(options, field), "oneoftag",
+ "class", GetMessagePath(options, field->containing_type()),
+ "settername", "set" + JSGetterName(options, field), "oneoftag",
(field->containing_oneof() ? "Oneof" : ""), "repeatedtag",
(field->is_repeated() ? "Repeated" : ""));
printer->Annotate("settername", field);
printer->Print(
- "this, $index$$oneofgroup$, value);$returnvalue$\n"
+ "this, $index$$oneofgroup$, value);\n"
"};\n"
"\n"
"\n",
"index", JSFieldIndex(field), "oneofgroup",
(field->containing_oneof() ? (", " + JSOneofArray(options, field))
- : ""),
- "returnvalue", JSReturnClause(field));
+ : ""));
if (field->is_repeated()) {
GenerateRepeatedMessageHelperMethods(options, printer, field);
@@ -2787,21 +2774,18 @@ void Generator::GenerateClassField(const
GenerateBytesWrapper(options, printer, field, BYTES_U8);
}
- if (untyped) {
- printer->Print(
- "/**\n"
- " * @param {*} value$returndoc$\n"
- " */\n",
- "returndoc", JSReturnDoc(options, field));
- } else {
- printer->Print(
- "/** @param {$optionaltype$} value$returndoc$ */\n", "optionaltype",
- JSFieldTypeAnnotation(options, field,
- /* is_setter_argument = */ true,
- /* force_present = */ false,
- /* singular_if_not_packed = */ false),
- "returndoc", JSReturnDoc(options, field));
- }
+ printer->Print(
+ "/**\n"
+ " * @param {$optionaltype$} value\n"
+ " * @return {!$class$} returns this\n"
+ " */\n",
+ "class", GetMessagePath(options, field->containing_type()),
+ "optionaltype",
+ untyped ? "*"
+ : JSFieldTypeAnnotation(options, field,
+ /* is_setter_argument = */ true,
+ /* force_present = */ false,
+ /* singular_if_not_packed = */ false));
if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 &&
!field->is_repeated() && !field->is_map() &&
@@ -2810,28 +2794,28 @@ void Generator::GenerateClassField(const
// setProto3*Field function.
printer->Print(
"$class$.prototype.$settername$ = function(value) {\n"
- " jspb.Message.setProto3$typetag$Field(this, $index$, "
- "value);$returnvalue$\n"
+ " return jspb.Message.setProto3$typetag$Field(this, $index$, "
+ "value);"
+ "\n"
"};\n"
"\n"
"\n",
"class", GetMessagePath(options, field->containing_type()),
"settername", "set" + JSGetterName(options, field), "typetag",
- JSTypeTag(field), "index", JSFieldIndex(field), "returnvalue",
- JSReturnClause(field));
+ JSTypeTag(field), "index", JSFieldIndex(field));
printer->Annotate("settername", field);
} else {
// Otherwise, use the regular setField function.
printer->Print(
"$class$.prototype.$settername$ = function(value) {\n"
- " jspb.Message.set$oneoftag$Field(this, $index$",
+ " return jspb.Message.set$oneoftag$Field(this, $index$",
"class", GetMessagePath(options, field->containing_type()),
"settername", "set" + JSGetterName(options, field), "oneoftag",
(field->containing_oneof() ? "Oneof" : ""), "index",
JSFieldIndex(field));
printer->Annotate("settername", field);
printer->Print(
- "$oneofgroup$, $type$value$rptvalueinit$$typeclose$);$returnvalue$\n"
+ "$oneofgroup$, $type$value$rptvalueinit$$typeclose$);\n"
"};\n"
"\n"
"\n",
@@ -2840,16 +2824,16 @@ void Generator::GenerateClassField(const
"typeclose", untyped ? ")" : "", "oneofgroup",
(field->containing_oneof() ? (", " + JSOneofArray(options, field))
: ""),
- "returnvalue", JSReturnClause(field), "rptvalueinit",
- (field->is_repeated() ? " || []" : ""));
+ "rptvalueinit", (field->is_repeated() ? " || []" : ""));
}
if (untyped) {
printer->Print(
"/**\n"
- " * Clears the value.$returndoc$\n"
+ " * Clears the value.\n"
+ " * @return {!$class$} returns this\n"
" */\n",
- "returndoc", JSReturnDoc(options, field));
+ "class", GetMessagePath(options, field->containing_type()));
}
if (field->is_repeated()) {
@@ -2863,19 +2847,18 @@ void Generator::GenerateClassField(const
// clang-format off
printer->Print(
"/**\n"
- " * Clears values from the map. The map will be non-null."
- "$returndoc$\n"
+ " * Clears values from the map. The map will be non-null.\n"
+ " * @return {!$class$} returns this\n"
" */\n"
"$class$.prototype.$clearername$ = function() {\n"
- " this.$gettername$().clear();$returnvalue$\n"
+ " this.$gettername$().clear();\n"
+ " return this;"
"};\n"
"\n"
"\n",
- "returndoc", JSReturnDoc(options, field),
"class", GetMessagePath(options, field->containing_type()),
"clearername", "clear" + JSGetterName(options, field),
- "gettername", "get" + JSGetterName(options, field),
- "returnvalue", JSReturnClause(field));
+ "gettername", "get" + JSGetterName(options, field));
// clang-format on
printer->Annotate("clearername", field);
} else if (field->is_repeated() ||
@@ -2885,22 +2868,21 @@ void Generator::GenerateClassField(const
// clang-format off
printer->Print(
"/**\n"
- " * $jsdoc$$returndoc$\n"
+ " * $jsdoc$\n"
+ " * @return {!$class$} returns this\n"
" */\n"
"$class$.prototype.$clearername$ = function() {\n"
- " this.$settername$($clearedvalue$);$returnvalue$\n"
+ " return this.$settername$($clearedvalue$);\n"
"};\n"
"\n"
"\n",
"jsdoc", field->is_repeated()
? "Clears the list making it empty but non-null."
: "Clears the message field making it undefined.",
- "returndoc", JSReturnDoc(options, field),
"class", GetMessagePath(options, field->containing_type()),
"clearername", "clear" + JSGetterName(options, field),
"settername", "set" + JSGetterName(options, field),
- "clearedvalue", (field->is_repeated() ? "[]" : "undefined"),
- "returnvalue", JSReturnClause(field));
+ "clearedvalue", (field->is_repeated() ? "[]" : "undefined"));
// clang-format on
printer->Annotate("clearername", field);
} else if (HasFieldPresence(options, field)) {
@@ -2909,12 +2891,12 @@ void Generator::GenerateClassField(const
// clang-format off
printer->Print(
"/**\n"
- " * Clears the field making it undefined.$returndoc$\n"
+ " * Clears the field making it undefined.\n"
+ " * @return {!$class$} returns this\n"
" */\n"
"$class$.prototype.$clearername$ = function() {\n"
- " jspb.Message.set$maybeoneof$Field(this, "
- "$index$$maybeoneofgroup$, ",
- "returndoc", JSReturnDoc(options, field),
+ " return jspb.Message.set$maybeoneof$Field(this, "
+ "$index$$maybeoneofgroup$, ",
"class", GetMessagePath(options, field->containing_type()),
"clearername", "clear" + JSGetterName(options, field),
"maybeoneof", (field->containing_oneof() ? "Oneof" : ""),
@@ -2925,12 +2907,11 @@ void Generator::GenerateClassField(const
// clang-format on
printer->Annotate("clearername", field);
printer->Print(
- "$clearedvalue$);$returnvalue$\n"
+ "$clearedvalue$);\n"
"};\n"
"\n"
"\n",
- "clearedvalue", (field->is_repeated() ? "[]" : "undefined"),
- "returnvalue", JSReturnClause(field));
+ "clearedvalue", (field->is_repeated() ? "[]" : "undefined"));
}
if (HasFieldPresence(options, field)) {
@@ -2957,10 +2938,12 @@ void Generator::GenerateRepeatedPrimitiv
printer->Print(
"/**\n"
" * @param {$optionaltype$} value\n"
- " * @param {number=} opt_index$returndoc$\n"
+ " * @param {number=} opt_index\n"
+ " * @return {!$class$} returns this\n"
" */\n"
"$class$.prototype.$addername$ = function(value, opt_index) {\n"
- " jspb.Message.addToRepeatedField(this, $index$",
+ " return jspb.Message.addToRepeatedField(this, "
+ "$index$",
"class", GetMessagePath(options, field->containing_type()), "addername",
"add" + JSGetterName(options, field, BYTES_DEFAULT,
/* drop_list = */ true),
@@ -2972,20 +2955,18 @@ void Generator::GenerateRepeatedPrimitiv
/* singular_if_not_packed = */ false,
BYTES_DEFAULT,
/* force_singular = */ true),
- "index", JSFieldIndex(field),
- "returndoc", JSReturnDoc(options, field));
+ "index", JSFieldIndex(field));
printer->Annotate("addername", field);
printer->Print(
"$oneofgroup$, $type$value$rptvalueinit$$typeclose$, "
- "opt_index);$returnvalue$\n"
+ "opt_index);\n"
"};\n"
"\n"
"\n",
"type", untyped ? "/** @type{string|number|boolean|!Uint8Array} */(" : "",
"typeclose", untyped ? ")" : "", "oneofgroup",
(field->containing_oneof() ? (", " + JSOneofArray(options, field)) : ""),
- "rptvalueinit", "",
- "returnvalue", JSReturnClause(field));
+ "rptvalueinit", "");
// clang-format on
}
@@ -3374,12 +3355,21 @@ void Generator::GenerateEnum(const Gener
enumdesc->name());
printer->Annotate("name", enumdesc);
+ std::set<string> used_name;
+ std::vector<int> valid_index;
for (int i = 0; i < enumdesc->value_count(); i++) {
+ if (enumdesc->options().allow_alias() &&
+ !used_name.insert(ToEnumCase(enumdesc->value(i)->name())).second) {
+ continue;
+ }
+ valid_index.push_back(i);
+ }
+ for (auto i : valid_index) {
const EnumValueDescriptor* value = enumdesc->value(i);
printer->Print(" $name$: $value$$comma$\n", "name",
ToEnumCase(value->name()), "value",
StrCat(value->number()), "comma",
- (i == enumdesc->value_count() - 1) ? "" : ",");
+ (i == valid_index.back()) ? "" : ",");
printer->Annotate("name", value);
}