aboutsummaryrefslogtreecommitdiffhomepage
path: root/java
diff options
context:
space:
mode:
authorGravatar Feng Xiao <xfxyjwf@gmail.com>2014-11-10 17:34:54 -0800
committerGravatar Feng Xiao <xfxyjwf@gmail.com>2014-11-10 17:34:54 -0800
commit6ef984af4b0c63c1c33127a12dcfc8e6359f0c9e (patch)
treed17c61ff9f3ae28224fbddac6d26bfc59e2cf755 /java
parentbaca1a8a1aa180c42de6278d3b8286c4496c6a10 (diff)
Down-integrate from internal code base.
Diffstat (limited to 'java')
-rw-r--r--java/pom.xml29
-rw-r--r--java/src/main/java/com/google/protobuf/AbstractMessage.java60
-rw-r--r--java/src/main/java/com/google/protobuf/CodedInputStream.java30
-rw-r--r--java/src/main/java/com/google/protobuf/Descriptors.java115
-rw-r--r--java/src/main/java/com/google/protobuf/DynamicMessage.java27
-rw-r--r--java/src/main/java/com/google/protobuf/Extension.java23
-rw-r--r--java/src/main/java/com/google/protobuf/ExtensionLite.java63
-rw-r--r--java/src/main/java/com/google/protobuf/FieldSet.java4
-rw-r--r--java/src/main/java/com/google/protobuf/GeneratedMessage.java364
-rw-r--r--java/src/main/java/com/google/protobuf/GeneratedMessageLite.java319
-rw-r--r--java/src/main/java/com/google/protobuf/Internal.java166
-rw-r--r--java/src/main/java/com/google/protobuf/LazyStringArrayList.java4
-rw-r--r--java/src/main/java/com/google/protobuf/LiteralByteString.java9
-rw-r--r--java/src/main/java/com/google/protobuf/MapEntry.java433
-rw-r--r--java/src/main/java/com/google/protobuf/MapEntryLite.java331
-rw-r--r--java/src/main/java/com/google/protobuf/MapField.java259
-rw-r--r--java/src/main/java/com/google/protobuf/MapFieldLite.java182
-rw-r--r--java/src/main/java/com/google/protobuf/Message.java19
-rw-r--r--java/src/main/java/com/google/protobuf/MessageReflection.java33
-rw-r--r--java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java24
-rw-r--r--java/src/main/java/com/google/protobuf/TextFormat.java14
-rw-r--r--java/src/main/java/com/google/protobuf/UnknownFieldSet.java15
-rw-r--r--java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java297
-rw-r--r--java/src/test/java/com/google/protobuf/CodedInputStreamTest.java10
-rw-r--r--java/src/test/java/com/google/protobuf/DescriptorsTest.java6
-rw-r--r--java/src/test/java/com/google/protobuf/FieldPresenceTest.java363
-rw-r--r--java/src/test/java/com/google/protobuf/GeneratedMessageTest.java141
-rw-r--r--java/src/test/java/com/google/protobuf/LazyMessageLiteTest.java31
-rw-r--r--java/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java23
-rw-r--r--java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java277
-rw-r--r--java/src/test/java/com/google/protobuf/MapForProto2Test.java488
-rw-r--r--java/src/test/java/com/google/protobuf/MapTest.java569
-rw-r--r--java/src/test/java/com/google/protobuf/TestBadIdentifiers.java2
-rw-r--r--java/src/test/java/com/google/protobuf/TextFormatTest.java15
-rw-r--r--java/src/test/java/com/google/protobuf/UnknownEnumValueTest.java255
-rw-r--r--java/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java317
-rw-r--r--java/src/test/java/com/google/protobuf/field_presence_test.proto93
-rw-r--r--java/src/test/java/com/google/protobuf/lazy_fields_lite.proto1
-rw-r--r--java/src/test/java/com/google/protobuf/lite_equals_and_hash.proto17
-rw-r--r--java/src/test/java/com/google/protobuf/map_for_proto2_lite_test.proto63
-rw-r--r--java/src/test/java/com/google/protobuf/map_for_proto2_test.proto62
-rw-r--r--java/src/test/java/com/google/protobuf/map_test.proto63
-rw-r--r--java/src/test/java/com/google/protobuf/multiple_files_test.proto1
-rw-r--r--java/src/test/java/com/google/protobuf/nested_builders_test.proto1
-rw-r--r--java/src/test/java/com/google/protobuf/nested_extension.proto1
-rw-r--r--java/src/test/java/com/google/protobuf/nested_extension_lite.proto1
-rw-r--r--java/src/test/java/com/google/protobuf/non_nested_extension.proto1
-rw-r--r--java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto1
-rw-r--r--java/src/test/java/com/google/protobuf/outer_class_name_test.proto2
-rw-r--r--java/src/test/java/com/google/protobuf/outer_class_name_test2.proto2
-rw-r--r--java/src/test/java/com/google/protobuf/outer_class_name_test3.proto2
-rw-r--r--java/src/test/java/com/google/protobuf/test_bad_identifiers.proto24
-rw-r--r--java/src/test/java/com/google/protobuf/test_check_utf8.proto1
-rw-r--r--java/src/test/java/com/google/protobuf/test_check_utf8_size.proto1
-rw-r--r--java/src/test/java/com/google/protobuf/test_custom_options.proto1
-rw-r--r--java/src/test/java/com/google/protobuf/test_extra_interfaces.proto1
56 files changed, 5365 insertions, 291 deletions
diff --git a/java/pom.xml b/java/pom.xml
index 1e3baa3c..a2bd1d1e 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -130,6 +130,10 @@
<arg value="../src/google/protobuf/unittest_lite_imports_nonlite.proto" />
<arg value="../src/google/protobuf/unittest_enormous_descriptor.proto" />
<arg value="../src/google/protobuf/unittest_no_generic_services.proto" />
+ <arg value="src/test/java/com/google/protobuf/field_presence_test.proto" />
+ <arg value="src/test/java/com/google/protobuf/map_for_proto2_lite_test.proto" />
+ <arg value="src/test/java/com/google/protobuf/map_for_proto2_test.proto" />
+ <arg value="src/test/java/com/google/protobuf/map_test.proto" />
</exec>
</tasks>
<testSourceRoot>target/generated-test-sources</testSourceRoot>
@@ -164,34 +168,37 @@
<configuration>
<includes>
<include>**/AbstractMessageLite.java</include>
+ <include>**/AbstractParser.java</include>
+ <include>**/BoundedByteString.java</include>
<include>**/ByteString.java</include>
<include>**/CodedInputStream.java</include>
<include>**/CodedOutputStream.java</include>
+ <include>**/ExtensionLite.java</include>
<include>**/ExtensionRegistryLite.java</include>
<include>**/FieldSet.java</include>
<include>**/GeneratedMessageLite.java</include>
<include>**/Internal.java</include>
<include>**/InvalidProtocolBufferException.java</include>
+ <include>**/LazyFieldLite.java</include>
<include>**/LazyStringArrayList.java</include>
<include>**/LazyStringList.java</include>
+ <include>**/LiteralByteString.java</include>
+ <include>**/MapEntryLite.java</include>
+ <include>**/MapFieldLite.java</include>
<include>**/MessageLite.java</include>
<include>**/MessageLiteOrBuilder.java</include>
+ <include>**/Parser.java</include>
+ <include>**/ProtocolStringList.java</include>
+ <include>**/RopeByteString.java</include>
<include>**/SmallSortedMap.java</include>
<include>**/UninitializedMessageException.java</include>
+ <include>**/UnknownFieldSetLite.java</include>
<include>**/UnmodifiableLazyStringList.java</include>
- <include>**/WireFormat.java</include>
- <include>**/Parser.java</include>
- <include>**/AbstractParser.java</include>
- <include>**/BoundedByteString.java</include>
- <include>**/LiteralByteString.java</include>
- <include>**/RopeByteString.java</include>
<include>**/Utf8.java</include>
- <include>**/LazyField.java</include>
- <include>**/LazyFieldLite.java</include>
- <include>**/ProtocolStringList.java</include>
+ <include>**/WireFormat.java</include>
</includes>
<testIncludes>
- <testInclude>**/LiteTest.java</testInclude>
+ <testInclude>**/**LiteTest.java</testInclude>
<testInclude>**/*Lite.java</testInclude>
</testIncludes>
</configuration>
@@ -200,7 +207,7 @@
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
- <include>**/LiteTest.java</include>
+ <include>**/**LiteTest.java</include>
</includes>
</configuration>
</plugin>
diff --git a/java/src/main/java/com/google/protobuf/AbstractMessage.java b/java/src/main/java/com/google/protobuf/AbstractMessage.java
index ae9d5e39..6de4cae3 100644
--- a/java/src/main/java/com/google/protobuf/AbstractMessage.java
+++ b/java/src/main/java/com/google/protobuf/AbstractMessage.java
@@ -37,6 +37,9 @@ import com.google.protobuf.Internal.EnumLite;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -144,6 +147,40 @@ public abstract class AbstractMessage extends AbstractMessageLite
}
/**
+ * Converts a list of MapEntry messages into a Map used for equals() and
+ * hashCode().
+ */
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ private static Map convertMapEntryListToMap(List list) {
+ if (list.isEmpty()) {
+ return Collections.emptyMap();
+ }
+ Map result = new HashMap();
+ Iterator iterator = list.iterator();
+ Message entry = (Message) iterator.next();
+ Descriptors.Descriptor descriptor = entry.getDescriptorForType();
+ Descriptors.FieldDescriptor key = descriptor.findFieldByName("key");
+ Descriptors.FieldDescriptor value = descriptor.findFieldByName("value");
+ result.put(entry.getField(key), entry.getField(value));
+ while (iterator.hasNext()) {
+ entry = (Message) iterator.next();
+ result.put(entry.getField(key), entry.getField(value));
+ }
+ return result;
+ }
+
+ /**
+ * Compares two map fields. The parameters must be a list of MapEntry
+ * messages.
+ */
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ private static boolean compareMapField(Object a, Object b) {
+ Map ma = convertMapEntryListToMap((List) a);
+ Map mb = convertMapEntryListToMap((List) b);
+ return MapFieldLite.equals(ma, mb);
+ }
+
+ /**
* Compares two set of fields.
* This method is used to implement {@link AbstractMessage#equals(Object)}
* and {@link AbstractMutableMessage#equals(Object)}. It takes special care
@@ -181,6 +218,10 @@ public abstract class AbstractMessage extends AbstractMessageLite
return false;
}
}
+ } else if (descriptor.isMapField()) {
+ if (!compareMapField(value1, value2)) {
+ return false;
+ }
} else {
// Compare non-bytes fields.
if (!value1.equals(value2)) {
@@ -190,6 +231,15 @@ public abstract class AbstractMessage extends AbstractMessageLite
}
return true;
}
+
+ /**
+ * Calculates the hash code of a map field. {@code value} must be a list of
+ * MapEntry messages.
+ */
+ @SuppressWarnings("unchecked")
+ private static int hashMapField(Object value) {
+ return MapFieldLite.calculateHashCodeForMap(convertMapEntryListToMap((List) value));
+ }
/** Get a hash code for given fields and values, using the given seed. */
@SuppressWarnings("unchecked")
@@ -198,7 +248,9 @@ public abstract class AbstractMessage extends AbstractMessageLite
FieldDescriptor field = entry.getKey();
Object value = entry.getValue();
hash = (37 * hash) + field.getNumber();
- if (field.getType() != FieldDescriptor.Type.ENUM){
+ if (field.isMapField()) {
+ hash = (53 * hash) + hashMapField(value);
+ } else if (field.getType() != FieldDescriptor.Type.ENUM){
hash = (53 * hash) + value.hashCode();
} else if (field.isRepeated()) {
List<? extends EnumLite> list = (List<? extends EnumLite>) value;
@@ -359,6 +411,12 @@ public abstract class AbstractMessage extends AbstractMessageLite
"getFieldBuilder() called on an unsupported message type.");
}
+ public Message.Builder getRepeatedFieldBuilder(final FieldDescriptor field,
+ int index) {
+ throw new UnsupportedOperationException(
+ "getRepeatedFieldBuilder() called on an unsupported message type.");
+ }
+
public String toString() {
return TextFormat.printToString(this);
}
diff --git a/java/src/main/java/com/google/protobuf/CodedInputStream.java b/java/src/main/java/com/google/protobuf/CodedInputStream.java
index a00ae86f..0ca00bab 100644
--- a/java/src/main/java/com/google/protobuf/CodedInputStream.java
+++ b/java/src/main/java/com/google/protobuf/CodedInputStream.java
@@ -612,16 +612,16 @@ public final class CodedInputStream {
return x;
} else if (bufferSize - pos < 9) {
break fastpath;
- } else if ((x ^= (buffer[pos++] << 7)) < 0L) {
- x ^= (~0L << 7);
- } else if ((x ^= (buffer[pos++] << 14)) >= 0L) {
- x ^= (~0L << 7) ^ (~0L << 14);
- } else if ((x ^= (buffer[pos++] << 21)) < 0L) {
- x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21);
+ } else if ((x ^= (buffer[pos++] << 7)) < 0) {
+ x ^= (~0 << 7);
+ } else if ((x ^= (buffer[pos++] << 14)) >= 0) {
+ x ^= (~0 << 7) ^ (~0 << 14);
+ } else if ((x ^= (buffer[pos++] << 21)) < 0) {
+ x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21);
} else {
int y = buffer[pos++];
x ^= y << 28;
- x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28);
+ x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21) ^ (~0 << 28);
if (y < 0 &&
buffer[pos++] < 0 &&
buffer[pos++] < 0 &&
@@ -739,13 +739,13 @@ public final class CodedInputStream {
return y;
} else if (bufferSize - pos < 9) {
break fastpath;
- } else if ((x = y ^ (buffer[pos++] << 7)) < 0L) {
- x ^= (~0L << 7);
- } else if ((x ^= (buffer[pos++] << 14)) >= 0L) {
- x ^= (~0L << 7) ^ (~0L << 14);
- } else if ((x ^= (buffer[pos++] << 21)) < 0L) {
- x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21);
- } else if ((x ^= ((long) buffer[pos++] << 28)) >= 0L) {
+ } else if ((y ^= (buffer[pos++] << 7)) < 0) {
+ x = y ^ (~0 << 7);
+ } else if ((y ^= (buffer[pos++] << 14)) >= 0) {
+ x = y ^ ((~0 << 7) ^ (~0 << 14));
+ } else if ((y ^= (buffer[pos++] << 21)) < 0) {
+ x = y ^ ((~0 << 7) ^ (~0 << 14) ^ (~0 << 21));
+ } else if ((x = ((long) y) ^ ((long) buffer[pos++] << 28)) >= 0L) {
x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28);
} else if ((x ^= ((long) buffer[pos++] << 35)) < 0L) {
x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35);
@@ -882,7 +882,7 @@ public final class CodedInputStream {
/** See setSizeLimit() */
private int sizeLimit = DEFAULT_SIZE_LIMIT;
- private static final int DEFAULT_RECURSION_LIMIT = 64;
+ private static final int DEFAULT_RECURSION_LIMIT = 100;
private static final int DEFAULT_SIZE_LIMIT = 64 << 20; // 64MB
private static final int BUFFER_SIZE = 4096;
diff --git a/java/src/main/java/com/google/protobuf/Descriptors.java b/java/src/main/java/com/google/protobuf/Descriptors.java
index caae0f77..d65e8b49 100644
--- a/java/src/main/java/com/google/protobuf/Descriptors.java
+++ b/java/src/main/java/com/google/protobuf/Descriptors.java
@@ -32,6 +32,7 @@ package com.google.protobuf;
import com.google.protobuf.DescriptorProtos.*;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -40,6 +41,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.WeakHashMap;
import java.util.logging.Logger;
import java.io.UnsupportedEncodingException;
@@ -123,6 +125,26 @@ public final class Descriptors {
return Collections.unmodifiableList(Arrays.asList(publicDependencies));
}
+ /** The syntax of the .proto file. */
+ public enum Syntax {
+ UNKNOWN("unknown"),
+ PROTO2("proto2"),
+ PROTO3("proto3");
+
+ Syntax(String name) {
+ this.name = name;
+ }
+ private final String name;
+ }
+
+ /** Get the syntax of the .proto file. */
+ public Syntax getSyntax() {
+ if (Syntax.PROTO3.name.equals(proto.getSyntax())) {
+ return Syntax.PROTO3;
+ }
+ return Syntax.PROTO2;
+ }
+
/**
* Find a message type in the file by name. Does not find nested types.
*
@@ -539,6 +561,10 @@ public final class Descriptors {
extensions[i].setProto(proto.getExtension(i));
}
}
+
+ boolean supportsUnknownEnumValue() {
+ return getSyntax() == Syntax.PROTO3;
+ }
}
// =================================================================
@@ -871,6 +897,11 @@ public final class Descriptors {
return (type == Type.STRING) && (getFile().getOptions().getJavaStringCheckUtf8());
}
+ boolean isMapField() {
+ return getType() == Type.MESSAGE && isRepeated()
+ && getMessageType().getOptions().getMapEntry();
+ }
+
// I'm pretty sure values() constructs a new array every time, since there
// is nothing stopping the caller from mutating the array. Therefore we
// make a static copy here.
@@ -1001,6 +1032,11 @@ public final class Descriptors {
return getNumber() - other.getNumber();
}
+ @Override
+ public String toString() {
+ return getFullName();
+ }
+
private final int index;
private FieldDescriptorProto proto;
@@ -1420,6 +1456,64 @@ public final class Descriptors {
return file.pool.enumValuesByNumber.get(
new DescriptorPool.DescriptorIntPair(this, number));
}
+
+ /**
+ * Get the enum value for a number. If no enum value has this number,
+ * construct an EnumValueDescriptor for it.
+ */
+ public EnumValueDescriptor findValueByNumberCreatingIfUnknown(final int number) {
+ EnumValueDescriptor result = findValueByNumber(number);
+ if (result != null) {
+ return result;
+ }
+ // The number represents an unknown enum value.
+ synchronized (this) {
+ // Descriptors are compared by object identity so for the same number
+ // we need to return the same EnumValueDescriptor object. This means
+ // we have to store created EnumValueDescriptors. However, as there
+ // are potentially 2G unknown enum values, storing all of these
+ // objects persistently will consume lots of memory for long-running
+ // services and it's also unnecessary as not many EnumValueDescriptors
+ // will be used at the same time.
+ //
+ // To solve the problem we take advantage of Java's weak references and
+ // rely on gc to release unused descriptors.
+ //
+ // Here is how it works:
+ // * We store unknown EnumValueDescriptors in a WeakHashMap with the
+ // value being a weak reference to the descriptor.
+ // * The descriptor holds a strong reference to the key so as long
+ // as the EnumValueDescriptor is in use, the key will be there
+ // and the corresponding map entry will be there. Following-up
+ // queries with the same number will return the same descriptor.
+ // * If the user no longer uses an unknown EnumValueDescriptor,
+ // it will be gc-ed since we only hold a weak reference to it in
+ // the map. The key in the corresponding map entry will also be
+ // gc-ed as the only strong reference to it is in the descriptor
+ // which is just gc-ed. With the key being gone WeakHashMap will
+ // then remove the whole entry. This way unknown descriptors will
+ // be freed automatically and we don't need to do anything to
+ // clean-up unused map entries.
+
+ // Note: We must use "new Integer(number)" here because we don't want
+ // these Integer objects to be cached.
+ Integer key = new Integer(number);
+ WeakReference<EnumValueDescriptor> reference = unknownValues.get(key);
+ if (reference != null) {
+ result = reference.get();
+ }
+ if (result == null) {
+ result = new EnumValueDescriptor(file, this, key);
+ unknownValues.put(key, new WeakReference<EnumValueDescriptor>(result));
+ }
+ }
+ return result;
+ }
+
+ // Used in tests only.
+ int getUnknownEnumValueDescriptorCount() {
+ return unknownValues.size();
+ }
private final int index;
private EnumDescriptorProto proto;
@@ -1427,6 +1521,8 @@ public final class Descriptors {
private final FileDescriptor file;
private final Descriptor containingType;
private EnumValueDescriptor[] values;
+ private final WeakHashMap<Integer, WeakReference<EnumValueDescriptor>> unknownValues =
+ new WeakHashMap<Integer, WeakReference<EnumValueDescriptor>>();
private EnumDescriptor(final EnumDescriptorProto proto,
final FileDescriptor file,
@@ -1531,6 +1627,25 @@ public final class Descriptors {
file.pool.addSymbol(this);
file.pool.addEnumValueByNumber(this);
}
+
+ private Integer number;
+ // Create an unknown enum value.
+ private EnumValueDescriptor(
+ final FileDescriptor file,
+ final EnumDescriptor parent,
+ final Integer number) {
+ String name = "UNKNOWN_ENUM_VALUE_" + parent.getName() + "_" + number;
+ EnumValueDescriptorProto proto = EnumValueDescriptorProto
+ .newBuilder().setName(name).setNumber(number).build();
+ this.index = -1;
+ this.proto = proto;
+ this.file = file;
+ this.type = parent;
+ this.fullName = parent.getFullName() + '.' + proto.getName();
+ this.number = number;
+
+ // Don't add this descriptor into pool.
+ }
/** See {@link FileDescriptor#setProto}. */
private void setProto(final EnumValueDescriptorProto proto) {
diff --git a/java/src/main/java/com/google/protobuf/DynamicMessage.java b/java/src/main/java/com/google/protobuf/DynamicMessage.java
index c9ce667b..06b30fff 100644
--- a/java/src/main/java/com/google/protobuf/DynamicMessage.java
+++ b/java/src/main/java/com/google/protobuf/DynamicMessage.java
@@ -549,12 +549,22 @@ public final class DynamicMessage extends AbstractMessage {
}
public Builder setUnknownFields(UnknownFieldSet unknownFields) {
+ if (getDescriptorForType().getFile().getSyntax()
+ == Descriptors.FileDescriptor.Syntax.PROTO3) {
+ // Proto3 discards unknown fields.
+ return this;
+ }
this.unknownFields = unknownFields;
return this;
}
@Override
public Builder mergeUnknownFields(UnknownFieldSet unknownFields) {
+ if (getDescriptorForType().getFile().getSyntax()
+ == Descriptors.FileDescriptor.Syntax.PROTO3) {
+ // Proto3 discards unknown fields.
+ return this;
+ }
this.unknownFields =
UnknownFieldSet.newBuilder(this.unknownFields)
.mergeFrom(unknownFields)
@@ -588,10 +598,12 @@ public final class DynamicMessage extends AbstractMessage {
throw new IllegalArgumentException(
"DynamicMessage should use EnumValueDescriptor to set Enum Value.");
}
- if (field.getEnumType() != ((EnumValueDescriptor) value).getType()) {
- throw new IllegalArgumentException(
- "EnumValueDescriptor doesn't much Enum Field.");
- }
+ // TODO(xiaofeng): Re-enable this check after Orgstore is fixed to not
+ // set incorrect EnumValueDescriptors.
+ // if (field.getEnumType() != ((EnumValueDescriptor) value).getType()) {
+ // throw new IllegalArgumentException(
+ // "EnumValueDescriptor doesn't match Enum Field.");
+ // }
}
/** Verifies the value for an enum field. */
@@ -618,5 +630,12 @@ public final class DynamicMessage extends AbstractMessage {
throw new UnsupportedOperationException(
"getFieldBuilder() called on a dynamic message type.");
}
+
+ @Override
+ public com.google.protobuf.Message.Builder getRepeatedFieldBuilder(FieldDescriptor field,
+ int index) {
+ throw new UnsupportedOperationException(
+ "getRepeatedFieldBuilder() called on a dynamic message type.");
+ }
}
}
diff --git a/java/src/main/java/com/google/protobuf/Extension.java b/java/src/main/java/com/google/protobuf/Extension.java
index 0baa22b3..68d29f33 100644
--- a/java/src/main/java/com/google/protobuf/Extension.java
+++ b/java/src/main/java/com/google/protobuf/Extension.java
@@ -35,27 +35,16 @@ package com.google.protobuf;
*
* @author liujisi@google.com (Jisi Liu)
*/
-public abstract class Extension<ContainingType extends MessageLite, Type> {
- /** Returns the field number of the extension. */
- public abstract int getNumber();
-
- /** Returns the type of the field. */
- public abstract WireFormat.FieldType getLiteType();
-
- /** Returns whether it is a repeated field. */
- public abstract boolean isRepeated();
+public abstract class Extension<ContainingType extends MessageLite, Type>
+ extends ExtensionLite<ContainingType, Type> {
/** Returns the descriptor of the extension. */
public abstract Descriptors.FieldDescriptor getDescriptor();
- /** Returns the default value of the extension field. */
- public abstract Type getDefaultValue();
-
- /**
- * Returns the default instance of the extension field, if it's a message
- * extension.
- */
- public abstract MessageLite getMessageDefaultInstance();
+ /** Returns whether or not this extension is a Lite Extension. */
+ final boolean isLite() {
+ return false;
+ }
// All the methods below are extension implementation details.
diff --git a/java/src/main/java/com/google/protobuf/ExtensionLite.java b/java/src/main/java/com/google/protobuf/ExtensionLite.java
new file mode 100644
index 00000000..f8f5bd2c
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/ExtensionLite.java
@@ -0,0 +1,63 @@
+// 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;
+
+/**
+ * Lite interface that generated extensions implement.
+ * <p>
+ * Methods are for use by generated code only. You can hold a reference to
+ * extensions using this type name.
+ */
+public abstract class ExtensionLite<ContainingType extends MessageLite, Type> {
+
+ /** Returns the field number of the extension. */
+ public abstract int getNumber();
+
+ /** Returns the type of the field. */
+ public abstract WireFormat.FieldType getLiteType();
+
+ /** Returns whether it is a repeated field. */
+ public abstract boolean isRepeated();
+
+ /** Returns the default value of the extension field. */
+ public abstract Type getDefaultValue();
+
+ /**
+ * Returns the default instance of the extension field, if it's a message
+ * extension.
+ */
+ public abstract MessageLite getMessageDefaultInstance();
+
+ /** Returns whether or not this extension is a Lite Extension. */
+ boolean isLite() {
+ return true;
+ }
+}
diff --git a/java/src/main/java/com/google/protobuf/FieldSet.java b/java/src/main/java/com/google/protobuf/FieldSet.java
index 392f4efc..ff9b5bc0 100644
--- a/java/src/main/java/com/google/protobuf/FieldSet.java
+++ b/java/src/main/java/com/google/protobuf/FieldSet.java
@@ -672,7 +672,7 @@ final class FieldSet<FieldDescriptorType extends
* {@link Message#getField(Descriptors.FieldDescriptor)} for
* this field.
*/
- private static void writeElementNoTag(
+ static void writeElementNoTag(
final CodedOutputStream output,
final WireFormat.FieldType type,
final Object value) throws IOException {
@@ -830,7 +830,7 @@ final class FieldSet<FieldDescriptorType extends
* {@link Message#getField(Descriptors.FieldDescriptor)} for
* this field.
*/
- private static int computeElementSizeNoTag(
+ static int computeElementSizeNoTag(
final WireFormat.FieldType type, final Object value) {
switch (type) {
// Note: Minor violation of 80-char limit rule here because this would
diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessage.java b/java/src/main/java/com/google/protobuf/GeneratedMessage.java
index a6101cb0..156d1633 100644
--- a/java/src/main/java/com/google/protobuf/GeneratedMessage.java
+++ b/java/src/main/java/com/google/protobuf/GeneratedMessage.java
@@ -31,16 +31,20 @@
package com.google.protobuf;
import com.google.protobuf.Descriptors.Descriptor;
+import com.google.protobuf.Descriptors.EnumDescriptor;
import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.FileDescriptor;
import com.google.protobuf.Descriptors.OneofDescriptor;
+import com.google.protobuf.GeneratedMessageLite.ExtendableMessage;
+import com.google.protobuf.GeneratedMessageLite.GeneratedExtension;
import java.io.IOException;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
@@ -67,10 +71,15 @@ public abstract class GeneratedMessage extends AbstractMessage
*/
protected static boolean alwaysUseFieldBuilders = false;
+ /** For use by generated code only. */
+ protected UnknownFieldSet unknownFields;
+
protected GeneratedMessage() {
+ unknownFields = UnknownFieldSet.getDefaultInstance();
}
protected GeneratedMessage(Builder<?> builder) {
+ unknownFields = builder.getUnknownFields();
}
public Parser<? extends GeneratedMessage> getParserForType() {
@@ -291,13 +300,12 @@ public abstract class GeneratedMessage extends AbstractMessage
return isClean;
}
- // This is implemented here only to work around an apparent bug in the
- // Java compiler and/or build system. See bug #1898463. The mere presence
- // of this dummy clone() implementation makes it go away.
@Override
public BuilderType clone() {
- throw new UnsupportedOperationException(
- "This is supposed to be overridden by subclasses.");
+ BuilderType builder =
+ (BuilderType) getDefaultInstanceForType().newBuilderForType();
+ builder.mergeFrom(buildPartial());
+ return builder;
}
/**
@@ -358,6 +366,13 @@ public abstract class GeneratedMessage extends AbstractMessage
}
//@Override (Java 1.6 override semantics, but we must support 1.5)
+ public Message.Builder getRepeatedFieldBuilder(final FieldDescriptor field,
+ int index) {
+ return internalGetFieldAccessorTable().getField(field).getRepeatedBuilder(
+ this, index);
+ }
+
+ //@Override (Java 1.6 override semantics, but we must support 1.5)
public boolean hasOneof(final OneofDescriptor oneof) {
return internalGetFieldAccessorTable().getOneof(oneof).has(this);
}
@@ -428,7 +443,7 @@ public abstract class GeneratedMessage extends AbstractMessage
return (BuilderType) this;
}
- public final BuilderType setUnknownFields(
+ public BuilderType setUnknownFields(
final UnknownFieldSet unknownFields) {
this.unknownFields = unknownFields;
onChanged();
@@ -436,7 +451,7 @@ public abstract class GeneratedMessage extends AbstractMessage
}
@Override
- public final BuilderType mergeUnknownFields(
+ public BuilderType mergeUnknownFields(
final UnknownFieldSet unknownFields) {
this.unknownFields =
UnknownFieldSet.newBuilder(this.unknownFields)
@@ -529,6 +544,25 @@ public abstract class GeneratedMessage extends AbstractMessage
isClean = false;
}
}
+
+ /**
+ * Gets the map field with the given field number. This method should be
+ * overridden in the generated message class if the message contains map
+ * fields.
+ *
+ * Unlike other field types, reflection support for map fields can't be
+ * implemented based on generated public API because we need to access a
+ * map field as a list in reflection API but the generated API only allows
+ * us to access it as a map. This method returns the underlying map field
+ * directly and thus enables us to access the map field as a list.
+ */
+ @SuppressWarnings({"unused", "rawtypes"})
+ protected MapField internalGetMapField(int fieldNumber) {
+ // Note that we can't use descriptor names here because this method will
+ // be called when descriptor is being initialized.
+ throw new RuntimeException(
+ "No map fields found in " + getClass().getName());
+ }
}
// =================================================================
@@ -541,19 +575,19 @@ public abstract class GeneratedMessage extends AbstractMessage
/** Check if a singular extension is present. */
<Type> boolean hasExtension(
- Extension<MessageType, Type> extension);
+ ExtensionLite<MessageType, Type> extension);
/** Get the number of elements in a repeated extension. */
<Type> int getExtensionCount(
- Extension<MessageType, List<Type>> extension);
+ ExtensionLite<MessageType, List<Type>> extension);
/** Get the value of an extension. */
<Type> Type getExtension(
- Extension<MessageType, Type> extension);
+ ExtensionLite<MessageType, Type> extension);
/** Get one element of a repeated extension. */
<Type> Type getExtension(
- Extension<MessageType, List<Type>> extension,
+ ExtensionLite<MessageType, List<Type>> extension,
int index);
}
@@ -625,7 +659,9 @@ public abstract class GeneratedMessage extends AbstractMessage
/** Check if a singular extension is present. */
//@Override (Java 1.6 override semantics, but we must support 1.5)
public final <Type> boolean hasExtension(
- final Extension<MessageType, Type> extension) {
+ final ExtensionLite<MessageType, Type> extensionLite) {
+ Extension<MessageType, Type> extension = checkNotLite(extensionLite);
+
verifyExtensionContainingType(extension);
return extensions.hasField(extension.getDescriptor());
}
@@ -633,7 +669,9 @@ public abstract class GeneratedMessage extends AbstractMessage
/** Get the number of elements in a repeated extension. */
//@Override (Java 1.6 override semantics, but we must support 1.5)
public final <Type> int getExtensionCount(
- final Extension<MessageType, List<Type>> extension) {
+ final ExtensionLite<MessageType, List<Type>> extensionLite) {
+ Extension<MessageType, List<Type>> extension = checkNotLite(extensionLite);
+
verifyExtensionContainingType(extension);
final FieldDescriptor descriptor = extension.getDescriptor();
return extensions.getRepeatedFieldCount(descriptor);
@@ -643,7 +681,9 @@ public abstract class GeneratedMessage extends AbstractMessage
//@Override (Java 1.6 override semantics, but we must support 1.5)
@SuppressWarnings("unchecked")
public final <Type> Type getExtension(
- final Extension<MessageType, Type> extension) {
+ final ExtensionLite<MessageType, Type> extensionLite) {
+ Extension<MessageType, Type> extension = checkNotLite(extensionLite);
+
verifyExtensionContainingType(extension);
FieldDescriptor descriptor = extension.getDescriptor();
final Object value = extensions.getField(descriptor);
@@ -666,8 +706,10 @@ public abstract class GeneratedMessage extends AbstractMessage
//@Override (Java 1.6 override semantics, but we must support 1.5)
@SuppressWarnings("unchecked")
public final <Type> Type getExtension(
- final Extension<MessageType, List<Type>> extension,
+ final ExtensionLite<MessageType, List<Type>> extensionLite,
final int index) {
+ Extension<MessageType, List<Type>> extension = checkNotLite(extensionLite);
+
verifyExtensionContainingType(extension);
FieldDescriptor descriptor = extension.getDescriptor();
return (Type) extension.singularFromReflectionType(
@@ -914,11 +956,10 @@ public abstract class GeneratedMessage extends AbstractMessage
// This is implemented here only to work around an apparent bug in the
// Java compiler and/or build system. See bug #1898463. The mere presence
- // of this dummy clone() implementation makes it go away.
+ // of this clone() implementation makes it go away.
@Override
public BuilderType clone() {
- throw new UnsupportedOperationException(
- "This is supposed to be overridden by subclasses.");
+ return super.clone();
}
private void ensureExtensionsIsMutable() {
@@ -943,7 +984,9 @@ public abstract class GeneratedMessage extends AbstractMessage
/** Check if a singular extension is present. */
//@Override (Java 1.6 override semantics, but we must support 1.5)
public final <Type> boolean hasExtension(
- final Extension<MessageType, Type> extension) {
+ final ExtensionLite<MessageType, Type> extensionLite) {
+ Extension<MessageType, Type> extension = checkNotLite(extensionLite);
+
verifyExtensionContainingType(extension);
return extensions.hasField(extension.getDescriptor());
}
@@ -951,7 +994,9 @@ public abstract class GeneratedMessage extends AbstractMessage
/** Get the number of elements in a repeated extension. */
//@Override (Java 1.6 override semantics, but we must support 1.5)
public final <Type> int getExtensionCount(
- final Extension<MessageType, List<Type>> extension) {
+ final ExtensionLite<MessageType, List<Type>> extensionLite) {
+ Extension<MessageType, List<Type>> extension = checkNotLite(extensionLite);
+
verifyExtensionContainingType(extension);
final FieldDescriptor descriptor = extension.getDescriptor();
return extensions.getRepeatedFieldCount(descriptor);
@@ -960,7 +1005,9 @@ public abstract class GeneratedMessage extends AbstractMessage
/** Get the value of an extension. */
//@Override (Java 1.6 override semantics, but we must support 1.5)
public final <Type> Type getExtension(
- final Extension<MessageType, Type> extension) {
+ final ExtensionLite<MessageType, Type> extensionLite) {
+ Extension<MessageType, Type> extension = checkNotLite(extensionLite);
+
verifyExtensionContainingType(extension);
FieldDescriptor descriptor = extension.getDescriptor();
final Object value = extensions.getField(descriptor);
@@ -982,8 +1029,10 @@ public abstract class GeneratedMessage extends AbstractMessage
/** Get one element of a repeated extension. */
//@Override (Java 1.6 override semantics, but we must support 1.5)
public final <Type> Type getExtension(
- final Extension<MessageType, List<Type>> extension,
+ final ExtensionLite<MessageType, List<Type>> extensionLite,
final int index) {
+ Extension<MessageType, List<Type>> extension = checkNotLite(extensionLite);
+
verifyExtensionContainingType(extension);
FieldDescriptor descriptor = extension.getDescriptor();
return (Type) extension.singularFromReflectionType(
@@ -992,8 +1041,10 @@ public abstract class GeneratedMessage extends AbstractMessage
/** Set the value of an extension. */
public final <Type> BuilderType setExtension(
- final Extension<MessageType, Type> extension,
+ final ExtensionLite<MessageType, Type> extensionLite,
final Type value) {
+ Extension<MessageType, Type> extension = checkNotLite(extensionLite);
+
verifyExtensionContainingType(extension);
ensureExtensionsIsMutable();
final FieldDescriptor descriptor = extension.getDescriptor();
@@ -1004,8 +1055,10 @@ public abstract class GeneratedMessage extends AbstractMessage
/** Set the value of one element of a repeated extension. */
public final <Type> BuilderType setExtension(
- final Extension<MessageType, List<Type>> extension,
+ final ExtensionLite<MessageType, List<Type>> extensionLite,
final int index, final Type value) {
+ Extension<MessageType, List<Type>> extension = checkNotLite(extensionLite);
+
verifyExtensionContainingType(extension);
ensureExtensionsIsMutable();
final FieldDescriptor descriptor = extension.getDescriptor();
@@ -1018,8 +1071,10 @@ public abstract class GeneratedMessage extends AbstractMessage
/** Append a value to a repeated extension. */
public final <Type> BuilderType addExtension(
- final Extension<MessageType, List<Type>> extension,
+ final ExtensionLite<MessageType, List<Type>> extensionLite,
final Type value) {
+ Extension<MessageType, List<Type>> extension = checkNotLite(extensionLite);
+
verifyExtensionContainingType(extension);
ensureExtensionsIsMutable();
final FieldDescriptor descriptor = extension.getDescriptor();
@@ -1031,7 +1086,9 @@ public abstract class GeneratedMessage extends AbstractMessage
/** Clear an extension. */
public final <Type> BuilderType clearExtension(
- final Extension<MessageType, ?> extension) {
+ final ExtensionLite<MessageType, ?> extensionLite) {
+ Extension<MessageType, ?> extension = checkNotLite(extensionLite);
+
verifyExtensionContainingType(extension);
ensureExtensionsIsMutable();
extensions.clearField(extension.getDescriptor());
@@ -1594,6 +1651,25 @@ public abstract class GeneratedMessage extends AbstractMessage
}
}
}
+
+ /**
+ * Gets the map field with the given field number. This method should be
+ * overridden in the generated message class if the message contains map
+ * fields.
+ *
+ * Unlike other field types, reflection support for map fields can't be
+ * implemented based on generated public API because we need to access a
+ * map field as a list in reflection API but the generated API only allows
+ * us to access it as a map. This method returns the underlying map field
+ * directly and thus enables us to access the map field as a list.
+ */
+ @SuppressWarnings({"rawtypes", "unused"})
+ protected MapField internalGetMapField(int fieldNumber) {
+ // Note that we can't use descriptor names here because this method will
+ // be called when descriptor is being initialized.
+ throw new RuntimeException(
+ "No map fields found in " + getClass().getName());
+ }
/**
* Users should ignore this class. This class provides the implementation
@@ -1633,6 +1709,11 @@ public abstract class GeneratedMessage extends AbstractMessage
oneofs = new OneofAccessor[descriptor.getOneofs().size()];
initialized = false;
}
+
+ private boolean isMapFieldEnabled(FieldDescriptor field) {
+ boolean result = true;
+ return result;
+ }
/**
* Ensures the field accessors are initialized. This method is thread-safe.
@@ -1657,8 +1738,13 @@ public abstract class GeneratedMessage extends AbstractMessage
}
if (field.isRepeated()) {
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
- fields[i] = new RepeatedMessageFieldAccessor(
- field, camelCaseNames[i], messageClass, builderClass);
+ if (field.isMapField() && isMapFieldEnabled(field)) {
+ fields[i] = new MapFieldAccessor(
+ field, camelCaseNames[i], messageClass, builderClass);
+ } else {
+ fields[i] = new RepeatedMessageFieldAccessor(
+ field, camelCaseNames[i], messageClass, builderClass);
+ }
} else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM) {
fields[i] = new RepeatedEnumFieldAccessor(
field, camelCaseNames[i], messageClass, builderClass);
@@ -1744,6 +1830,8 @@ public abstract class GeneratedMessage extends AbstractMessage
void clear(Builder builder);
Message.Builder newBuilder();
Message.Builder getBuilder(GeneratedMessage.Builder builder);
+ Message.Builder getRepeatedBuilder(GeneratedMessage.Builder builder,
+ int index);
}
/** OneofAccessor provides access to a single oneof. */
@@ -1799,9 +1887,9 @@ public abstract class GeneratedMessage extends AbstractMessage
invokeOrDie(clearMethod, builder);
}
}
-
+
private static boolean supportFieldPresence(FileDescriptor file) {
- return true;
+ return file.getSyntax() == FileDescriptor.Syntax.PROTO2;
}
// ---------------------------------------------------------------
@@ -1919,6 +2007,11 @@ public abstract class GeneratedMessage extends AbstractMessage
throw new UnsupportedOperationException(
"getFieldBuilder() called on a non-Message type.");
}
+ public Message.Builder getRepeatedBuilder(GeneratedMessage.Builder builder,
+ int index) {
+ throw new UnsupportedOperationException(
+ "getRepeatedFieldBuilder() called on a non-Message type.");
+ }
}
private static class RepeatedFieldAccessor implements FieldAccessor {
@@ -2014,6 +2107,113 @@ public abstract class GeneratedMessage extends AbstractMessage
throw new UnsupportedOperationException(
"getFieldBuilder() called on a non-Message type.");
}
+ public Message.Builder getRepeatedBuilder(GeneratedMessage.Builder builder,
+ int index) {
+ throw new UnsupportedOperationException(
+ "getRepeatedFieldBuilder() called on a non-Message type.");
+ }
+ }
+
+ private static class MapFieldAccessor implements FieldAccessor {
+ MapFieldAccessor(
+ final FieldDescriptor descriptor, final String camelCaseName,
+ final Class<? extends GeneratedMessage> messageClass,
+ final Class<? extends Builder> builderClass) {
+ field = descriptor;
+ Method getDefaultInstanceMethod =
+ getMethodOrDie(messageClass, "getDefaultInstance");
+ MapField defaultMapField = getMapField(
+ (GeneratedMessage) invokeOrDie(getDefaultInstanceMethod, null));
+ mapEntryMessageDefaultInstance =
+ defaultMapField.getMapEntryMessageDefaultInstance();
+ }
+
+ private final FieldDescriptor field;
+ private final Message mapEntryMessageDefaultInstance;
+
+ private MapField<?, ?> getMapField(GeneratedMessage message) {
+ return (MapField<?, ?>) message.internalGetMapField(field.getNumber());
+ }
+
+ private MapField<?, ?> getMapField(GeneratedMessage.Builder builder) {
+ return (MapField<?, ?>) builder.internalGetMapField(field.getNumber());
+ }
+
+ public Object get(GeneratedMessage message) {
+ List result = new ArrayList();
+ for (int i = 0; i < getRepeatedCount(message); i++) {
+ result.add(getRepeated(message, i));
+ }
+ return Collections.unmodifiableList(result);
+ }
+
+ public Object get(Builder builder) {
+ List result = new ArrayList();
+ for (int i = 0; i < getRepeatedCount(builder); i++) {
+ result.add(getRepeated(builder, i));
+ }
+ return Collections.unmodifiableList(result);
+ }
+
+ public void set(Builder builder, Object value) {
+ clear(builder);
+ for (Object entry : (List) value) {
+ addRepeated(builder, entry);
+ }
+ }
+
+ public Object getRepeated(GeneratedMessage message, int index) {
+ return getMapField(message).getList().get(index);
+ }
+
+ public Object getRepeated(Builder builder, int index) {
+ return getMapField(builder).getList().get(index);
+ }
+
+ public void setRepeated(Builder builder, int index, Object value) {
+ getMapField(builder).getMutableList().set(index, (Message) value);
+ }
+
+ public void addRepeated(Builder builder, Object value) {
+ getMapField(builder).getMutableList().add((Message) value);
+ }
+
+ public boolean has(GeneratedMessage message) {
+ throw new UnsupportedOperationException(
+ "hasField() is not supported for repeated fields.");
+ }
+
+ public boolean has(Builder builder) {
+ throw new UnsupportedOperationException(
+ "hasField() is not supported for repeated fields.");
+ }
+
+ public int getRepeatedCount(GeneratedMessage message) {
+ return getMapField(message).getList().size();
+ }
+
+ public int getRepeatedCount(Builder builder) {
+ return getMapField(builder).getList().size();
+ }
+
+ public void clear(Builder builder) {
+ getMapField(builder).getMutableList().clear();
+ }
+
+ public com.google.protobuf.Message.Builder newBuilder() {
+ return mapEntryMessageDefaultInstance.newBuilderForType();
+ }
+
+ public com.google.protobuf.Message.Builder getBuilder(Builder builder) {
+ throw new UnsupportedOperationException(
+ "Nested builder not supported for map fields.");
+ }
+
+ public com.google.protobuf.Message.Builder getRepeatedBuilder(
+ Builder builder, int index) {
+ throw new UnsupportedOperationException(
+ "Nested builder not supported for map fields.");
+ }
}
// ---------------------------------------------------------------
@@ -2026,28 +2226,60 @@ public abstract class GeneratedMessage extends AbstractMessage
final Class<? extends Builder> builderClass,
final String containingOneofCamelCaseName) {
super(descriptor, camelCaseName, messageClass, builderClass, containingOneofCamelCaseName);
+
+ enumDescriptor = descriptor.getEnumType();
valueOfMethod = getMethodOrDie(type, "valueOf",
EnumValueDescriptor.class);
getValueDescriptorMethod =
getMethodOrDie(type, "getValueDescriptor");
+
+ supportUnknownEnumValue = descriptor.getFile().supportsUnknownEnumValue();
+ if (supportUnknownEnumValue) {
+ getValueMethod =
+ getMethodOrDie(messageClass, "get" + camelCaseName + "Value");
+ getValueMethodBuilder =
+ getMethodOrDie(builderClass, "get" + camelCaseName + "Value");
+ setValueMethod =
+ getMethodOrDie(builderClass, "set" + camelCaseName + "Value", int.class);
+ }
}
+
+ private EnumDescriptor enumDescriptor;
private Method valueOfMethod;
private Method getValueDescriptorMethod;
+
+ private boolean supportUnknownEnumValue;
+ private Method getValueMethod;
+ private Method getValueMethodBuilder;
+ private Method setValueMethod;
@Override
public Object get(final GeneratedMessage message) {
+ if (supportUnknownEnumValue) {
+ int value = (Integer) invokeOrDie(getValueMethod, message);
+ return enumDescriptor.findValueByNumberCreatingIfUnknown(value);
+ }
return invokeOrDie(getValueDescriptorMethod, super.get(message));
}
@Override
public Object get(final GeneratedMessage.Builder builder) {
+ if (supportUnknownEnumValue) {
+ int value = (Integer) invokeOrDie(getValueMethodBuilder, builder);
+ return enumDescriptor.findValueByNumberCreatingIfUnknown(value);
+ }
return invokeOrDie(getValueDescriptorMethod, super.get(builder));
}
@Override
public void set(final Builder builder, final Object value) {
+ if (supportUnknownEnumValue) {
+ invokeOrDie(setValueMethod, builder,
+ ((EnumValueDescriptor) value).getNumber());
+ return;
+ }
super.set(builder, invokeOrDie(valueOfMethod, null, value));
}
}
@@ -2059,22 +2291,44 @@ public abstract class GeneratedMessage extends AbstractMessage
final Class<? extends GeneratedMessage> messageClass,
final Class<? extends Builder> builderClass) {
super(descriptor, camelCaseName, messageClass, builderClass);
+
+ enumDescriptor = descriptor.getEnumType();
valueOfMethod = getMethodOrDie(type, "valueOf",
EnumValueDescriptor.class);
getValueDescriptorMethod =
getMethodOrDie(type, "getValueDescriptor");
+
+ supportUnknownEnumValue = descriptor.getFile().supportsUnknownEnumValue();
+ if (supportUnknownEnumValue) {
+ getRepeatedValueMethod =
+ getMethodOrDie(messageClass, "get" + camelCaseName + "Value", int.class);
+ getRepeatedValueMethodBuilder =
+ getMethodOrDie(builderClass, "get" + camelCaseName + "Value", int.class);
+ setRepeatedValueMethod =
+ getMethodOrDie(builderClass, "set" + camelCaseName + "Value", int.class, int.class);
+ addRepeatedValueMethod =
+ getMethodOrDie(builderClass, "add" + camelCaseName + "Value", int.class);
+ }
}
+ private EnumDescriptor enumDescriptor;
private final Method valueOfMethod;
private final Method getValueDescriptorMethod;
+
+ private boolean supportUnknownEnumValue;
+ private Method getRepeatedValueMethod;
+ private Method getRepeatedValueMethodBuilder;
+ private Method setRepeatedValueMethod;
+ private Method addRepeatedValueMethod;
@Override
@SuppressWarnings("unchecked")
public Object get(final GeneratedMessage message) {
final List newList = new ArrayList();
- for (final Object element : (List) super.get(message)) {
- newList.add(invokeOrDie(getValueDescriptorMethod, element));
+ final int size = getRepeatedCount(message);
+ for (int i = 0; i < size; i++) {
+ newList.add(getRepeated(message, i));
}
return Collections.unmodifiableList(newList);
}
@@ -2083,8 +2337,9 @@ public abstract class GeneratedMessage extends AbstractMessage
@SuppressWarnings("unchecked")
public Object get(final GeneratedMessage.Builder builder) {
final List newList = new ArrayList();
- for (final Object element : (List) super.get(builder)) {
- newList.add(invokeOrDie(getValueDescriptorMethod, element));
+ final int size = getRepeatedCount(builder);
+ for (int i = 0; i < size; i++) {
+ newList.add(getRepeated(builder, i));
}
return Collections.unmodifiableList(newList);
}
@@ -2092,23 +2347,41 @@ public abstract class GeneratedMessage extends AbstractMessage
@Override
public Object getRepeated(final GeneratedMessage message,
final int index) {
+ if (supportUnknownEnumValue) {
+ int value = (Integer) invokeOrDie(getRepeatedValueMethod, message, index);
+ return enumDescriptor.findValueByNumberCreatingIfUnknown(value);
+ }
return invokeOrDie(getValueDescriptorMethod,
super.getRepeated(message, index));
}
@Override
public Object getRepeated(final GeneratedMessage.Builder builder,
final int index) {
+ if (supportUnknownEnumValue) {
+ int value = (Integer) invokeOrDie(getRepeatedValueMethodBuilder, builder, index);
+ return enumDescriptor.findValueByNumberCreatingIfUnknown(value);
+ }
return invokeOrDie(getValueDescriptorMethod,
super.getRepeated(builder, index));
}
@Override
public void setRepeated(final Builder builder,
final int index, final Object value) {
+ if (supportUnknownEnumValue) {
+ invokeOrDie(setRepeatedValueMethod, builder, index,
+ ((EnumValueDescriptor) value).getNumber());
+ return;
+ }
super.setRepeated(builder, index, invokeOrDie(valueOfMethod, null,
value));
}
@Override
public void addRepeated(final Builder builder, final Object value) {
+ if (supportUnknownEnumValue) {
+ invokeOrDie(addRepeatedValueMethod, builder,
+ ((EnumValueDescriptor) value).getNumber());
+ return;
+ }
super.addRepeated(builder, invokeOrDie(valueOfMethod, null, value));
}
}
@@ -2168,9 +2441,12 @@ public abstract class GeneratedMessage extends AbstractMessage
super(descriptor, camelCaseName, messageClass, builderClass);
newBuilderMethod = getMethodOrDie(type, "newBuilder");
+ getBuilderMethodBuilder = getMethodOrDie(builderClass,
+ "get" + camelCaseName + "Builder", Integer.TYPE);
}
private final Method newBuilderMethod;
+ private final Method getBuilderMethodBuilder;
private Object coerceType(final Object value) {
if (type.isInstance(value)) {
@@ -2198,6 +2474,12 @@ public abstract class GeneratedMessage extends AbstractMessage
public Message.Builder newBuilder() {
return (Message.Builder) invokeOrDie(newBuilderMethod, null);
}
+ @Override
+ public Message.Builder getRepeatedBuilder(
+ final GeneratedMessage.Builder builder, final int index) {
+ return (Message.Builder) invokeOrDie(
+ getBuilderMethodBuilder, builder, index);
+ }
}
}
@@ -2210,4 +2492,18 @@ public abstract class GeneratedMessage extends AbstractMessage
protected Object writeReplace() throws ObjectStreamException {
return new GeneratedMessageLite.SerializedForm(this);
}
+
+ /**
+ * Checks that the {@link Extension} is non-Lite and returns it as a
+ * {@link GeneratedExtension}.
+ */
+ private static <MessageType extends ExtendableMessage<MessageType>, T>
+ Extension<MessageType, T> checkNotLite(
+ ExtensionLite<MessageType, T> extension) {
+ if (extension.isLite()) {
+ throw new IllegalArgumentException("Expected non-lite extension.");
+ }
+
+ return (Extension<MessageType, T>) extension;
+ }
}
diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java
index 6c5136fd..8fe33bc6 100644
--- a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java
+++ b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java
@@ -30,6 +30,8 @@
package com.google.protobuf;
+import com.google.protobuf.WireFormat.FieldType;
+
import java.io.IOException;
import java.io.ObjectStreamException;
import java.io.Serializable;
@@ -50,10 +52,15 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
implements Serializable {
private static final long serialVersionUID = 1L;
+ /** For use by generated code only. */
+ protected UnknownFieldSetLite unknownFields;
+
protected GeneratedMessageLite() {
+ unknownFields = UnknownFieldSetLite.getDefaultInstance();
}
protected GeneratedMessageLite(Builder builder) {
+ unknownFields = builder.unknownFields;
}
public Parser<? extends MessageLite> getParserForType() {
@@ -62,15 +69,16 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
}
/**
- * Called by subclasses to parse an unknown field.
+ * Called by subclasses to parse an unknown field. For use by generated code
+ * only.
* @return {@code true} unless the tag is an end-group tag.
*/
- protected boolean parseUnknownField(
+ protected static boolean parseUnknownField(
CodedInputStream input,
- CodedOutputStream unknownFieldsCodedOutput,
+ UnknownFieldSetLite.Builder unknownFields,
ExtensionRegistryLite extensionRegistry,
int tag) throws IOException {
- return input.skipField(tag, unknownFieldsCodedOutput);
+ return unknownFields.mergeFieldFrom(tag, input);
}
/**
@@ -84,22 +92,28 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
public abstract static class Builder<MessageType extends GeneratedMessageLite,
BuilderType extends Builder>
extends AbstractMessageLite.Builder<BuilderType> {
+
+ private UnknownFieldSetLite unknownFields =
+ UnknownFieldSetLite.getDefaultInstance();
+
protected Builder() {}
//@Override (Java 1.6 override semantics, but we must support 1.5)
public BuilderType clear() {
- unknownFields = ByteString.EMPTY;
+ unknownFields = UnknownFieldSetLite.getDefaultInstance();
return (BuilderType) this;
}
- // This is implemented here only to work around an apparent bug in the
- // Java compiler and/or build system. See bug #1898463. The mere presence
- // of this dummy clone() implementation makes it go away.
- @Override
+ //@Override (Java 1.6 override semantics, but we must support 1.5)
public BuilderType clone() {
- throw new UnsupportedOperationException(
- "This is supposed to be overridden by subclasses.");
+ BuilderType builder =
+ (BuilderType) getDefaultInstanceForType().newBuilderForType();
+ builder.mergeFrom(buildPartial());
+ return builder;
}
+
+ /** All subclasses implement this. */
+ public abstract MessageType buildPartial();
/** All subclasses implement this. */
public abstract BuilderType mergeFrom(MessageType message);
@@ -113,22 +127,43 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
*/
protected boolean parseUnknownField(
CodedInputStream input,
- CodedOutputStream unknownFieldsCodedOutput,
+ UnknownFieldSetLite.Builder unknownFields,
ExtensionRegistryLite extensionRegistry,
int tag) throws IOException {
- return input.skipField(tag, unknownFieldsCodedOutput);
+ return unknownFields.mergeFieldFrom(tag, input);
}
- public final ByteString getUnknownFields() {
- return unknownFields;
+ /**
+ * Merge some unknown fields into the {@link UnknownFieldSetLite} for this
+ * message.
+ *
+ * <p>For use by generated code only.
+ */
+ protected final BuilderType mergeUnknownFields(
+ final UnknownFieldSetLite unknownFields) {
+ this.unknownFields = UnknownFieldSetLite.concat(this.unknownFields, unknownFields);
+ return (BuilderType) this;
}
-
- public final BuilderType setUnknownFields(final ByteString unknownFields) {
- this.unknownFields = unknownFields;
+
+ public BuilderType mergeFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ MessageType parsedMessage = null;
+ try {
+ parsedMessage =
+ (MessageType) getDefaultInstanceForType().getParserForType().parsePartialFrom(
+ input, extensionRegistry);
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ parsedMessage = (MessageType) e.getUnfinishedMessage();
+ throw e;
+ } finally {
+ if (parsedMessage != null) {
+ mergeFrom(parsedMessage);
+ }
+ }
return (BuilderType) this;
}
-
- private ByteString unknownFields = ByteString.EMPTY;
}
@@ -143,18 +178,18 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
/** Check if a singular extension is present. */
<Type> boolean hasExtension(
- GeneratedExtension<MessageType, Type> extension);
+ ExtensionLite<MessageType, Type> extension);
/** Get the number of elements in a repeated extension. */
<Type> int getExtensionCount(
- GeneratedExtension<MessageType, List<Type>> extension);
+ ExtensionLite<MessageType, List<Type>> extension);
/** Get the value of an extension. */
- <Type> Type getExtension(GeneratedExtension<MessageType, Type> extension);
+ <Type> Type getExtension(ExtensionLite<MessageType, Type> extension);
/** Get one element of a repeated extension. */
<Type> Type getExtension(
- GeneratedExtension<MessageType, List<Type>> extension,
+ ExtensionLite<MessageType, List<Type>> extension,
int index);
}
@@ -166,7 +201,11 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
extends GeneratedMessageLite
implements ExtendableMessageOrBuilder<MessageType> {
- private final FieldSet<ExtensionDescriptor> extensions;
+ /**
+ * Represents the set of extensions on this message. For use by generated
+ * code only.
+ */
+ protected final FieldSet<ExtensionDescriptor> extensions;
protected ExtendableMessage() {
this.extensions = FieldSet.newFieldSet();
@@ -190,30 +229,39 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
/** Check if a singular extension is present. */
//@Override (Java 1.6 override semantics, but we must support 1.5)
public final <Type> boolean hasExtension(
- final GeneratedExtension<MessageType, Type> extension) {
- verifyExtensionContainingType(extension);
- return extensions.hasField(extension.descriptor);
+ final ExtensionLite<MessageType, Type> extension) {
+ GeneratedExtension<MessageType, Type> extensionLite =
+ checkIsLite(extension);
+
+ verifyExtensionContainingType(extensionLite);
+ return extensions.hasField(extensionLite.descriptor);
}
/** Get the number of elements in a repeated extension. */
//@Override (Java 1.6 override semantics, but we must support 1.5)
public final <Type> int getExtensionCount(
- final GeneratedExtension<MessageType, List<Type>> extension) {
- verifyExtensionContainingType(extension);
- return extensions.getRepeatedFieldCount(extension.descriptor);
+ final ExtensionLite<MessageType, List<Type>> extension) {
+ GeneratedExtension<MessageType, List<Type>> extensionLite =
+ checkIsLite(extension);
+
+ verifyExtensionContainingType(extensionLite);
+ return extensions.getRepeatedFieldCount(extensionLite.descriptor);
}
/** Get the value of an extension. */
//@Override (Java 1.6 override semantics, but we must support 1.5)
@SuppressWarnings("unchecked")
public final <Type> Type getExtension(
- final GeneratedExtension<MessageType, Type> extension) {
- verifyExtensionContainingType(extension);
- final Object value = extensions.getField(extension.descriptor);
+ final ExtensionLite<MessageType, Type> extension) {
+ GeneratedExtension<MessageType, Type> extensionLite =
+ checkIsLite(extension);
+
+ verifyExtensionContainingType(extensionLite);
+ final Object value = extensions.getField(extensionLite.descriptor);
if (value == null) {
- return extension.defaultValue;
+ return extensionLite.defaultValue;
} else {
- return (Type) extension.fromFieldSetType(value);
+ return (Type) extensionLite.fromFieldSetType(value);
}
}
@@ -221,11 +269,14 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
//@Override (Java 1.6 override semantics, but we must support 1.5)
@SuppressWarnings("unchecked")
public final <Type> Type getExtension(
- final GeneratedExtension<MessageType, List<Type>> extension,
+ final ExtensionLite<MessageType, List<Type>> extension,
final int index) {
- verifyExtensionContainingType(extension);
- return (Type) extension.singularFromFieldSetType(
- extensions.getRepeatedField(extension.descriptor, index));
+ GeneratedExtension<MessageType, List<Type>> extensionLite =
+ checkIsLite(extension);
+
+ verifyExtensionContainingType(extensionLite);
+ return (Type) extensionLite.singularFromFieldSetType(
+ extensions.getRepeatedField(extensionLite.descriptor, index));
}
/** Called by subclasses to check if all extensions are initialized. */
@@ -233,25 +284,6 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
return extensions.isInitialized();
}
- /**
- * Called by subclasses to parse an unknown field or an extension.
- * @return {@code true} unless the tag is an end-group tag.
- */
- @Override
- protected boolean parseUnknownField(
- CodedInputStream input,
- CodedOutputStream unknownFieldsCodedOutput,
- ExtensionRegistryLite extensionRegistry,
- int tag) throws IOException {
- return GeneratedMessageLite.parseUnknownField(
- extensions,
- getDefaultInstanceForType(),
- input,
- unknownFieldsCodedOutput,
- extensionRegistry,
- tag);
- }
-
/**
* Used by parsing constructors in generated classes.
@@ -377,30 +409,39 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
/** Check if a singular extension is present. */
//@Override (Java 1.6 override semantics, but we must support 1.5)
public final <Type> boolean hasExtension(
- final GeneratedExtension<MessageType, Type> extension) {
- verifyExtensionContainingType(extension);
- return extensions.hasField(extension.descriptor);
+ final ExtensionLite<MessageType, Type> extension) {
+ GeneratedExtension<MessageType, Type> extensionLite =
+ checkIsLite(extension);
+
+ verifyExtensionContainingType(extensionLite);
+ return extensions.hasField(extensionLite.descriptor);
}
/** Get the number of elements in a repeated extension. */
//@Override (Java 1.6 override semantics, but we must support 1.5)
public final <Type> int getExtensionCount(
- final GeneratedExtension<MessageType, List<Type>> extension) {
- verifyExtensionContainingType(extension);
- return extensions.getRepeatedFieldCount(extension.descriptor);
+ final ExtensionLite<MessageType, List<Type>> extension) {
+ GeneratedExtension<MessageType, List<Type>> extensionLite =
+ checkIsLite(extension);
+
+ verifyExtensionContainingType(extensionLite);
+ return extensions.getRepeatedFieldCount(extensionLite.descriptor);
}
/** Get the value of an extension. */
//@Override (Java 1.6 override semantics, but we must support 1.5)
@SuppressWarnings("unchecked")
public final <Type> Type getExtension(
- final GeneratedExtension<MessageType, Type> extension) {
- verifyExtensionContainingType(extension);
- final Object value = extensions.getField(extension.descriptor);
+ final ExtensionLite<MessageType, Type> extension) {
+ GeneratedExtension<MessageType, Type> extensionLite =
+ checkIsLite(extension);
+
+ verifyExtensionContainingType(extensionLite);
+ final Object value = extensions.getField(extensionLite.descriptor);
if (value == null) {
- return extension.defaultValue;
+ return extensionLite.defaultValue;
} else {
- return (Type) extension.fromFieldSetType(value);
+ return (Type) extensionLite.fromFieldSetType(value);
}
}
@@ -408,11 +449,14 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
@SuppressWarnings("unchecked")
//@Override (Java 1.6 override semantics, but we must support 1.5)
public final <Type> Type getExtension(
- final GeneratedExtension<MessageType, List<Type>> extension,
+ final ExtensionLite<MessageType, List<Type>> extension,
final int index) {
- verifyExtensionContainingType(extension);
- return (Type) extension.singularFromFieldSetType(
- extensions.getRepeatedField(extension.descriptor, index));
+ GeneratedExtension<MessageType, List<Type>> extensionLite =
+ checkIsLite(extension);
+
+ verifyExtensionContainingType(extensionLite);
+ return (Type) extensionLite.singularFromFieldSetType(
+ extensions.getRepeatedField(extensionLite.descriptor, index));
}
// This is implemented here only to work around an apparent bug in the
@@ -423,46 +467,57 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
throw new UnsupportedOperationException(
"This is supposed to be overridden by subclasses.");
}
-
+
/** Set the value of an extension. */
public final <Type> BuilderType setExtension(
- final GeneratedExtension<MessageType, Type> extension,
+ final ExtensionLite<MessageType, Type> extension,
final Type value) {
- verifyExtensionContainingType(extension);
+ GeneratedExtension<MessageType, Type> extensionLite =
+ checkIsLite(extension);
+
+ verifyExtensionContainingType(extensionLite);
ensureExtensionsIsMutable();
- extensions.setField(extension.descriptor,
- extension.toFieldSetType(value));
+ extensions.setField(extensionLite.descriptor,
+ extensionLite.toFieldSetType(value));
return (BuilderType) this;
}
/** Set the value of one element of a repeated extension. */
public final <Type> BuilderType setExtension(
- final GeneratedExtension<MessageType, List<Type>> extension,
+ final ExtensionLite<MessageType, List<Type>> extension,
final int index, final Type value) {
- verifyExtensionContainingType(extension);
+ GeneratedExtension<MessageType, List<Type>> extensionLite =
+ checkIsLite(extension);
+
+ verifyExtensionContainingType(extensionLite);
ensureExtensionsIsMutable();
- extensions.setRepeatedField(extension.descriptor, index,
- extension.singularToFieldSetType(value));
+ extensions.setRepeatedField(extensionLite.descriptor, index,
+ extensionLite.singularToFieldSetType(value));
return (BuilderType) this;
}
/** Append a value to a repeated extension. */
public final <Type> BuilderType addExtension(
- final GeneratedExtension<MessageType, List<Type>> extension,
+ final ExtensionLite<MessageType, List<Type>> extension,
final Type value) {
- verifyExtensionContainingType(extension);
+ GeneratedExtension<MessageType, List<Type>> extensionLite =
+ checkIsLite(extension);
+
+ verifyExtensionContainingType(extensionLite);
ensureExtensionsIsMutable();
- extensions.addRepeatedField(extension.descriptor,
- extension.singularToFieldSetType(value));
+ extensions.addRepeatedField(extensionLite.descriptor,
+ extensionLite.singularToFieldSetType(value));
return (BuilderType) this;
}
/** Clear an extension. */
public final <Type> BuilderType clearExtension(
- final GeneratedExtension<MessageType, ?> extension) {
- verifyExtensionContainingType(extension);
+ final ExtensionLite<MessageType, ?> extension) {
+ GeneratedExtension<MessageType, ?> extensionLite = checkIsLite(extension);
+
+ verifyExtensionContainingType(extensionLite);
ensureExtensionsIsMutable();
- extensions.clearField(extension.descriptor);
+ extensions.clearField(extensionLite.descriptor);
return (BuilderType) this;
}
@@ -471,44 +526,24 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
return extensions.isInitialized();
}
- /**
- * Called by subclasses to parse an unknown field or an extension.
- * @return {@code true} unless the tag is an end-group tag.
- */
- @Override
- protected boolean parseUnknownField(
- CodedInputStream input,
- CodedOutputStream unknownFieldsCodedOutput,
- ExtensionRegistryLite extensionRegistry,
- int tag) throws IOException {
- ensureExtensionsIsMutable();
- return GeneratedMessageLite.parseUnknownField(
- extensions,
- getDefaultInstanceForType(),
- input,
- unknownFieldsCodedOutput,
- extensionRegistry,
- tag);
- }
-
protected final void mergeExtensionFields(final MessageType other) {
ensureExtensionsIsMutable();
extensions.mergeFrom(((ExtendableMessage) other).extensions);
}
}
- // -----------------------------------------------------------------
+ //-----------------------------------------------------------------
/**
- * Parse an unknown field or an extension.
+ * Parse an unknown field or an extension. For use by generated code only.
* @return {@code true} unless the tag is an end-group tag.
*/
- private static <MessageType extends MessageLite>
+ protected static <MessageType extends MessageLite>
boolean parseUnknownField(
FieldSet<ExtensionDescriptor> extensions,
MessageType defaultInstance,
CodedInputStream input,
- CodedOutputStream unknownFieldsCodedOutput,
+ UnknownFieldSetLite.Builder unknownFields,
ExtensionRegistryLite extensionRegistry,
int tag) throws IOException {
int wireType = WireFormat.getTagWireType(tag);
@@ -537,7 +572,7 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
}
if (unknown) { // Unknown field or wrong wire type. Skip.
- return input.skipField(tag, unknownFieldsCodedOutput);
+ return unknownFields.mergeFieldFrom(tag, input);
}
if (packed) {
@@ -599,8 +634,7 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
// If the number isn't recognized as a valid value for this enum,
// write it to unknown fields object.
if (value == null) {
- unknownFieldsCodedOutput.writeRawVarint32(tag);
- unknownFieldsCodedOutput.writeUInt32NoTag(rawValue);
+ unknownFields.mergeVarintField(fieldNumber, rawValue);
return true;
}
break;
@@ -768,7 +802,8 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
* this type as parameters to extension accessors and ExtensionRegistry.add().
*/
public static class GeneratedExtension<
- ContainingType extends MessageLite, Type> {
+ ContainingType extends MessageLite, Type>
+ extends ExtensionLite<ContainingType, Type> {
/**
* Create a new isntance with the given parameters.
@@ -888,6 +923,18 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
return value;
}
}
+
+ public FieldType getLiteType() {
+ return descriptor.getLiteType();
+ }
+
+ public boolean isRepeated() {
+ return descriptor.isRepeated;
+ }
+
+ public Type getDefaultValue() {
+ return defaultValue;
+ }
}
/**
@@ -897,8 +944,8 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
static final class SerializedForm implements Serializable {
private static final long serialVersionUID = 0L;
- private String messageClassName;
- private byte[] asBytes;
+ private final String messageClassName;
+ private final byte[] asBytes;
/**
* Creates the serialized form by calling {@link com.google.protobuf.MessageLite#toByteArray}.
@@ -918,19 +965,17 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
protected Object readResolve() throws ObjectStreamException {
try {
Class messageClass = Class.forName(messageClassName);
- Method newBuilder = messageClass.getMethod("newBuilder");
- MessageLite.Builder builder =
- (MessageLite.Builder) newBuilder.invoke(null);
- builder.mergeFrom(asBytes);
- return builder.buildPartial();
+ Parser<?> parser =
+ (Parser<?>) messageClass.getField("PARSER").get(null);
+ return parser.parsePartialFrom(asBytes);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Unable to find proto buffer class", e);
- } catch (NoSuchMethodException e) {
- throw new RuntimeException("Unable to find newBuilder method", e);
+ } catch (NoSuchFieldException e) {
+ throw new RuntimeException("Unable to find PARSER", e);
+ } catch (SecurityException e) {
+ throw new RuntimeException("Unable to call PARSER", e);
} catch (IllegalAccessException e) {
- throw new RuntimeException("Unable to call newBuilder method", e);
- } catch (InvocationTargetException e) {
- throw new RuntimeException("Error calling newBuilder", e.getCause());
+ throw new RuntimeException("Unable to call parseFrom method", e);
} catch (InvalidProtocolBufferException e) {
throw new RuntimeException("Unable to understand proto buffer", e);
}
@@ -946,4 +991,18 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
protected Object writeReplace() throws ObjectStreamException {
return new SerializedForm(this);
}
+
+ /**
+ * Checks that the {@link Extension} is Lite and returns it as a
+ * {@link GeneratedExtension}.
+ */
+ private static <MessageType extends ExtendableMessage<MessageType>, T>
+ GeneratedExtension<MessageType, T> checkIsLite(
+ ExtensionLite<MessageType, T> extension) {
+ if (!extension.isLite()) {
+ throw new IllegalArgumentException("Expected a lite extension.");
+ }
+
+ return (GeneratedExtension<MessageType, T>) extension;
+ }
}
diff --git a/java/src/main/java/com/google/protobuf/Internal.java b/java/src/main/java/com/google/protobuf/Internal.java
index 48d29e69..ba0f526b 100644
--- a/java/src/main/java/com/google/protobuf/Internal.java
+++ b/java/src/main/java/com/google/protobuf/Internal.java
@@ -33,8 +33,14 @@ package com.google.protobuf;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
+import java.util.AbstractList;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
import java.util.Arrays;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
/**
* The classes contained within are used internally by the Protocol Buffer
@@ -388,4 +394,164 @@ public class Internal {
public static final ByteBuffer EMPTY_BYTE_BUFFER =
ByteBuffer.wrap(EMPTY_BYTE_ARRAY);
+
+ /**
+ * Provides an immutable view of List<T> around a List<F>.
+ *
+ * Protobuf internal. Used in protobuf generated code only.
+ */
+ public static class ListAdapter<F, T> extends AbstractList<T> {
+ /**
+ * Convert individual elements of the List from F to T.
+ */
+ public interface Converter<F, T> {
+ T convert(F from);
+ }
+
+ private final List<F> fromList;
+ private final Converter<F, T> converter;
+
+ public ListAdapter(List<F> fromList, Converter<F, T> converter) {
+ this.fromList = fromList;
+ this.converter = converter;
+ }
+
+ @Override
+ public T get(int index) {
+ return converter.convert(fromList.get(index));
+ }
+
+ @Override
+ public int size() {
+ return fromList.size();
+ }
+ }
+
+ /**
+ * Wrap around a Map<K, RealValue> and provide a Map<K, V> interface.
+ */
+ public static class MapAdapter<K, V, RealValue> extends AbstractMap<K, V> {
+ /**
+ * An interface used to convert between two types.
+ */
+ public interface Converter<A, B> {
+ B doForward(A object);
+ A doBackward(B object);
+ }
+
+ public static <T extends EnumLite> Converter<Integer, T> newEnumConverter(
+ final EnumLiteMap<T> enumMap, final T unrecognizedValue) {
+ return new Converter<Integer, T>() {
+ public T doForward(Integer value) {
+ T result = enumMap.findValueByNumber(value);
+ return result == null ? unrecognizedValue : result;
+ }
+ public Integer doBackward(T value) {
+ return value.getNumber();
+ }
+ };
+ }
+
+ private final Map<K, RealValue> realMap;
+ private final Converter<RealValue, V> valueConverter;
+
+ public MapAdapter(Map<K, RealValue> realMap,
+ Converter<RealValue, V> valueConverter) {
+ this.realMap = realMap;
+ this.valueConverter = valueConverter;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public V get(Object key) {
+ RealValue result = realMap.get(key);
+ if (result == null) {
+ return null;
+ }
+ return valueConverter.doForward(result);
+ }
+
+ @Override
+ public V put(K key, V value) {
+ RealValue oldValue = realMap.put(key, valueConverter.doBackward(value));
+ if (oldValue == null) {
+ return null;
+ }
+ return valueConverter.doForward(oldValue);
+ }
+
+ @Override
+ public Set<java.util.Map.Entry<K, V>> entrySet() {
+ return new SetAdapter(realMap.entrySet());
+ }
+
+ private class SetAdapter extends AbstractSet<Map.Entry<K, V>> {
+ private final Set<Map.Entry<K, RealValue>> realSet;
+ public SetAdapter(Set<Map.Entry<K, RealValue>> realSet) {
+ this.realSet = realSet;
+ }
+
+ @Override
+ public Iterator<java.util.Map.Entry<K, V>> iterator() {
+ return new IteratorAdapter(realSet.iterator());
+ }
+
+ @Override
+ public int size() {
+ return realSet.size();
+ }
+ }
+
+ private class IteratorAdapter implements Iterator<Map.Entry<K, V>> {
+ private final Iterator<Map.Entry<K, RealValue>> realIterator;
+
+ public IteratorAdapter(
+ Iterator<Map.Entry<K, RealValue>> realIterator) {
+ this.realIterator = realIterator;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return realIterator.hasNext();
+ }
+
+ @Override
+ public java.util.Map.Entry<K, V> next() {
+ return new EntryAdapter(realIterator.next());
+ }
+
+ @Override
+ public void remove() {
+ realIterator.remove();
+ }
+ }
+
+ private class EntryAdapter implements Map.Entry<K, V> {
+ private final Map.Entry<K, RealValue> realEntry;
+
+ public EntryAdapter(Map.Entry<K, RealValue> realEntry) {
+ this.realEntry = realEntry;
+ }
+
+ @Override
+ public K getKey() {
+ return realEntry.getKey();
+ }
+
+ @Override
+ public V getValue() {
+ return valueConverter.doForward(realEntry.getValue());
+ }
+
+ @Override
+ public V setValue(V value) {
+ RealValue oldValue = realEntry.setValue(
+ valueConverter.doBackward(value));
+ if (oldValue == null) {
+ return null;
+ }
+ return valueConverter.doForward(oldValue);
+ }
+ }
+ }
}
diff --git a/java/src/main/java/com/google/protobuf/LazyStringArrayList.java b/java/src/main/java/com/google/protobuf/LazyStringArrayList.java
index 61c7e1ea..2d40a51f 100644
--- a/java/src/main/java/com/google/protobuf/LazyStringArrayList.java
+++ b/java/src/main/java/com/google/protobuf/LazyStringArrayList.java
@@ -74,6 +74,10 @@ public class LazyStringArrayList extends AbstractList<String>
list = new ArrayList<Object>();
}
+ public LazyStringArrayList(int intialCapacity) {
+ list = new ArrayList<Object>(intialCapacity);
+ }
+
public LazyStringArrayList(LazyStringList from) {
list = new ArrayList<Object>(from.size());
addAll(from);
diff --git a/java/src/main/java/com/google/protobuf/LiteralByteString.java b/java/src/main/java/com/google/protobuf/LiteralByteString.java
index 83e71e93..127c574d 100644
--- a/java/src/main/java/com/google/protobuf/LiteralByteString.java
+++ b/java/src/main/java/com/google/protobuf/LiteralByteString.java
@@ -190,6 +190,15 @@ class LiteralByteString extends ByteString {
}
if (other instanceof LiteralByteString) {
+ LiteralByteString otherAsLiteral = (LiteralByteString) other;
+ // If we know the hash codes and they are not equal, we know the byte
+ // strings are not equal.
+ if (hash != 0
+ && otherAsLiteral.hash != 0
+ && hash != otherAsLiteral.hash) {
+ return false;
+ }
+
return equalsRange((LiteralByteString) other, 0, size());
} else if (other instanceof RopeByteString) {
return other.equals(this);
diff --git a/java/src/main/java/com/google/protobuf/MapEntry.java b/java/src/main/java/com/google/protobuf/MapEntry.java
new file mode 100644
index 00000000..8e3551b6
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/MapEntry.java
@@ -0,0 +1,433 @@
+// 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.EnumValueDescriptor;
+import com.google.protobuf.Descriptors.FieldDescriptor;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Implements MapEntry messages.
+ *
+ * In reflection API, map fields will be treated as repeated message fields and
+ * each map entry is accessed as a message. This MapEntry class is used to
+ * represent these map entry messages in reflection API.
+ *
+ * Protobuf internal. Users shouldn't use this class.
+ */
+public final class MapEntry<K, V> extends AbstractMessage {
+ private static class Metadata<K, V> {
+ public final Descriptor descriptor;
+ public final MapEntry<K, V> defaultInstance;
+ public final AbstractParser<MapEntry<K, V>> parser;
+
+ public Metadata(
+ final Descriptor descriptor, final MapEntry<K, V> defaultInstance) {
+ this.descriptor = descriptor;
+ this.defaultInstance = defaultInstance;
+ final Metadata<K, V> thisMetadata = this;
+ this.parser = new AbstractParser<MapEntry<K, V>>() {
+ private final Parser<MapEntryLite<K, V>> dataParser =
+ defaultInstance.data.getParserForType();
+ @Override
+ public MapEntry<K, V> parsePartialFrom(
+ CodedInputStream input, ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException {
+ MapEntryLite<K, V> data =
+ dataParser.parsePartialFrom(input, extensionRegistry);
+ return new MapEntry<K, V>(thisMetadata, data);
+ }
+
+ };
+ }
+ }
+
+ private final Metadata<K, V> metadata;
+ private final MapEntryLite<K, V> data;
+
+ /** Create a default MapEntry instance. */
+ private MapEntry(Descriptor descriptor,
+ WireFormat.FieldType keyType, K defaultKey,
+ WireFormat.FieldType valueType, V defaultValue) {
+ this.data = MapEntryLite.newDefaultInstance(
+ keyType, defaultKey, valueType, defaultValue);
+ this.metadata = new Metadata<K, V>(descriptor, this);
+ }
+
+ /** Create a new MapEntry message. */
+ private MapEntry(Metadata<K, V> metadata, MapEntryLite<K, V> data) {
+ this.metadata = metadata;
+ this.data = data;
+ }
+
+ /**
+ * Create a default MapEntry instance. A default MapEntry instance should be
+ * created only once for each map entry message type. Generated code should
+ * store the created default instance and use it later to create new MapEntry
+ * messages of the same type.
+ */
+ public static <K, V> MapEntry<K, V> newDefaultInstance(
+ Descriptor descriptor,
+ WireFormat.FieldType keyType, K defaultKey,
+ WireFormat.FieldType valueType, V defaultValue) {
+ return new MapEntry<K, V>(
+ descriptor, keyType, defaultKey, valueType, defaultValue);
+ }
+
+ public K getKey() {
+ return data.getKey();
+ }
+
+ public V getValue() {
+ return data.getValue();
+ }
+
+ @Override
+ public int getSerializedSize() {
+ return data.getSerializedSize();
+ }
+
+ @Override
+ public void writeTo(CodedOutputStream output) throws IOException {
+ data.writeTo(output);
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return data.isInitialized();
+ }
+
+ @Override
+ public Parser<MapEntry<K, V>> getParserForType() {
+ return metadata.parser;
+ }
+
+ @Override
+ public Builder<K, V> newBuilderForType() {
+ return new Builder<K, V>(metadata);
+ }
+
+ @Override
+ public Builder<K, V> toBuilder() {
+ return new Builder<K, V>(metadata, data);
+ }
+
+ @Override
+ public MapEntry<K, V> getDefaultInstanceForType() {
+ return metadata.defaultInstance;
+ }
+
+ @Override
+ public Descriptor getDescriptorForType() {
+ return metadata.descriptor;
+ }
+
+ @Override
+ public Map<FieldDescriptor, Object> getAllFields() {
+ final TreeMap<FieldDescriptor, Object> result =
+ new TreeMap<FieldDescriptor, Object>();
+ for (final FieldDescriptor field : metadata.descriptor.getFields()) {
+ if (hasField(field)) {
+ result.put(field, getField(field));
+ }
+ }
+ return Collections.unmodifiableMap(result);
+ }
+
+ private void checkFieldDescriptor(FieldDescriptor field) {
+ if (field.getContainingType() != metadata.descriptor) {
+ throw new RuntimeException(
+ "Wrong FieldDescriptor \"" + field.getFullName()
+ + "\" used in message \"" + metadata.descriptor.getFullName());
+ }
+ }
+
+ @Override
+ public boolean hasField(FieldDescriptor field) {
+ checkFieldDescriptor(field);;
+ // A MapEntry always contains two fields.
+ return true;
+ }
+
+ @Override
+ public Object getField(FieldDescriptor field) {
+ checkFieldDescriptor(field);
+ Object result = field.getNumber() == 1 ? getKey() : getValue();
+ // Convert enums to EnumValueDescriptor.
+ if (field.getType() == FieldDescriptor.Type.ENUM) {
+ result = field.getEnumType().findValueByNumberCreatingIfUnknown(
+ (java.lang.Integer) result);
+ }
+ return result;
+ }
+
+ @Override
+ public int getRepeatedFieldCount(FieldDescriptor field) {
+ throw new RuntimeException(
+ "There is no repeated field in a map entry message.");
+ }
+
+ @Override
+ public Object getRepeatedField(FieldDescriptor field, int index) {
+ throw new RuntimeException(
+ "There is no repeated field in a map entry message.");
+ }
+
+ @Override
+ public UnknownFieldSet getUnknownFields() {
+ return UnknownFieldSet.getDefaultInstance();
+ }
+
+ /**
+ * Builder to create {@link MapEntry} messages.
+ */
+ public static class Builder<K, V>
+ extends AbstractMessage.Builder<Builder<K, V>> {
+ private final Metadata<K, V> metadata;
+ private MapEntryLite<K, V> data;
+ private MapEntryLite.Builder<K, V> dataBuilder;
+
+ private Builder(Metadata<K, V> metadata) {
+ this.metadata = metadata;
+ this.data = metadata.defaultInstance.data;
+ this.dataBuilder = null;
+ }
+
+ private Builder(Metadata<K, V> metadata, MapEntryLite<K, V> data) {
+ this.metadata = metadata;
+ this.data = data;
+ this.dataBuilder = null;
+ }
+
+ public K getKey() {
+ return dataBuilder == null ? data.getKey() : dataBuilder.getKey();
+ }
+
+ public V getValue() {
+ return dataBuilder == null ? data.getValue() : dataBuilder.getValue();
+ }
+
+ private void ensureMutable() {
+ if (dataBuilder == null) {
+ dataBuilder = data.toBuilder();
+ }
+ }
+
+ public Builder<K, V> setKey(K key) {
+ ensureMutable();
+ dataBuilder.setKey(key);
+ return this;
+ }
+
+ public Builder<K, V> clearKey() {
+ ensureMutable();
+ dataBuilder.clearKey();
+ return this;
+ }
+
+ public Builder<K, V> setValue(V value) {
+ ensureMutable();
+ dataBuilder.setValue(value);
+ return this;
+ }
+
+ public Builder<K, V> clearValue() {
+ ensureMutable();
+ dataBuilder.clearValue();
+ return this;
+ }
+
+ @Override
+ public MapEntry<K, V> build() {
+ MapEntry<K, V> result = buildPartial();
+ if (!result.isInitialized()) {
+ throw newUninitializedMessageException(result);
+ }
+ return result;
+ }
+
+ @Override
+ public MapEntry<K, V> buildPartial() {
+ if (dataBuilder != null) {
+ data = dataBuilder.build();
+ dataBuilder = null;
+ }
+ return new MapEntry<K, V>(metadata, data);
+ }
+
+ @Override
+ public Descriptor getDescriptorForType() {
+ return metadata.descriptor;
+ }
+
+ private void checkFieldDescriptor(FieldDescriptor field) {
+ if (field.getContainingType() != metadata.descriptor) {
+ throw new RuntimeException(
+ "Wrong FieldDescriptor \"" + field.getFullName()
+ + "\" used in message \"" + metadata.descriptor.getFullName());
+ }
+ }
+
+ @Override
+ public com.google.protobuf.Message.Builder newBuilderForField(
+ FieldDescriptor field) {
+ checkFieldDescriptor(field);;
+ // This method should be called for message fields and in a MapEntry
+ // message only the value field can possibly be a message field.
+ if (field.getNumber() != 2
+ || field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
+ throw new RuntimeException(
+ "\"" + field.getFullName() + "\" is not a message value field.");
+ }
+ return ((Message) data.getValue()).newBuilderForType();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Builder<K, V> setField(FieldDescriptor field, Object value) {
+ checkFieldDescriptor(field);
+ if (field.getNumber() == 1) {
+ setKey((K) value);
+ } else {
+ if (field.getType() == FieldDescriptor.Type.ENUM) {
+ value = ((EnumValueDescriptor) value).getNumber();
+ }
+ setValue((V) value);
+ }
+ return this;
+ }
+
+ @Override
+ public Builder<K, V> clearField(FieldDescriptor field) {
+ checkFieldDescriptor(field);
+ if (field.getNumber() == 1) {
+ clearKey();
+ } else {
+ clearValue();
+ }
+ return this;
+ }
+
+ @Override
+ public Builder<K, V> setRepeatedField(FieldDescriptor field, int index,
+ Object value) {
+ throw new RuntimeException(
+ "There is no repeated field in a map entry message.");
+ }
+
+ @Override
+ public Builder<K, V> addRepeatedField(FieldDescriptor field, Object value) {
+ throw new RuntimeException(
+ "There is no repeated field in a map entry message.");
+ }
+
+ @Override
+ public Builder<K, V> setUnknownFields(UnknownFieldSet unknownFields) {
+ // Unknown fields are discarded for MapEntry message.
+ return this;
+ }
+
+ @Override
+ public MapEntry<K, V> getDefaultInstanceForType() {
+ return metadata.defaultInstance;
+ }
+
+ @Override
+ public boolean isInitialized() {
+ if (dataBuilder != null) {
+ return dataBuilder.isInitialized();
+ } else {
+ return data.isInitialized();
+ }
+ }
+
+ @Override
+ public Map<FieldDescriptor, Object> getAllFields() {
+ final TreeMap<FieldDescriptor, Object> result =
+ new TreeMap<FieldDescriptor, Object>();
+ for (final FieldDescriptor field : metadata.descriptor.getFields()) {
+ if (hasField(field)) {
+ result.put(field, getField(field));
+ }
+ }
+ return Collections.unmodifiableMap(result);
+ }
+
+ @Override
+ public boolean hasField(FieldDescriptor field) {
+ checkFieldDescriptor(field);
+ return true;
+ }
+
+ @Override
+ public Object getField(FieldDescriptor field) {
+ checkFieldDescriptor(field);
+ Object result = field.getNumber() == 1 ? getKey() : getValue();
+ // Convert enums to EnumValueDescriptor.
+ if (field.getType() == FieldDescriptor.Type.ENUM) {
+ result = field.getEnumType().findValueByNumberCreatingIfUnknown(
+ (java.lang.Integer) result);
+ }
+ return result;
+ }
+
+ @Override
+ public int getRepeatedFieldCount(FieldDescriptor field) {
+ throw new RuntimeException(
+ "There is no repeated field in a map entry message.");
+ }
+
+ @Override
+ public Object getRepeatedField(FieldDescriptor field, int index) {
+ throw new RuntimeException(
+ "There is no repeated field in a map entry message.");
+ }
+
+ @Override
+ public UnknownFieldSet getUnknownFields() {
+ return UnknownFieldSet.getDefaultInstance();
+ }
+
+ @Override
+ public Builder<K, V> clone() {
+ if (dataBuilder == null) {
+ return new Builder<K, V>(metadata, data);
+ } else {
+ return new Builder<K, V>(metadata, dataBuilder.build());
+ }
+ }
+ }
+}
diff --git a/java/src/main/java/com/google/protobuf/MapEntryLite.java b/java/src/main/java/com/google/protobuf/MapEntryLite.java
new file mode 100644
index 00000000..bcffa946
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/MapEntryLite.java
@@ -0,0 +1,331 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import java.io.IOException;
+
+/**
+ * Implements the lite version of map entry messages.
+ *
+ * This class serves as an utility class to help do serialization/parsing of
+ * map entries. It's used in generated code and also in the full version
+ * MapEntry message.
+ *
+ * Protobuf internal. Users shouldn't use.
+ */
+public class MapEntryLite<K, V> extends AbstractMessageLite {
+ private static class Metadata<K, V> {
+ public final MapEntryLite<K, V> defaultInstance;
+ public final WireFormat.FieldType keyType;
+ public final WireFormat.FieldType valueType;
+ public final Parser<MapEntryLite<K, V>> parser;
+ public Metadata(
+ MapEntryLite<K, V> defaultInstance,
+ WireFormat.FieldType keyType,
+ WireFormat.FieldType valueType) {
+ this.defaultInstance = defaultInstance;
+ this.keyType = keyType;
+ this.valueType = valueType;
+ final Metadata<K, V> finalThis = this;
+ this.parser = new AbstractParser<MapEntryLite<K, V>>() {
+ @Override
+ public MapEntryLite<K, V> parsePartialFrom(
+ CodedInputStream input, ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException {
+ return new MapEntryLite<K, V>(finalThis, input, extensionRegistry);
+ }
+ };
+ }
+ }
+
+ private static final int KEY_FIELD_NUMBER = 1;
+ private static final int VALUE_FIELD_NUMBER = 2;
+
+ private final Metadata<K, V> metadata;
+ private final K key;
+ private final V value;
+
+ /** Creates a default MapEntryLite message instance. */
+ private MapEntryLite(
+ WireFormat.FieldType keyType, K defaultKey,
+ WireFormat.FieldType valueType, V defaultValue) {
+ this.metadata = new Metadata<K, V>(this, keyType, valueType);
+ this.key = defaultKey;
+ this.value = defaultValue;
+ }
+
+ /** Creates a new MapEntryLite message. */
+ private MapEntryLite(Metadata<K, V> metadata, K key, V value) {
+ this.metadata = metadata;
+ this.key = key;
+ this.value = value;
+ }
+
+ public K getKey() {
+ return key;
+ }
+
+ public V getValue() {
+ return value;
+ }
+
+ /**
+ * Creates a default MapEntryLite message instance.
+ *
+ * This method is used by generated code to create the default instance for
+ * a map entry message. The created default instance should be used to create
+ * new map entry messages of the same type. For each map entry message, only
+ * one default instance should be created.
+ */
+ public static <K, V> MapEntryLite<K, V> newDefaultInstance(
+ WireFormat.FieldType keyType, K defaultKey,
+ WireFormat.FieldType valueType, V defaultValue) {
+ return new MapEntryLite<K, V>(
+ keyType, defaultKey, valueType, defaultValue);
+ }
+
+ @Override
+ public void writeTo(CodedOutputStream output) throws IOException {
+ writeField(KEY_FIELD_NUMBER, metadata.keyType, key, output);
+ writeField(VALUE_FIELD_NUMBER, metadata.valueType, value, output);
+ }
+
+ private void writeField(
+ int number, WireFormat.FieldType type, Object value,
+ CodedOutputStream output) throws IOException {
+ output.writeTag(number, type.getWireType());
+ FieldSet.writeElementNoTag(output, type, value);
+ }
+
+ private volatile int cachedSerializedSize = -1;
+ @Override
+ public int getSerializedSize() {
+ if (cachedSerializedSize != -1) {
+ return cachedSerializedSize;
+ }
+ int size = 0;
+ size += getFieldSize(KEY_FIELD_NUMBER, metadata.keyType, key);
+ size += getFieldSize(VALUE_FIELD_NUMBER, metadata.valueType, value);
+ cachedSerializedSize = size;
+ return size;
+ }
+
+ private int getFieldSize(
+ int number, WireFormat.FieldType type, Object value) {
+ return CodedOutputStream.computeTagSize(number)
+ + FieldSet.computeElementSizeNoTag(type, value);
+ }
+
+ /** Parsing constructor. */
+ private MapEntryLite(
+ Metadata<K, V> metadata,
+ CodedInputStream input,
+ ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException {
+ try {
+ K key = metadata.defaultInstance.key;
+ V value = metadata.defaultInstance.value;
+ while (true) {
+ int tag = input.readTag();
+ if (tag == 0) {
+ break;
+ }
+ if (tag == WireFormat.makeTag(
+ KEY_FIELD_NUMBER, metadata.keyType.getWireType())) {
+ key = mergeField(
+ input, extensionRegistry, metadata.keyType, key);
+ } else if (tag == WireFormat.makeTag(
+ VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) {
+ value = mergeField(
+ input, extensionRegistry, metadata.valueType, value);
+ } else {
+ if (!input.skipField(tag)) {
+ break;
+ }
+ }
+ }
+ this.metadata = metadata;
+ this.key = key;
+ this.value = value;
+ } catch (InvalidProtocolBufferException e) {
+ throw e.setUnfinishedMessage(this);
+ } catch (IOException e) {
+ throw new InvalidProtocolBufferException(e.getMessage())
+ .setUnfinishedMessage(this);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T> T mergeField(
+ CodedInputStream input, ExtensionRegistryLite extensionRegistry,
+ WireFormat.FieldType type, T value) throws IOException {
+ switch (type) {
+ case MESSAGE:
+ MessageLite.Builder subBuilder = ((MessageLite) value).toBuilder();
+ input.readMessage(subBuilder, extensionRegistry);
+ return (T) subBuilder.buildPartial();
+ case ENUM:
+ return (T) (java.lang.Integer) input.readEnum();
+ case GROUP:
+ throw new RuntimeException("Groups are not allowed in maps.");
+ default:
+ return (T) FieldSet.readPrimitiveField(input, type, true);
+ }
+ }
+
+ @Override
+ public Parser<MapEntryLite<K, V>> getParserForType() {
+ return metadata.parser;
+ }
+
+ @Override
+ public Builder<K, V> newBuilderForType() {
+ return new Builder<K, V>(metadata);
+ }
+
+ @Override
+ public Builder<K, V> toBuilder() {
+ return new Builder<K, V>(metadata, key, value);
+ }
+
+ @Override
+ public MapEntryLite<K, V> getDefaultInstanceForType() {
+ return metadata.defaultInstance;
+ }
+
+ @Override
+ public boolean isInitialized() {
+ if (metadata.valueType.getJavaType() == WireFormat.JavaType.MESSAGE) {
+ return ((MessageLite) value).isInitialized();
+ }
+ return true;
+ }
+
+ /**
+ * Builder used to create {@link MapEntryLite} messages.
+ */
+ public static class Builder<K, V>
+ extends AbstractMessageLite.Builder<Builder<K, V>> {
+ private final Metadata<K, V> metadata;
+ private K key;
+ private V value;
+
+ private Builder(Metadata<K, V> metadata) {
+ this.metadata = metadata;
+ this.key = metadata.defaultInstance.key;
+ this.value = metadata.defaultInstance.value;
+ }
+
+ public K getKey() {
+ return key;
+ }
+
+ public V getValue() {
+ return value;
+ }
+
+ public Builder<K, V> setKey(K key) {
+ this.key = key;
+ return this;
+ }
+
+ public Builder<K, V> setValue(V value) {
+ this.value = value;
+ return this;
+ }
+
+ public Builder<K, V> clearKey() {
+ this.key = metadata.defaultInstance.key;
+ return this;
+ }
+
+ public Builder<K, V> clearValue() {
+ this.value = metadata.defaultInstance.value;
+ return this;
+ }
+
+ @Override
+ public Builder<K, V> clear() {
+ this.key = metadata.defaultInstance.key;
+ this.value = metadata.defaultInstance.value;
+ return this;
+ }
+
+ @Override
+ public MapEntryLite<K, V> build() {
+ MapEntryLite<K, V> result = buildPartial();
+ if (!result.isInitialized()) {
+ throw newUninitializedMessageException(result);
+ }
+ return result;
+ }
+
+ @Override
+ public MapEntryLite<K, V> buildPartial() {
+ return new MapEntryLite<K, V>(metadata, key, value);
+ }
+
+ @Override
+ public MessageLite getDefaultInstanceForType() {
+ return metadata.defaultInstance;
+ }
+
+ @Override
+ public boolean isInitialized() {
+ if (metadata.valueType.getJavaType() == WireFormat.JavaType.MESSAGE) {
+ return ((MessageLite) value).isInitialized();
+ }
+ return true;
+ }
+
+ private Builder(Metadata<K, V> metadata, K key, V value) {
+ this.metadata = metadata;
+ this.key = key;
+ this.value = value;
+ }
+
+ @Override
+ public Builder<K, V> clone() {
+ return new Builder<K, V>(metadata, key, value);
+ }
+
+ @Override
+ public Builder<K, V> mergeFrom(
+ CodedInputStream input, ExtensionRegistryLite extensionRegistry)
+ throws IOException {
+ MapEntryLite<K, V> entry =
+ new MapEntryLite<K, V>(metadata, input, extensionRegistry);
+ this.key = entry.key;
+ this.value = entry.value;
+ return this;
+ }
+ }
+}
diff --git a/java/src/main/java/com/google/protobuf/MapField.java b/java/src/main/java/com/google/protobuf/MapField.java
new file mode 100644
index 00000000..5bd70dbb
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/MapField.java
@@ -0,0 +1,259 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Internal representation of map fields in generated messages.
+ *
+ * This class supports accessing the map field as a {@link Map} to be used in
+ * generated API and also supports accessing the field as a {@link List} to be
+ * used in reflection API. It keeps track of where the data is currently stored
+ * and do necessary conversions between map and list.
+ *
+ * This class is a protobuf implementation detail. Users shouldn't use this
+ * class directly.
+ *
+ * THREAD-SAFETY NOTE: Read-only access is thread-safe. Users can call getMap()
+ * and getList() concurrently in multiple threads. If write-access is needed,
+ * all access must be synchronized.
+ */
+public class MapField<K, V> {
+ /**
+ * Indicates where the data of this map field is currently stored.
+ *
+ * MAP: Data is stored in mapData.
+ * LIST: Data is stored in listData.
+ * BOTH: mapData and listData have the same data.
+ *
+ * When the map field is accessed (through generated API or reflection API),
+ * it will shift between these 3 modes:
+ *
+ * getMap() getList() getMutableMap() getMutableList()
+ * MAP MAP BOTH MAP LIST
+ * LIST BOTH LIST MAP LIST
+ * BOTH BOTH BOTH MAP LIST
+ *
+ * As the map field changes its mode, the list/map reference returned in a
+ * previous method call may be invalidated.
+ */
+ private enum StorageMode {MAP, LIST, BOTH}
+
+ private volatile StorageMode mode;
+ private Map<K, V> mapData;
+ private List<Message> listData;
+
+ // Convert between a map entry Message and a key-value pair.
+ private static interface Converter<K, V> {
+ Message convertKeyAndValueToMessage(K key, V value);
+ void convertMessageToKeyAndValue(Message message, Map<K, V> map);
+
+ Message getMessageDefaultInstance();
+ }
+
+ private static class ImmutableMessageConverter<K, V> implements Converter<K, V> {
+ private final MapEntry<K, V> defaultEntry;
+ public ImmutableMessageConverter(MapEntry<K, V> defaultEntry) {
+ this.defaultEntry = defaultEntry;
+ }
+
+ public Message convertKeyAndValueToMessage(K key, V value) {
+ return defaultEntry.newBuilderForType().setKey(key).setValue(value).build();
+ }
+
+ public void convertMessageToKeyAndValue(Message message, Map<K, V> map) {
+ MapEntry<K, V> entry = (MapEntry<K, V>) message;
+ map.put(entry.getKey(), entry.getValue());
+ }
+
+ public Message getMessageDefaultInstance() {
+ return defaultEntry;
+ }
+ }
+
+
+ private final Converter<K, V> converter;
+
+ private MapField(
+ Converter<K, V> converter,
+ StorageMode mode,
+ Map<K, V> mapData,
+ List<Message> listData) {
+ this.converter = converter;
+ this.mode = mode;
+ this.mapData = mapData;
+ this.listData = listData;
+ }
+
+ private MapField(
+ MapEntry<K, V> defaultEntry,
+ StorageMode mode,
+ Map<K, V> mapData,
+ List<Message> listData) {
+ this(new ImmutableMessageConverter<K, V>(defaultEntry), mode, mapData, listData);
+ }
+
+
+ /** Returns an immutable empty MapField. */
+ public static <K, V> MapField<K, V> emptyMapField(
+ MapEntry<K, V> defaultEntry) {
+ return new MapField<K, V>(
+ defaultEntry, StorageMode.MAP, Collections.<K, V>emptyMap(), null);
+ }
+
+
+ /** Creates a new mutable empty MapField. */
+ public static <K, V> MapField<K, V> newMapField(MapEntry<K, V> defaultEntry) {
+ return new MapField<K, V>(
+ defaultEntry, StorageMode.MAP, new HashMap<K, V>(), null);
+ }
+
+
+ private Message convertKeyAndValueToMessage(K key, V value) {
+ return converter.convertKeyAndValueToMessage(key, value);
+ }
+
+ @SuppressWarnings("unchecked")
+ private void convertMessageToKeyAndValue(Message message, Map<K, V> map) {
+ converter.convertMessageToKeyAndValue(message, map);
+ }
+
+ private List<Message> convertMapToList(Map<K, V> mapData) {
+ List<Message> listData = new ArrayList<Message>();
+ for (Map.Entry<K, V> entry : mapData.entrySet()) {
+ listData.add(
+ convertKeyAndValueToMessage(
+ entry.getKey(), entry.getValue()));
+ }
+ return listData;
+ }
+
+ private Map<K, V> convertListToMap(List<Message> listData) {
+ Map<K, V> mapData = new HashMap<K, V>();
+ for (Message item : listData) {
+ convertMessageToKeyAndValue(item, mapData);
+ }
+ return mapData;
+ }
+
+ /** Returns the content of this MapField as a read-only Map. */
+ public Map<K, V> getMap() {
+ if (mode == StorageMode.LIST) {
+ synchronized (this) {
+ if (mode == StorageMode.LIST) {
+ mapData = convertListToMap(listData);
+ mode = StorageMode.BOTH;
+ }
+ }
+ }
+ return Collections.unmodifiableMap(mapData);
+ }
+
+ /** Gets a mutable Map view of this MapField. */
+ public Map<K, V> getMutableMap() {
+ if (mode != StorageMode.MAP) {
+ if (mode == StorageMode.LIST) {
+ mapData = convertListToMap(listData);
+ }
+ listData = null;
+ mode = StorageMode.MAP;
+ }
+ return mapData;
+ }
+
+ public void mergeFrom(MapField<K, V> other) {
+ getMutableMap().putAll(MapFieldLite.copy(other.getMap()));
+ }
+
+ public void clear() {
+ mapData = new HashMap<K, V>();
+ mode = StorageMode.MAP;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof MapField)) {
+ return false;
+ }
+ MapField<K, V> other = (MapField<K, V>) object;
+ return MapFieldLite.<K, V>equals(getMap(), other.getMap());
+ }
+
+ @Override
+ public int hashCode() {
+ return MapFieldLite.<K, V>calculateHashCodeForMap(getMap());
+ }
+
+ /** Returns a deep copy of this MapField. */
+ public MapField<K, V> copy() {
+ return new MapField<K, V>(
+ converter, StorageMode.MAP, MapFieldLite.copy(getMap()), null);
+ }
+
+ /** Gets the content of this MapField as a read-only List. */
+ List<Message> getList() {
+ if (mode == StorageMode.MAP) {
+ synchronized (this) {
+ if (mode == StorageMode.MAP) {
+ listData = convertMapToList(mapData);
+ mode = StorageMode.BOTH;
+ }
+ }
+ }
+ return Collections.unmodifiableList(listData);
+ }
+
+ /** Gets a mutable List view of this MapField. */
+ List<Message> getMutableList() {
+ if (mode != StorageMode.LIST) {
+ if (mode == StorageMode.MAP) {
+ listData = convertMapToList(mapData);
+ }
+ mapData = null;
+ mode = StorageMode.LIST;
+ }
+ return listData;
+ }
+
+ /**
+ * Gets the default instance of the message stored in the list view of this
+ * map field.
+ */
+ Message getMapEntryMessageDefaultInstance() {
+ return converter.getMessageDefaultInstance();
+ }
+}
diff --git a/java/src/main/java/com/google/protobuf/MapFieldLite.java b/java/src/main/java/com/google/protobuf/MapFieldLite.java
new file mode 100644
index 00000000..eea36d9e
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/MapFieldLite.java
@@ -0,0 +1,182 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Internal representation of map fields in generated lite-runtime messages.
+ *
+ * This class is a protobuf implementation detail. Users shouldn't use this
+ * class directly.
+ */
+public class MapFieldLite<K, V> {
+ private Map<K, V> mapData;
+
+ private MapFieldLite(Map<K, V> mapData) {
+ this.mapData = mapData;
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ private static final MapFieldLite EMPTY_MAP_FIELD =
+ new MapFieldLite(Collections.emptyMap());
+
+ /** Returns an singleton immutable empty MapFieldLite instance. */
+ @SuppressWarnings({"unchecked", "cast"})
+ public static <K, V> MapFieldLite<K, V> emptyMapField() {
+ return (MapFieldLite<K, V>) EMPTY_MAP_FIELD;
+ }
+
+ /** Creates a new MapFieldLite instance. */
+ public static <K, V> MapFieldLite<K, V> newMapField() {
+ return new MapFieldLite<K, V>(new HashMap<K, V>());
+ }
+
+ /** Gets the content of this MapField as a read-only Map. */
+ public Map<K, V> getMap() {
+ return Collections.unmodifiableMap(mapData);
+ }
+
+ /** Gets a mutable Map view of this MapField. */
+ public Map<K, V> getMutableMap() {
+ return mapData;
+ }
+
+ public void mergeFrom(MapFieldLite<K, V> other) {
+ mapData.putAll(copy(other.mapData));
+ }
+
+ public void clear() {
+ mapData.clear();
+ }
+
+ private static boolean equals(Object a, Object b) {
+ if (a instanceof byte[] && b instanceof byte[]) {
+ return Arrays.equals((byte[]) a, (byte[]) b);
+ }
+ return a.equals(b);
+ }
+
+ /**
+ * Checks whether two {@link Map}s are equal. We don't use the default equals
+ * method of {@link Map} because it compares by identity not by content for
+ * byte arrays.
+ */
+ static <K, V> boolean equals(Map<K, V> a, Map<K, V> b) {
+ if (a == b) {
+ return true;
+ }
+ if (a.size() != a.size()) {
+ return false;
+ }
+ for (Map.Entry<K, V> entry : a.entrySet()) {
+ if (!b.containsKey(entry.getKey())) {
+ return false;
+ }
+ if (!equals(entry.getValue(), b.get(entry.getKey()))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Checks whether two map fields are equal.
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof MapFieldLite)) {
+ return false;
+ }
+ MapFieldLite<K, V> other = (MapFieldLite<K, V>) object;
+ return equals(mapData, other.mapData);
+ }
+
+ private static int calculateHashCodeForObject(Object a) {
+ if (a instanceof byte[]) {
+ return LiteralByteString.hashCode((byte[]) a);
+ }
+ if (a instanceof Internal.EnumLite) {
+ return Internal.hashEnum((Internal.EnumLite) a);
+ }
+ return a.hashCode();
+ }
+
+ /**
+ * Calculates the hash code for a {@link Map}. We don't use the default hash
+ * code method of {@link Map} because for byte arrays and protobuf enums it
+ * use {@link Object#hashCode()}.
+ */
+ static <K, V> int calculateHashCodeForMap(Map<K, V> a) {
+ int result = 0;
+ for (Map.Entry<K, V> entry : a.entrySet()) {
+ result += calculateHashCodeForObject(entry.getKey())
+ ^ calculateHashCodeForObject(entry.getValue());
+ }
+ return result;
+ }
+
+ @Override
+ public int hashCode() {
+ return calculateHashCodeForMap(mapData);
+ }
+
+ private static Object copy(Object object) {
+ if (object instanceof byte[]) {
+ byte[] data = (byte[]) object;
+ return Arrays.copyOf(data, data.length);
+ }
+ return object;
+ }
+
+ /**
+ * Makes a deep copy of a {@link Map}. Immutable objects in the map will be
+ * shared (e.g., integers, strings, immutable messages) and mutable ones will
+ * have a copy (e.g., byte arrays, mutable messages).
+ */
+ @SuppressWarnings("unchecked")
+ static <K, V> Map<K, V> copy(Map<K, V> map) {
+ Map<K, V> result = new HashMap<K, V>();
+ for (Map.Entry<K, V> entry : map.entrySet()) {
+ result.put(entry.getKey(), (V) copy(entry.getValue()));
+ }
+ return result;
+ }
+
+ /** Returns a deep copy of this map field. */
+ public MapFieldLite<K, V> copy() {
+ return new MapFieldLite<K, V>(copy(mapData));
+ }
+}
diff --git a/java/src/main/java/com/google/protobuf/Message.java b/java/src/main/java/com/google/protobuf/Message.java
index 5673d3bb..5c75b58b 100644
--- a/java/src/main/java/com/google/protobuf/Message.java
+++ b/java/src/main/java/com/google/protobuf/Message.java
@@ -168,6 +168,25 @@ public interface Message extends MessageLite, MessageOrBuilder {
Builder getFieldBuilder(Descriptors.FieldDescriptor field);
/**
+ * Get a nested builder instance for the given repeated field instance.
+ * <p>
+ * Normally, we hold a reference to the immutable message object for the
+ * message type field. Some implementations(the generated message builders),
+ * however, can also hold a reference to the builder object (a nested
+ * builder) for the field.
+ * <p>
+ * If the field is already backed up by a nested builder, the nested builder
+ * will be returned. Otherwise, a new field builder will be created and
+ * returned. The original message field (if exist) will be merged into the
+ * field builder, which will then be nested into its parent builder.
+ * <p>
+ * NOTE: implementations that do not support nested builders will throw
+ * <code>UnsupportedException</code>.
+ */
+ Builder getRepeatedFieldBuilder(Descriptors.FieldDescriptor field,
+ int index);
+
+ /**
* Sets a field to the given value. The value must be of the correct type
* for this field, i.e. the same type that
* {@link Message#getField(Descriptors.FieldDescriptor)} would return.
diff --git a/java/src/main/java/com/google/protobuf/MessageReflection.java b/java/src/main/java/com/google/protobuf/MessageReflection.java
index edb5b5c2..06e3c99b 100644
--- a/java/src/main/java/com/google/protobuf/MessageReflection.java
+++ b/java/src/main/java/com/google/protobuf/MessageReflection.java
@@ -752,13 +752,18 @@ class MessageReflection {
if (field.getLiteType() == WireFormat.FieldType.ENUM) {
while (input.getBytesUntilLimit() > 0) {
final int rawValue = input.readEnum();
- final Object value = field.getEnumType().findValueByNumber(rawValue);
- if (value == null) {
- // If the number isn't recognized as a valid value for this
- // enum, drop it (don't even add it to unknownFields).
- return true;
+ if (field.getFile().supportsUnknownEnumValue()) {
+ target.addRepeatedField(field,
+ field.getEnumType().findValueByNumberCreatingIfUnknown(rawValue));
+ } else {
+ final Object value = field.getEnumType().findValueByNumber(rawValue);
+ if (value == null) {
+ // If the number isn't recognized as a valid value for this
+ // enum, drop it (don't even add it to unknownFields).
+ return true;
+ }
+ target.addRepeatedField(field, value);
}
- target.addRepeatedField(field, value);
}
} else {
while (input.getBytesUntilLimit() > 0) {
@@ -783,12 +788,16 @@ class MessageReflection {
}
case ENUM:
final int rawValue = input.readEnum();
- value = field.getEnumType().findValueByNumber(rawValue);
- // If the number isn't recognized as a valid value for this enum,
- // drop it.
- if (value == null) {
- unknownFields.mergeVarintField(fieldNumber, rawValue);
- return true;
+ if (field.getFile().supportsUnknownEnumValue()) {
+ value = field.getEnumType().findValueByNumberCreatingIfUnknown(rawValue);
+ } else {
+ value = field.getEnumType().findValueByNumber(rawValue);
+ // If the number isn't recognized as a valid value for this enum,
+ // drop it.
+ if (value == null) {
+ unknownFields.mergeVarintField(fieldNumber, rawValue);
+ return true;
+ }
}
break;
default:
diff --git a/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java b/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java
index 22760d3a..63535ac8 100644
--- a/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java
+++ b/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java
@@ -367,22 +367,28 @@ public class RepeatedFieldBuilder
throw new NullPointerException();
}
}
+
+ // If we can inspect the size, we can more efficiently add messages.
+ int size = -1;
if (values instanceof Collection) {
@SuppressWarnings("unchecked") final
Collection<MType> collection = (Collection<MType>) values;
if (collection.size() == 0) {
return this;
}
- ensureMutableMessageList();
- for (MType value : values) {
- addMessage(value);
- }
- } else {
- ensureMutableMessageList();
- for (MType value : values) {
- addMessage(value);
- }
+ size = collection.size();
+ }
+ ensureMutableMessageList();
+
+ if (size >= 0 && messages instanceof ArrayList) {
+ ((ArrayList<MType>) messages)
+ .ensureCapacity(messages.size() + size);
}
+
+ for (MType value : values) {
+ addMessage(value);
+ }
+
onChanged();
incrementModCounts();
return this;
diff --git a/java/src/main/java/com/google/protobuf/TextFormat.java b/java/src/main/java/com/google/protobuf/TextFormat.java
index 57d0ca68..44674811 100644
--- a/java/src/main/java/com/google/protobuf/TextFormat.java
+++ b/java/src/main/java/com/google/protobuf/TextFormat.java
@@ -1211,6 +1211,7 @@ public final class TextFormat {
private SingularOverwritePolicy singularOverwritePolicy =
SingularOverwritePolicy.ALLOW_SINGULAR_OVERWRITES;
+
/**
* Sets parser behavior when a non-repeated field appears more than once.
*/
@@ -1418,6 +1419,12 @@ public final class TextFormat {
} else {
consumeFieldValue(tokenizer, extensionRegistry, target, field, extension);
}
+
+ // For historical reasons, fields may optionally be separated by commas or
+ // semicolons.
+ if (!tokenizer.tryConsume(";")) {
+ tokenizer.tryConsume(",");
+ }
}
/**
@@ -1656,10 +1663,9 @@ public final class TextFormat {
case '\'': builder.append("\\\'"); break;
case '"' : builder.append("\\\""); break;
default:
- // Note: Bytes with the high-order bit set should be escaped. Since
- // bytes are signed, such bytes will compare less than 0x20, hence
- // the following line is correct.
- if (b >= 0x20) {
+ // Only ASCII characters between 0x20 (space) and 0x7e (tilde) are
+ // printable. Other byte values must be escaped.
+ if (b >= 0x20 && b <= 0x7e) {
builder.append((char) b);
} else {
builder.append('\\');
diff --git a/java/src/main/java/com/google/protobuf/UnknownFieldSet.java b/java/src/main/java/com/google/protobuf/UnknownFieldSet.java
index 99de3732..7cd2250e 100644
--- a/java/src/main/java/com/google/protobuf/UnknownFieldSet.java
+++ b/java/src/main/java/com/google/protobuf/UnknownFieldSet.java
@@ -431,6 +431,21 @@ public final class UnknownFieldSet implements MessageLite {
return this;
}
+
+ /**
+ * Convenience method for merging a length-delimited field.
+ *
+ * <p>For use by generated code only.
+ */
+ public Builder mergeLengthDelimitedField(
+ final int number, final ByteString value) {
+ if (number == 0) {
+ throw new IllegalArgumentException("Zero is not a valid field number.");
+ }
+ getFieldBuilder(number).addLengthDelimited(value);
+ return this;
+ }
+
/** Check if the given field number is present in the set. */
public boolean hasField(final int number) {
if (number == 0) {
diff --git a/java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java b/java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java
new file mode 100644
index 00000000..7ea84022
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java
@@ -0,0 +1,297 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import java.io.IOException;
+
+/**
+ * {@code UnknownFieldSetLite} is used to keep track of fields which were seen
+ * when parsing a protocol message but whose field numbers or types are
+ * unrecognized. This most frequently occurs when new fields are added to a
+ * message type and then messages containing those fields are read by old
+ * software that was compiled before the new types were added.
+ *
+ * <p>For use by generated code only.
+ *
+ * @author dweis@google.com (Daniel Weis)
+ */
+public final class UnknownFieldSetLite {
+
+ private static final UnknownFieldSetLite DEFAULT_INSTANCE =
+ new UnknownFieldSetLite(ByteString.EMPTY);
+
+ /**
+ * Get an empty {@code UnknownFieldSetLite}.
+ *
+ * <p>For use by generated code only.
+ */
+ public static UnknownFieldSetLite getDefaultInstance() {
+ return DEFAULT_INSTANCE;
+ }
+
+ /**
+ * Create a new {@link Builder}.
+ *
+ * <p>For use by generated code only.
+ */
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ /**
+ * Returns an {@code UnknownFieldSetLite} that is the composite of {@code first} and
+ * {@code second}.
+ */
+ static UnknownFieldSetLite concat(UnknownFieldSetLite first, UnknownFieldSetLite second) {
+ return new UnknownFieldSetLite(first.byteString.concat(second.byteString));
+ }
+
+ /**
+ * The internal representation of the unknown fields.
+ */
+ private final ByteString byteString;
+
+ /**
+ * Constructs the {@code UnknownFieldSetLite} as a thin wrapper around {@link ByteString}.
+ */
+ private UnknownFieldSetLite(ByteString byteString) {
+ this.byteString = byteString;
+ }
+
+ /**
+ * Serializes the set and writes it to {@code output}.
+ *
+ * <p>For use by generated code only.
+ */
+ public void writeTo(CodedOutputStream output) throws IOException {
+ output.writeRawBytes(byteString);
+ }
+
+
+ /**
+ * Get the number of bytes required to encode this set.
+ *
+ * <p>For use by generated code only.
+ */
+ public int getSerializedSize() {
+ return byteString.size();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof UnknownFieldSetLite) {
+ return byteString.equals(((UnknownFieldSetLite) obj).byteString);
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return byteString.hashCode();
+ }
+
+ /**
+ * Builder for {@link UnknownFieldSetLite}s.
+ *
+ * <p>Use {@link UnknownFieldSet#newBuilder()} to construct a {@code Builder}.
+ *
+ * <p>For use by generated code only.
+ */
+ public static final class Builder {
+
+ private ByteString.Output byteStringOutput;
+ private CodedOutputStream output;
+ private boolean built;
+
+ /**
+ * Constructs a {@code Builder}. Lazily initialized by
+ * {@link #ensureInitializedButNotBuilt()}.
+ */
+ private Builder() {}
+
+ /**
+ * Ensures internal state is initialized for use.
+ */
+ private void ensureInitializedButNotBuilt() {
+ if (built) {
+ throw new IllegalStateException("Do not reuse UnknownFieldSetLite Builders.");
+ }
+
+ if (output == null && byteStringOutput == null) {
+ byteStringOutput = ByteString.newOutput(100 /* initialCapacity */);
+ output = CodedOutputStream.newInstance(byteStringOutput);
+ }
+ }
+
+ /**
+ * Parse a single field from {@code input} and merge it into this set.
+ *
+ * <p>For use by generated code only.
+ *
+ * @param tag The field's tag number, which was already parsed.
+ * @return {@code false} if the tag is an end group tag.
+ */
+ public boolean mergeFieldFrom(final int tag, final CodedInputStream input)
+ throws IOException {
+ ensureInitializedButNotBuilt();
+
+ final int fieldNumber = WireFormat.getTagFieldNumber(tag);
+ switch (WireFormat.getTagWireType(tag)) {
+ case WireFormat.WIRETYPE_VARINT:
+ output.writeUInt64(fieldNumber, input.readInt64());
+ return true;
+ case WireFormat.WIRETYPE_FIXED32:
+ output.writeFixed32(fieldNumber, input.readFixed32());
+ return true;
+ case WireFormat.WIRETYPE_FIXED64:
+ output.writeFixed64(fieldNumber, input.readFixed64());
+ return true;
+ case WireFormat.WIRETYPE_LENGTH_DELIMITED:
+ output.writeBytes(fieldNumber, input.readBytes());
+ return true;
+ case WireFormat.WIRETYPE_START_GROUP:
+ final Builder subBuilder = newBuilder();
+ subBuilder.mergeFrom(input);
+ input.checkLastTagWas(
+ WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP));
+
+ output.writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP);
+ subBuilder.build().writeTo(output);
+ output.writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP);
+ return true;
+ case WireFormat.WIRETYPE_END_GROUP:
+ return false;
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+
+ /**
+ * Convenience method for merging a new field containing a single varint
+ * value. This is used in particular when an unknown enum value is
+ * encountered.
+ *
+ * <p>For use by generated code only.
+ */
+ public Builder mergeVarintField(int fieldNumber, int value) {
+ if (fieldNumber == 0) {
+ throw new IllegalArgumentException("Zero is not a valid field number.");
+ }
+ ensureInitializedButNotBuilt();
+ try {
+ output.writeUInt64(fieldNumber, value);
+ } catch (IOException e) {
+ // Should never happen.
+ }
+ return this;
+ }
+
+ /**
+ * Convenience method for merging a length-delimited field.
+ *
+ * <p>For use by generated code only.
+ */
+ public Builder mergeLengthDelimitedField(
+ final int fieldNumber, final ByteString value) {
+ if (fieldNumber == 0) {
+ throw new IllegalArgumentException("Zero is not a valid field number.");
+ }
+ ensureInitializedButNotBuilt();
+ try {
+ output.writeBytes(fieldNumber, value);
+ } catch (IOException e) {
+ // Should never happen.
+ }
+ return this;
+ }
+
+ /**
+ * Build the {@link UnknownFieldSetLite} and return it.
+ *
+ * <p>Once {@code build()} has been called, the {@code Builder} will no
+ * longer be usable. Calling any method after {@code build()} will result
+ * in undefined behavior and can cause a {@code IllegalStateException} to be
+ * thrown.
+ *
+ * <p>For use by generated code only.
+ */
+ public UnknownFieldSetLite build() {
+ if (built) {
+ throw new IllegalStateException("Do not reuse UnknownFieldSetLite Builders.");
+ }
+
+ built = true;
+
+ final UnknownFieldSetLite result;
+ // If we were never initialized, no data was written.
+ if (output == null) {
+ result = getDefaultInstance();
+ } else {
+ try {
+ output.flush();
+ } catch (IOException e) {
+ // Should never happen.
+ }
+ ByteString byteString = byteStringOutput.toByteString();
+ if (byteString.isEmpty()) {
+ result = getDefaultInstance();
+ } else {
+ result = new UnknownFieldSetLite(byteString);
+ }
+ }
+
+ // Allow for garbage collection.
+ output = null;
+ byteStringOutput = null;
+ return result;
+ }
+
+ /**
+ * Parse an entire message from {@code input} and merge its fields into
+ * this set.
+ */
+ private Builder mergeFrom(final CodedInputStream input) throws IOException {
+ // Ensures initialization in mergeFieldFrom.
+ while (true) {
+ final int tag = input.readTag();
+ if (tag == 0 || !mergeFieldFrom(tag, input)) {
+ break;
+ }
+ }
+ return this;
+ }
+ }
+}
diff --git a/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java b/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java
index 3f656e25..18d8142c 100644
--- a/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java
+++ b/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java
@@ -455,19 +455,19 @@ public class CodedInputStreamTest extends TestCase {
}
public void testMaliciousRecursion() throws Exception {
- ByteString data64 = makeRecursiveMessage(64).toByteString();
- ByteString data65 = makeRecursiveMessage(65).toByteString();
+ ByteString data100 = makeRecursiveMessage(100).toByteString();
+ ByteString data101 = makeRecursiveMessage(101).toByteString();
- assertMessageDepth(TestRecursiveMessage.parseFrom(data64), 64);
+ assertMessageDepth(TestRecursiveMessage.parseFrom(data100), 100);
try {
- TestRecursiveMessage.parseFrom(data65);
+ TestRecursiveMessage.parseFrom(data101);
fail("Should have thrown an exception!");
} catch (InvalidProtocolBufferException e) {
// success.
}
- CodedInputStream input = data64.newCodedInput();
+ CodedInputStream input = data100.newCodedInput();
input.setRecursionLimit(8);
try {
TestRecursiveMessage.parseFrom(input);
diff --git a/java/src/test/java/com/google/protobuf/DescriptorsTest.java b/java/src/test/java/com/google/protobuf/DescriptorsTest.java
index 0092771b..05832a12 100644
--- a/java/src/test/java/com/google/protobuf/DescriptorsTest.java
+++ b/java/src/test/java/com/google/protobuf/DescriptorsTest.java
@@ -706,6 +706,12 @@ public class DescriptorsTest extends TestCase {
assertTrue(TestMultipleExtensionRanges.getDescriptor().isExtensionNumber(4143));
}
+ public void testToString() {
+ assertEquals("protobuf_unittest.TestAllTypes.optional_uint64",
+ UnittestProto.TestAllTypes.getDescriptor().findFieldByNumber(
+ UnittestProto.TestAllTypes.OPTIONAL_UINT64_FIELD_NUMBER).toString());
+ }
+
public void testPackedEnumField() throws Exception {
FileDescriptorProto fileDescriptorProto = FileDescriptorProto.newBuilder()
.setName("foo.proto")
diff --git a/java/src/test/java/com/google/protobuf/FieldPresenceTest.java b/java/src/test/java/com/google/protobuf/FieldPresenceTest.java
new file mode 100644
index 00000000..acf2b023
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/FieldPresenceTest.java
@@ -0,0 +1,363 @@
+// 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.FieldDescriptor;
+import com.google.protobuf.FieldPresenceTestProto.TestAllTypes;
+import com.google.protobuf.FieldPresenceTestProto.TestOptionalFieldsOnly;
+import com.google.protobuf.FieldPresenceTestProto.TestRepeatedFieldsOnly;
+import protobuf_unittest.UnittestProto;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for protos that doesn't support field presence test for optional
+ * non-message fields.
+ */
+public class FieldPresenceTest extends TestCase {
+ private static boolean hasMethod(Class clazz, String name) {
+ try {
+ if (clazz.getMethod(name, new Class[]{}) != null) {
+ return true;
+ } else {
+ return false;
+ }
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ private static boolean isHasMethodRemoved(
+ Class classWithFieldPresence,
+ Class classWithoutFieldPresence,
+ String camelName) {
+ return hasMethod(classWithFieldPresence, "get" + camelName)
+ && hasMethod(classWithFieldPresence, "has" + camelName)
+ && hasMethod(classWithoutFieldPresence, "get" + camelName)
+ && !hasMethod(classWithoutFieldPresence, "has" + camelName);
+ }
+
+ public void testHasMethod() {
+ // Optional non-message fields don't have a hasFoo() method generated.
+ assertTrue(isHasMethodRemoved(
+ UnittestProto.TestAllTypes.class,
+ TestAllTypes.class,
+ "OptionalInt32"));
+ assertTrue(isHasMethodRemoved(
+ UnittestProto.TestAllTypes.class,
+ TestAllTypes.class,
+ "OptionalString"));
+ assertTrue(isHasMethodRemoved(
+ UnittestProto.TestAllTypes.class,
+ TestAllTypes.class,
+ "OptionalBytes"));
+ assertTrue(isHasMethodRemoved(
+ UnittestProto.TestAllTypes.class,
+ TestAllTypes.class,
+ "OptionalNestedEnum"));
+
+ assertTrue(isHasMethodRemoved(
+ UnittestProto.TestAllTypes.Builder.class,
+ TestAllTypes.Builder.class,
+ "OptionalInt32"));
+ assertTrue(isHasMethodRemoved(
+ UnittestProto.TestAllTypes.Builder.class,
+ TestAllTypes.Builder.class,
+ "OptionalString"));
+ assertTrue(isHasMethodRemoved(
+ UnittestProto.TestAllTypes.Builder.class,
+ TestAllTypes.Builder.class,
+ "OptionalBytes"));
+ assertTrue(isHasMethodRemoved(
+ UnittestProto.TestAllTypes.Builder.class,
+ TestAllTypes.Builder.class,
+ "OptionalNestedEnum"));
+
+ // message fields still have the hasFoo() method generated.
+ assertFalse(TestAllTypes.newBuilder().build().hasOptionalNestedMessage());
+ assertFalse(TestAllTypes.newBuilder().hasOptionalNestedMessage());
+
+ // oneof fields don't have hasFoo() methods (even for message types).
+ assertTrue(isHasMethodRemoved(
+ UnittestProto.TestAllTypes.class,
+ TestAllTypes.class,
+ "OneofUint32"));
+ assertTrue(isHasMethodRemoved(
+ UnittestProto.TestAllTypes.class,
+ TestAllTypes.class,
+ "OneofString"));
+ assertTrue(isHasMethodRemoved(
+ UnittestProto.TestAllTypes.class,
+ TestAllTypes.class,
+ "OneofBytes"));
+ assertTrue(isHasMethodRemoved(
+ UnittestProto.TestAllTypes.class,
+ TestAllTypes.class,
+ "OneofNestedMessage"));
+
+ assertTrue(isHasMethodRemoved(
+ UnittestProto.TestAllTypes.Builder.class,
+ TestAllTypes.Builder.class,
+ "OneofUint32"));
+ assertTrue(isHasMethodRemoved(
+ UnittestProto.TestAllTypes.Builder.class,
+ TestAllTypes.Builder.class,
+ "OneofString"));
+ assertTrue(isHasMethodRemoved(
+ UnittestProto.TestAllTypes.Builder.class,
+ TestAllTypes.Builder.class,
+ "OneofBytes"));
+ assertTrue(isHasMethodRemoved(
+ UnittestProto.TestAllTypes.Builder.class,
+ TestAllTypes.Builder.class,
+ "OneofNestedMessage"));
+ }
+
+ public void testFieldPresence() {
+ // Optional non-message fields set to their default value are treated the
+ // same way as not set.
+
+ // Serialization will ignore such fields.
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+ builder.setOptionalInt32(0);
+ builder.setOptionalString("");
+ builder.setOptionalBytes(ByteString.EMPTY);
+ builder.setOptionalNestedEnum(TestAllTypes.NestedEnum.FOO);
+ TestAllTypes message = builder.build();
+ assertEquals(0, message.getSerializedSize());
+
+ // mergeFrom() will ignore such fields.
+ TestAllTypes.Builder a = TestAllTypes.newBuilder();
+ a.setOptionalInt32(1);
+ a.setOptionalString("x");
+ a.setOptionalBytes(ByteString.copyFromUtf8("y"));
+ a.setOptionalNestedEnum(TestAllTypes.NestedEnum.BAR);
+ TestAllTypes.Builder b = TestAllTypes.newBuilder();
+ b.setOptionalInt32(0);
+ b.setOptionalString("");
+ b.setOptionalBytes(ByteString.EMPTY);
+ b.setOptionalNestedEnum(TestAllTypes.NestedEnum.FOO);
+ a.mergeFrom(b.build());
+ message = a.build();
+ assertEquals(1, message.getOptionalInt32());
+ assertEquals("x", message.getOptionalString());
+ assertEquals(ByteString.copyFromUtf8("y"), message.getOptionalBytes());
+ assertEquals(TestAllTypes.NestedEnum.BAR, message.getOptionalNestedEnum());
+
+ // equals()/hashCode() should produce the same results.
+ TestAllTypes empty = TestAllTypes.newBuilder().build();
+ message = builder.build();
+ assertTrue(empty.equals(message));
+ assertTrue(message.equals(empty));
+ assertEquals(empty.hashCode(), message.hashCode());
+ }
+
+ public void testFieldPresenceByReflection() {
+ Descriptor descriptor = TestAllTypes.getDescriptor();
+ FieldDescriptor optionalInt32Field = descriptor.findFieldByName("optional_int32");
+ FieldDescriptor optionalStringField = descriptor.findFieldByName("optional_string");
+ FieldDescriptor optionalBytesField = descriptor.findFieldByName("optional_bytes");
+ FieldDescriptor optionalNestedEnumField = descriptor.findFieldByName("optional_nested_enum");
+
+ // Field not present.
+ TestAllTypes message = TestAllTypes.newBuilder().build();
+ assertFalse(message.hasField(optionalInt32Field));
+ assertFalse(message.hasField(optionalStringField));
+ assertFalse(message.hasField(optionalBytesField));
+ assertFalse(message.hasField(optionalNestedEnumField));
+ assertEquals(0, message.getAllFields().size());
+
+ // Field set to default value is seen as not present.
+ message = TestAllTypes.newBuilder()
+ .setOptionalInt32(0)
+ .setOptionalString("")
+ .setOptionalBytes(ByteString.EMPTY)
+ .setOptionalNestedEnum(TestAllTypes.NestedEnum.FOO)
+ .build();
+ assertFalse(message.hasField(optionalInt32Field));
+ assertFalse(message.hasField(optionalStringField));
+ assertFalse(message.hasField(optionalBytesField));
+ assertFalse(message.hasField(optionalNestedEnumField));
+ assertEquals(0, message.getAllFields().size());
+
+ // Field set to non-default value is seen as present.
+ message = TestAllTypes.newBuilder()
+ .setOptionalInt32(1)
+ .setOptionalString("x")
+ .setOptionalBytes(ByteString.copyFromUtf8("y"))
+ .setOptionalNestedEnum(TestAllTypes.NestedEnum.BAR)
+ .build();
+ assertTrue(message.hasField(optionalInt32Field));
+ assertTrue(message.hasField(optionalStringField));
+ assertTrue(message.hasField(optionalBytesField));
+ assertTrue(message.hasField(optionalNestedEnumField));
+ assertEquals(4, message.getAllFields().size());
+ }
+
+ public void testMessageField() {
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+ assertFalse(builder.hasOptionalNestedMessage());
+ assertFalse(builder.build().hasOptionalNestedMessage());
+
+ TestAllTypes.NestedMessage.Builder nestedBuilder =
+ builder.getOptionalNestedMessageBuilder();
+ assertTrue(builder.hasOptionalNestedMessage());
+ assertTrue(builder.build().hasOptionalNestedMessage());
+
+ nestedBuilder.setValue(1);
+ assertEquals(1, builder.build().getOptionalNestedMessage().getValue());
+
+ builder.clearOptionalNestedMessage();
+ assertFalse(builder.hasOptionalNestedMessage());
+ assertFalse(builder.build().hasOptionalNestedMessage());
+
+ // Unlike non-message fields, if we set a message field to its default value (i.e.,
+ // default instance), the field should be seen as present.
+ builder.setOptionalNestedMessage(TestAllTypes.NestedMessage.getDefaultInstance());
+ assertTrue(builder.hasOptionalNestedMessage());
+ assertTrue(builder.build().hasOptionalNestedMessage());
+ }
+
+ public void testSerializeAndParse() throws Exception {
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+ builder.setOptionalInt32(1234);
+ builder.setOptionalString("hello");
+ builder.setOptionalNestedMessage(TestAllTypes.NestedMessage.getDefaultInstance());
+ // Set an oneof field to its default value and expect it to be serialized (i.e.,
+ // an oneof field set to the default value should be treated as present).
+ builder.setOneofInt32(0);
+ ByteString data = builder.build().toByteString();
+
+ TestAllTypes message = TestAllTypes.parseFrom(data);
+ assertEquals(1234, message.getOptionalInt32());
+ assertEquals("hello", message.getOptionalString());
+ // Fields not set will have the default value.
+ assertEquals(ByteString.EMPTY, message.getOptionalBytes());
+ assertEquals(TestAllTypes.NestedEnum.FOO, message.getOptionalNestedEnum());
+ // The message field is set despite that it's set with a default instance.
+ assertTrue(message.hasOptionalNestedMessage());
+ assertEquals(0, message.getOptionalNestedMessage().getValue());
+ // The oneof field set to its default value is also present.
+ assertEquals(
+ TestAllTypes.OneofFieldCase.ONEOF_INT32, message.getOneofFieldCase());
+ }
+
+ // Regression test for b/16173397
+ // Make sure we haven't screwed up the code generation for repeated fields.
+ public void testRepeatedFields() throws Exception {
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+ builder.setOptionalInt32(1234);
+ builder.setOptionalString("hello");
+ builder.setOptionalNestedMessage(TestAllTypes.NestedMessage.getDefaultInstance());
+ builder.addRepeatedInt32(4321);
+ builder.addRepeatedString("world");
+ builder.addRepeatedNestedMessage(TestAllTypes.NestedMessage.getDefaultInstance());
+ ByteString data = builder.build().toByteString();
+
+ TestOptionalFieldsOnly optionalOnlyMessage = TestOptionalFieldsOnly.parseFrom(data);
+ assertEquals(1234, optionalOnlyMessage.getOptionalInt32());
+ assertEquals("hello", optionalOnlyMessage.getOptionalString());
+ assertTrue(optionalOnlyMessage.hasOptionalNestedMessage());
+ assertEquals(0, optionalOnlyMessage.getOptionalNestedMessage().getValue());
+
+ TestRepeatedFieldsOnly repeatedOnlyMessage = TestRepeatedFieldsOnly.parseFrom(data);
+ assertEquals(1, repeatedOnlyMessage.getRepeatedInt32Count());
+ assertEquals(4321, repeatedOnlyMessage.getRepeatedInt32(0));
+ assertEquals(1, repeatedOnlyMessage.getRepeatedStringCount());
+ assertEquals("world", repeatedOnlyMessage.getRepeatedString(0));
+ assertEquals(1, repeatedOnlyMessage.getRepeatedNestedMessageCount());
+ assertEquals(0, repeatedOnlyMessage.getRepeatedNestedMessage(0).getValue());
+ }
+
+ public void testIsInitialized() throws Exception {
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+
+ // Test optional proto2 message fields.
+ UnittestProto.TestRequired.Builder proto2Builder =
+ builder.getOptionalProto2MessageBuilder();
+ assertFalse(builder.isInitialized());
+ assertFalse(builder.buildPartial().isInitialized());
+
+ proto2Builder.setA(1).setB(2).setC(3);
+ assertTrue(builder.isInitialized());
+ assertTrue(builder.buildPartial().isInitialized());
+
+ // Test oneof proto2 message fields.
+ proto2Builder = builder.getOneofProto2MessageBuilder();
+ assertFalse(builder.isInitialized());
+ assertFalse(builder.buildPartial().isInitialized());
+
+ proto2Builder.setA(1).setB(2).setC(3);
+ assertTrue(builder.isInitialized());
+ assertTrue(builder.buildPartial().isInitialized());
+
+ // Test repeated proto2 message fields.
+ proto2Builder = builder.addRepeatedProto2MessageBuilder();
+ assertFalse(builder.isInitialized());
+ assertFalse(builder.buildPartial().isInitialized());
+
+ proto2Builder.setA(1).setB(2).setC(3);
+ assertTrue(builder.isInitialized());
+ assertTrue(builder.buildPartial().isInitialized());
+ }
+
+
+ // Test that unknown fields are dropped.
+ public void testUnknownFields() throws Exception {
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+ builder.setOptionalInt32(1234);
+ builder.addRepeatedInt32(5678);
+ TestAllTypes message = builder.build();
+ ByteString data = message.toByteString();
+
+ TestOptionalFieldsOnly optionalOnlyMessage =
+ TestOptionalFieldsOnly.parseFrom(data);
+ // UnknownFieldSet should be empty.
+ assertEquals(
+ 0, optionalOnlyMessage.getUnknownFields().toByteString().size());
+ assertEquals(1234, optionalOnlyMessage.getOptionalInt32());
+ message = TestAllTypes.parseFrom(optionalOnlyMessage.toByteString());
+ assertEquals(1234, message.getOptionalInt32());
+ // The repeated field is discarded because it's unknown to the optional-only
+ // message.
+ assertEquals(0, message.getRepeatedInt32Count());
+
+ DynamicMessage dynamicOptionalOnlyMessage =
+ DynamicMessage.getDefaultInstance(
+ TestOptionalFieldsOnly.getDescriptor())
+ .getParserForType().parseFrom(data);
+ assertEquals(
+ 0, dynamicOptionalOnlyMessage.getUnknownFields().toByteString().size());
+ assertEquals(optionalOnlyMessage.toByteString(),
+ dynamicOptionalOnlyMessage.toByteString());
+ }
+}
diff --git a/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java b/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java
index 0b3482c3..41ed7bd0 100644
--- a/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java
+++ b/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java
@@ -157,15 +157,12 @@ public class GeneratedMessageTest extends TestCase {
public void testProtosShareRepeatedArraysIfDidntChange() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
builder.addRepeatedInt32(100);
- builder.addRepeatedImportEnum(UnittestImport.ImportEnum.IMPORT_BAR);
builder.addRepeatedForeignMessage(ForeignMessage.getDefaultInstance());
TestAllTypes value1 = builder.build();
TestAllTypes value2 = value1.toBuilder().build();
assertSame(value1.getRepeatedInt32List(), value2.getRepeatedInt32List());
- assertSame(value1.getRepeatedImportEnumList(),
- value2.getRepeatedImportEnumList());
assertSame(value1.getRepeatedForeignMessageList(),
value2.getRepeatedForeignMessageList());
}
@@ -1512,4 +1509,142 @@ public class GeneratedMessageTest extends TestCase {
assertEquals(message2.getFooMessage().getQuxInt(), 234);
}
}
+
+ public void testGetRepeatedFieldBuilder() {
+ Descriptor descriptor = TestAllTypes.getDescriptor();
+
+ FieldDescriptor fieldDescriptor =
+ descriptor.findFieldByName("repeated_nested_message");
+ FieldDescriptor foreignFieldDescriptor =
+ descriptor.findFieldByName("repeated_foreign_message");
+ FieldDescriptor importFieldDescriptor =
+ descriptor.findFieldByName("repeated_import_message");
+
+ // Mutate the message with new field builder
+ // Mutate nested message
+ TestAllTypes.Builder builder1 = TestAllTypes.newBuilder();
+ Message.Builder fieldBuilder1 = builder1.newBuilderForField(
+ fieldDescriptor);
+ FieldDescriptor subFieldDescriptor1 =
+ fieldBuilder1.getDescriptorForType().findFieldByName("bb");
+ fieldBuilder1.setField(subFieldDescriptor1, 1);
+ builder1.addRepeatedField(fieldDescriptor, fieldBuilder1.build());
+
+ // Mutate foreign message
+ Message.Builder foreignFieldBuilder1 = builder1.newBuilderForField(
+ foreignFieldDescriptor);
+ FieldDescriptor subForeignFieldDescriptor1 =
+ foreignFieldBuilder1.getDescriptorForType().findFieldByName("c");
+ foreignFieldBuilder1.setField(subForeignFieldDescriptor1, 2);
+ builder1.addRepeatedField(foreignFieldDescriptor,
+ foreignFieldBuilder1.build());
+
+ // Mutate import message
+ Message.Builder importFieldBuilder1 = builder1.newBuilderForField(
+ importFieldDescriptor);
+ FieldDescriptor subImportFieldDescriptor1 =
+ importFieldBuilder1.getDescriptorForType().findFieldByName("d");
+ importFieldBuilder1.setField(subImportFieldDescriptor1, 3);
+ builder1.addRepeatedField(importFieldDescriptor,
+ importFieldBuilder1.build());
+
+ Message newMessage1 = builder1.build();
+
+ // Mutate the message with existing field builder
+ // Mutate nested message
+ TestAllTypes.Builder builder2 = TestAllTypes.newBuilder();
+ builder2.addRepeatedNestedMessageBuilder();
+ Message.Builder fieldBuilder2 = builder2.getRepeatedFieldBuilder(
+ fieldDescriptor, 0);
+ FieldDescriptor subFieldDescriptor2 =
+ fieldBuilder2.getDescriptorForType().findFieldByName("bb");
+ fieldBuilder2.setField(subFieldDescriptor2, 1);
+
+ // Mutate foreign message
+ Message.Builder foreignFieldBuilder2 = builder2.newBuilderForField(
+ foreignFieldDescriptor);
+ FieldDescriptor subForeignFieldDescriptor2 =
+ foreignFieldBuilder2.getDescriptorForType().findFieldByName("c");
+ foreignFieldBuilder2.setField(subForeignFieldDescriptor2, 2);
+ builder2.addRepeatedField(foreignFieldDescriptor,
+ foreignFieldBuilder2.build());
+
+ // Mutate import message
+ Message.Builder importFieldBuilder2 = builder2.newBuilderForField(
+ importFieldDescriptor);
+ FieldDescriptor subImportFieldDescriptor2 =
+ importFieldBuilder2.getDescriptorForType().findFieldByName("d");
+ importFieldBuilder2.setField(subImportFieldDescriptor2, 3);
+ builder2.addRepeatedField(importFieldDescriptor,
+ importFieldBuilder2.build());
+
+ Message newMessage2 = builder2.build();
+
+ // These two messages should be equal.
+ assertEquals(newMessage1, newMessage2);
+ }
+
+ public void testGetRepeatedFieldBuilderWithInitializedValue() {
+ Descriptor descriptor = TestAllTypes.getDescriptor();
+ FieldDescriptor fieldDescriptor =
+ descriptor.findFieldByName("repeated_nested_message");
+
+ // Before setting field, builder is initialized by default value.
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+ builder.addRepeatedNestedMessageBuilder();
+ NestedMessage.Builder fieldBuilder =
+ (NestedMessage.Builder) builder.getRepeatedFieldBuilder(fieldDescriptor, 0);
+ assertEquals(0, fieldBuilder.getBb());
+
+ // Setting field value with new field builder instance.
+ builder = TestAllTypes.newBuilder();
+ NestedMessage.Builder newFieldBuilder =
+ builder.addRepeatedNestedMessageBuilder();
+ newFieldBuilder.setBb(2);
+ // Then get the field builder instance by getRepeatedFieldBuilder().
+ fieldBuilder =
+ (NestedMessage.Builder) builder.getRepeatedFieldBuilder(fieldDescriptor, 0);
+ // It should contain new value.
+ assertEquals(2, fieldBuilder.getBb());
+ // These two builder should be equal.
+ assertSame(fieldBuilder, newFieldBuilder);
+ }
+
+ public void testGetRepeatedFieldBuilderNotSupportedException() {
+ Descriptor descriptor = TestAllTypes.getDescriptor();
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+ try {
+ builder.getRepeatedFieldBuilder(descriptor.findFieldByName("repeated_int32"), 0);
+ fail("Exception was not thrown");
+ } catch (UnsupportedOperationException e) {
+ // We expect this exception.
+ }
+ try {
+ builder.getRepeatedFieldBuilder(
+ descriptor.findFieldByName("repeated_nested_enum"), 0);
+ fail("Exception was not thrown");
+ } catch (UnsupportedOperationException e) {
+ // We expect this exception.
+ }
+ try {
+ builder.getRepeatedFieldBuilder(descriptor.findFieldByName("optional_int32"), 0);
+ fail("Exception was not thrown");
+ } catch (UnsupportedOperationException e) {
+ // We expect this exception.
+ }
+ try {
+ builder.getRepeatedFieldBuilder(
+ descriptor.findFieldByName("optional_nested_enum"), 0);
+ fail("Exception was not thrown");
+ } catch (UnsupportedOperationException e) {
+ // We expect this exception.
+ }
+ try {
+ builder.getRepeatedFieldBuilder(
+ descriptor.findFieldByName("optional_nested_message"), 0);
+ fail("Exception was not thrown");
+ } catch (UnsupportedOperationException e) {
+ // We expect this exception.
+ }
+ }
}
diff --git a/java/src/test/java/com/google/protobuf/LazyMessageLiteTest.java b/java/src/test/java/com/google/protobuf/LazyMessageLiteTest.java
index 00e3a843..9de794fe 100644
--- a/java/src/test/java/com/google/protobuf/LazyMessageLiteTest.java
+++ b/java/src/test/java/com/google/protobuf/LazyMessageLiteTest.java
@@ -36,8 +36,6 @@ import protobuf_unittest.LazyFieldsLite.LazyNestedInnerMessageLite;
import junit.framework.TestCase;
-import org.easymock.classextension.EasyMock;
-
import java.util.ArrayList;
/**
@@ -52,14 +50,10 @@ public class LazyMessageLiteTest extends TestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
-
- originalLazyInnerMessageLiteParser = LazyInnerMessageLite.PARSER;
}
@Override
protected void tearDown() throws Exception {
- LazyInnerMessageLite.PARSER = originalLazyInnerMessageLiteParser;
-
super.tearDown();
}
@@ -291,29 +285,4 @@ public class LazyMessageLiteTest extends TestCase {
assertEquals(bytes, deserialized.toByteString());
}
-
- public void testLaziness() throws InvalidProtocolBufferException {
- LazyInnerMessageLite inner = LazyInnerMessageLite.newBuilder()
- .setNum(2)
- .build();
- LazyMessageLite outer = LazyMessageLite.newBuilder()
- .setNum(1)
- .setInner(inner)
- .setOneofInner(inner)
- .build();
- ByteString bytes = outer.toByteString();
-
-
- // The parser for inner / oneofInner message shouldn't be used if
- // getInner / getOneofInner is not called.
- LazyInnerMessageLite.PARSER = EasyMock.createStrictMock(Parser.class);
-
- EasyMock.replay(LazyInnerMessageLite.PARSER);
-
- LazyMessageLite deserialized = LazyMessageLite.parseFrom(bytes);
- assertEquals(1, deserialized.getNum());
- assertEquals(421, deserialized.getNumWithDefault());
-
- EasyMock.verify(LazyInnerMessageLite.PARSER);
- }
}
diff --git a/java/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java b/java/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java
index 94f4fcf5..035917c8 100644
--- a/java/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java
+++ b/java/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java
@@ -82,4 +82,27 @@ public class LiteEqualsAndHashTest extends TestCase {
BarPrime barPrime = BarPrime.newBuilder().setName("bar").build();
assertFalse(bar.equals(barPrime));
}
+
+ public void testEqualsAndHashCodeWithUnknownFields() throws InvalidProtocolBufferException {
+ Foo fooWithOnlyValue = Foo.newBuilder()
+ .setValue(1)
+ .build();
+
+ Foo fooWithValueAndExtension = fooWithOnlyValue.toBuilder()
+ .setValue(1)
+ .setExtension(Bar.fooExt, Bar.newBuilder()
+ .setName("name")
+ .build())
+ .build();
+
+ Foo fooWithValueAndUnknownFields = Foo.parseFrom(fooWithValueAndExtension.toByteArray());
+
+ assertEqualsAndHashCodeAreFalse(fooWithOnlyValue, fooWithValueAndUnknownFields);
+ assertEqualsAndHashCodeAreFalse(fooWithValueAndExtension, fooWithValueAndUnknownFields);
+ }
+
+ private void assertEqualsAndHashCodeAreFalse(Object o1, Object o2) {
+ assertFalse(o1.equals(o2));
+ assertFalse(o1.hashCode() == o2.hashCode());
+ }
}
diff --git a/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java b/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java
new file mode 100644
index 00000000..22dbeb76
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java
@@ -0,0 +1,277 @@
+// 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 map_lite_test.MapForProto2TestProto.TestMap;
+import map_lite_test.MapForProto2TestProto.TestMap.MessageValue;
+import map_lite_test.MapForProto2TestProto.TestUnknownEnumValue;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for map fields.
+ */
+public class MapForProto2LiteTest extends TestCase {
+ private void setMapValues(TestMap.Builder builder) {
+ builder.getMutableInt32ToInt32Field().put(1, 11);
+ builder.getMutableInt32ToInt32Field().put(2, 22);
+ builder.getMutableInt32ToInt32Field().put(3, 33);
+
+ builder.getMutableInt32ToStringField().put(1, "11");
+ builder.getMutableInt32ToStringField().put(2, "22");
+ builder.getMutableInt32ToStringField().put(3, "33");
+
+ builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("11"));
+ builder.getMutableInt32ToBytesField().put(2, TestUtil.toBytes("22"));
+ builder.getMutableInt32ToBytesField().put(3, TestUtil.toBytes("33"));
+
+ builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.FOO);
+ builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.BAR);
+ builder.getMutableInt32ToEnumField().put(3, TestMap.EnumValue.BAZ);
+
+ builder.getMutableInt32ToMessageField().put(
+ 1, MessageValue.newBuilder().setValue(11).build());
+ builder.getMutableInt32ToMessageField().put(
+ 2, MessageValue.newBuilder().setValue(22).build());
+ builder.getMutableInt32ToMessageField().put(
+ 3, MessageValue.newBuilder().setValue(33).build());
+
+ builder.getMutableStringToInt32Field().put("1", 11);
+ builder.getMutableStringToInt32Field().put("2", 22);
+ builder.getMutableStringToInt32Field().put("3", 33);
+ }
+
+ private void assertMapValuesSet(TestMap message) {
+ assertEquals(3, message.getInt32ToInt32Field().size());
+ assertEquals(11, message.getInt32ToInt32Field().get(1).intValue());
+ assertEquals(22, message.getInt32ToInt32Field().get(2).intValue());
+ assertEquals(33, message.getInt32ToInt32Field().get(3).intValue());
+
+ assertEquals(3, message.getInt32ToStringField().size());
+ assertEquals("11", message.getInt32ToStringField().get(1));
+ assertEquals("22", message.getInt32ToStringField().get(2));
+ assertEquals("33", message.getInt32ToStringField().get(3));
+
+ assertEquals(3, message.getInt32ToBytesField().size());
+ assertEquals(TestUtil.toBytes("11"), message.getInt32ToBytesField().get(1));
+ assertEquals(TestUtil.toBytes("22"), message.getInt32ToBytesField().get(2));
+ assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3));
+
+ assertEquals(3, message.getInt32ToEnumField().size());
+ assertEquals(TestMap.EnumValue.FOO, message.getInt32ToEnumField().get(1));
+ assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(2));
+ assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3));
+
+ assertEquals(3, message.getInt32ToMessageField().size());
+ assertEquals(11, message.getInt32ToMessageField().get(1).getValue());
+ assertEquals(22, message.getInt32ToMessageField().get(2).getValue());
+ assertEquals(33, message.getInt32ToMessageField().get(3).getValue());
+
+ assertEquals(3, message.getStringToInt32Field().size());
+ assertEquals(11, message.getStringToInt32Field().get("1").intValue());
+ assertEquals(22, message.getStringToInt32Field().get("2").intValue());
+ assertEquals(33, message.getStringToInt32Field().get("3").intValue());
+ }
+
+ private void updateMapValues(TestMap.Builder builder) {
+ builder.getMutableInt32ToInt32Field().put(1, 111);
+ builder.getMutableInt32ToInt32Field().remove(2);
+ builder.getMutableInt32ToInt32Field().put(4, 44);
+
+ builder.getMutableInt32ToStringField().put(1, "111");
+ builder.getMutableInt32ToStringField().remove(2);
+ builder.getMutableInt32ToStringField().put(4, "44");
+
+ builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("111"));
+ builder.getMutableInt32ToBytesField().remove(2);
+ builder.getMutableInt32ToBytesField().put(4, TestUtil.toBytes("44"));
+
+ builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.BAR);
+ builder.getMutableInt32ToEnumField().remove(2);
+ builder.getMutableInt32ToEnumField().put(4, TestMap.EnumValue.QUX);
+
+ builder.getMutableInt32ToMessageField().put(
+ 1, MessageValue.newBuilder().setValue(111).build());
+ builder.getMutableInt32ToMessageField().remove(2);
+ builder.getMutableInt32ToMessageField().put(
+ 4, MessageValue.newBuilder().setValue(44).build());
+
+ builder.getMutableStringToInt32Field().put("1", 111);
+ builder.getMutableStringToInt32Field().remove("2");
+ builder.getMutableStringToInt32Field().put("4", 44);
+ }
+
+ private void assertMapValuesUpdated(TestMap message) {
+ assertEquals(3, message.getInt32ToInt32Field().size());
+ assertEquals(111, message.getInt32ToInt32Field().get(1).intValue());
+ assertEquals(33, message.getInt32ToInt32Field().get(3).intValue());
+ assertEquals(44, message.getInt32ToInt32Field().get(4).intValue());
+
+ assertEquals(3, message.getInt32ToStringField().size());
+ assertEquals("111", message.getInt32ToStringField().get(1));
+ assertEquals("33", message.getInt32ToStringField().get(3));
+ assertEquals("44", message.getInt32ToStringField().get(4));
+
+ assertEquals(3, message.getInt32ToBytesField().size());
+ assertEquals(TestUtil.toBytes("111"), message.getInt32ToBytesField().get(1));
+ assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3));
+ assertEquals(TestUtil.toBytes("44"), message.getInt32ToBytesField().get(4));
+
+ assertEquals(3, message.getInt32ToEnumField().size());
+ assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(1));
+ assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3));
+ assertEquals(TestMap.EnumValue.QUX, message.getInt32ToEnumField().get(4));
+
+ assertEquals(3, message.getInt32ToMessageField().size());
+ assertEquals(111, message.getInt32ToMessageField().get(1).getValue());
+ assertEquals(33, message.getInt32ToMessageField().get(3).getValue());
+ assertEquals(44, message.getInt32ToMessageField().get(4).getValue());
+
+ assertEquals(3, message.getStringToInt32Field().size());
+ assertEquals(111, message.getStringToInt32Field().get("1").intValue());
+ assertEquals(33, message.getStringToInt32Field().get("3").intValue());
+ assertEquals(44, message.getStringToInt32Field().get("4").intValue());
+ }
+
+ private void assertMapValuesCleared(TestMap message) {
+ assertEquals(0, message.getInt32ToInt32Field().size());
+ assertEquals(0, message.getInt32ToStringField().size());
+ assertEquals(0, message.getInt32ToBytesField().size());
+ assertEquals(0, message.getInt32ToEnumField().size());
+ assertEquals(0, message.getInt32ToMessageField().size());
+ assertEquals(0, message.getStringToInt32Field().size());
+ }
+
+ public void testGettersAndSetters() throws Exception {
+ TestMap.Builder builder = TestMap.newBuilder();
+ TestMap message = builder.build();
+ assertMapValuesCleared(message);
+
+ builder = message.toBuilder();
+ setMapValues(builder);
+ message = builder.build();
+ assertMapValuesSet(message);
+
+ builder = message.toBuilder();
+ updateMapValues(builder);
+ message = builder.build();
+ assertMapValuesUpdated(message);
+
+ builder = message.toBuilder();
+ builder.clear();
+ message = builder.build();
+ assertMapValuesCleared(message);
+ }
+
+ public void testSerializeAndParse() throws Exception {
+ TestMap.Builder builder = TestMap.newBuilder();
+ setMapValues(builder);
+ TestMap message = builder.build();
+ assertEquals(message.getSerializedSize(), message.toByteString().size());
+ message = TestMap.PARSER.parseFrom(message.toByteString());
+ assertMapValuesSet(message);
+
+ builder = message.toBuilder();
+ updateMapValues(builder);
+ message = builder.build();
+ assertEquals(message.getSerializedSize(), message.toByteString().size());
+ message = TestMap.PARSER.parseFrom(message.toByteString());
+ assertMapValuesUpdated(message);
+
+ builder = message.toBuilder();
+ builder.clear();
+ message = builder.build();
+ assertEquals(message.getSerializedSize(), message.toByteString().size());
+ message = TestMap.PARSER.parseFrom(message.toByteString());
+ assertMapValuesCleared(message);
+ }
+
+ public void testMergeFrom() throws Exception {
+ TestMap.Builder builder = TestMap.newBuilder();
+ setMapValues(builder);
+ TestMap message = builder.build();
+
+ TestMap.Builder other = TestMap.newBuilder();
+ other.mergeFrom(message);
+ assertMapValuesSet(other.build());
+ }
+
+ public void testEqualsAndHashCode() throws Exception {
+ // Test that generated equals() and hashCode() will disregard the order
+ // of map entries when comparing/hashing map fields.
+
+ // We can't control the order of elements in a HashMap. The best we can do
+ // here is to add elements in different order.
+ TestMap.Builder b1 = TestMap.newBuilder();
+ b1.getMutableInt32ToInt32Field().put(1, 2);
+ b1.getMutableInt32ToInt32Field().put(3, 4);
+ b1.getMutableInt32ToInt32Field().put(5, 6);
+ TestMap m1 = b1.build();
+
+ TestMap.Builder b2 = TestMap.newBuilder();
+ b2.getMutableInt32ToInt32Field().put(5, 6);
+ b2.getMutableInt32ToInt32Field().put(1, 2);
+ b2.getMutableInt32ToInt32Field().put(3, 4);
+ TestMap m2 = b2.build();
+
+ assertEquals(m1, m2);
+ assertEquals(m1.hashCode(), m2.hashCode());
+
+ // Make sure we did compare map fields.
+ b2.getMutableInt32ToInt32Field().put(1, 0);
+ m2 = b2.build();
+ assertFalse(m1.equals(m2));
+ // Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed
+ // to be different.
+ }
+
+ public void testUnknownEnumValues() throws Exception {
+ TestUnknownEnumValue.Builder builder =
+ TestUnknownEnumValue.newBuilder();
+ builder.getMutableInt32ToInt32Field().put(1, 1);
+ builder.getMutableInt32ToInt32Field().put(2, 54321);
+ ByteString data = builder.build().toByteString();
+
+ TestMap message = TestMap.parseFrom(data);
+ // Entries with unknown enum values will be stored into UnknownFieldSet so
+ // there is only one entry in the map.
+ assertEquals(1, message.getInt32ToEnumField().size());
+ assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(1));
+ // Serializing and parsing should preserve the unknown entry.
+ data = message.toByteString();
+ TestUnknownEnumValue messageWithUnknownEnums =
+ TestUnknownEnumValue.parseFrom(data);
+ assertEquals(2, messageWithUnknownEnums.getInt32ToInt32Field().size());
+ assertEquals(1, messageWithUnknownEnums.getInt32ToInt32Field().get(1).intValue());
+ assertEquals(54321, messageWithUnknownEnums.getInt32ToInt32Field().get(2).intValue());
+ }
+
+}
diff --git a/java/src/test/java/com/google/protobuf/MapForProto2Test.java b/java/src/test/java/com/google/protobuf/MapForProto2Test.java
new file mode 100644
index 00000000..d78c0d94
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/MapForProto2Test.java
@@ -0,0 +1,488 @@
+// 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.FieldDescriptor;
+import map_test.MapForProto2TestProto.TestMap;
+import map_test.MapForProto2TestProto.TestMap.MessageValue;
+import map_test.MapForProto2TestProto.TestUnknownEnumValue;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Unit tests for map fields in proto2 protos.
+ */
+public class MapForProto2Test extends TestCase {
+ private void setMapValues(TestMap.Builder builder) {
+ builder.getMutableInt32ToInt32Field().put(1, 11);
+ builder.getMutableInt32ToInt32Field().put(2, 22);
+ builder.getMutableInt32ToInt32Field().put(3, 33);
+
+ builder.getMutableInt32ToStringField().put(1, "11");
+ builder.getMutableInt32ToStringField().put(2, "22");
+ builder.getMutableInt32ToStringField().put(3, "33");
+
+ builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("11"));
+ builder.getMutableInt32ToBytesField().put(2, TestUtil.toBytes("22"));
+ builder.getMutableInt32ToBytesField().put(3, TestUtil.toBytes("33"));
+
+ builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.FOO);
+ builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.BAR);
+ builder.getMutableInt32ToEnumField().put(3, TestMap.EnumValue.BAZ);
+
+ builder.getMutableInt32ToMessageField().put(
+ 1, MessageValue.newBuilder().setValue(11).build());
+ builder.getMutableInt32ToMessageField().put(
+ 2, MessageValue.newBuilder().setValue(22).build());
+ builder.getMutableInt32ToMessageField().put(
+ 3, MessageValue.newBuilder().setValue(33).build());
+
+ builder.getMutableStringToInt32Field().put("1", 11);
+ builder.getMutableStringToInt32Field().put("2", 22);
+ builder.getMutableStringToInt32Field().put("3", 33);
+ }
+
+ private void assertMapValuesSet(TestMap message) {
+ assertEquals(3, message.getInt32ToInt32Field().size());
+ assertEquals(11, message.getInt32ToInt32Field().get(1).intValue());
+ assertEquals(22, message.getInt32ToInt32Field().get(2).intValue());
+ assertEquals(33, message.getInt32ToInt32Field().get(3).intValue());
+
+ assertEquals(3, message.getInt32ToStringField().size());
+ assertEquals("11", message.getInt32ToStringField().get(1));
+ assertEquals("22", message.getInt32ToStringField().get(2));
+ assertEquals("33", message.getInt32ToStringField().get(3));
+
+ assertEquals(3, message.getInt32ToBytesField().size());
+ assertEquals(TestUtil.toBytes("11"), message.getInt32ToBytesField().get(1));
+ assertEquals(TestUtil.toBytes("22"), message.getInt32ToBytesField().get(2));
+ assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3));
+
+ assertEquals(3, message.getInt32ToEnumField().size());
+ assertEquals(TestMap.EnumValue.FOO, message.getInt32ToEnumField().get(1));
+ assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(2));
+ assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3));
+
+ assertEquals(3, message.getInt32ToMessageField().size());
+ assertEquals(11, message.getInt32ToMessageField().get(1).getValue());
+ assertEquals(22, message.getInt32ToMessageField().get(2).getValue());
+ assertEquals(33, message.getInt32ToMessageField().get(3).getValue());
+
+ assertEquals(3, message.getStringToInt32Field().size());
+ assertEquals(11, message.getStringToInt32Field().get("1").intValue());
+ assertEquals(22, message.getStringToInt32Field().get("2").intValue());
+ assertEquals(33, message.getStringToInt32Field().get("3").intValue());
+ }
+
+ private void updateMapValues(TestMap.Builder builder) {
+ builder.getMutableInt32ToInt32Field().put(1, 111);
+ builder.getMutableInt32ToInt32Field().remove(2);
+ builder.getMutableInt32ToInt32Field().put(4, 44);
+
+ builder.getMutableInt32ToStringField().put(1, "111");
+ builder.getMutableInt32ToStringField().remove(2);
+ builder.getMutableInt32ToStringField().put(4, "44");
+
+ builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("111"));
+ builder.getMutableInt32ToBytesField().remove(2);
+ builder.getMutableInt32ToBytesField().put(4, TestUtil.toBytes("44"));
+
+ builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.BAR);
+ builder.getMutableInt32ToEnumField().remove(2);
+ builder.getMutableInt32ToEnumField().put(4, TestMap.EnumValue.QUX);
+
+ builder.getMutableInt32ToMessageField().put(
+ 1, MessageValue.newBuilder().setValue(111).build());
+ builder.getMutableInt32ToMessageField().remove(2);
+ builder.getMutableInt32ToMessageField().put(
+ 4, MessageValue.newBuilder().setValue(44).build());
+
+ builder.getMutableStringToInt32Field().put("1", 111);
+ builder.getMutableStringToInt32Field().remove("2");
+ builder.getMutableStringToInt32Field().put("4", 44);
+ }
+
+ private void assertMapValuesUpdated(TestMap message) {
+ assertEquals(3, message.getInt32ToInt32Field().size());
+ assertEquals(111, message.getInt32ToInt32Field().get(1).intValue());
+ assertEquals(33, message.getInt32ToInt32Field().get(3).intValue());
+ assertEquals(44, message.getInt32ToInt32Field().get(4).intValue());
+
+ assertEquals(3, message.getInt32ToStringField().size());
+ assertEquals("111", message.getInt32ToStringField().get(1));
+ assertEquals("33", message.getInt32ToStringField().get(3));
+ assertEquals("44", message.getInt32ToStringField().get(4));
+
+ assertEquals(3, message.getInt32ToBytesField().size());
+ assertEquals(TestUtil.toBytes("111"), message.getInt32ToBytesField().get(1));
+ assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3));
+ assertEquals(TestUtil.toBytes("44"), message.getInt32ToBytesField().get(4));
+
+ assertEquals(3, message.getInt32ToEnumField().size());
+ assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(1));
+ assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3));
+ assertEquals(TestMap.EnumValue.QUX, message.getInt32ToEnumField().get(4));
+
+ assertEquals(3, message.getInt32ToMessageField().size());
+ assertEquals(111, message.getInt32ToMessageField().get(1).getValue());
+ assertEquals(33, message.getInt32ToMessageField().get(3).getValue());
+ assertEquals(44, message.getInt32ToMessageField().get(4).getValue());
+
+ assertEquals(3, message.getStringToInt32Field().size());
+ assertEquals(111, message.getStringToInt32Field().get("1").intValue());
+ assertEquals(33, message.getStringToInt32Field().get("3").intValue());
+ assertEquals(44, message.getStringToInt32Field().get("4").intValue());
+ }
+
+ private void assertMapValuesCleared(TestMap message) {
+ assertEquals(0, message.getInt32ToInt32Field().size());
+ assertEquals(0, message.getInt32ToStringField().size());
+ assertEquals(0, message.getInt32ToBytesField().size());
+ assertEquals(0, message.getInt32ToEnumField().size());
+ assertEquals(0, message.getInt32ToMessageField().size());
+ assertEquals(0, message.getStringToInt32Field().size());
+ }
+
+ public void testGettersAndSetters() throws Exception {
+ TestMap.Builder builder = TestMap.newBuilder();
+ TestMap message = builder.build();
+ assertMapValuesCleared(message);
+
+ builder = message.toBuilder();
+ setMapValues(builder);
+ message = builder.build();
+ assertMapValuesSet(message);
+
+ builder = message.toBuilder();
+ updateMapValues(builder);
+ message = builder.build();
+ assertMapValuesUpdated(message);
+
+ builder = message.toBuilder();
+ builder.clear();
+ message = builder.build();
+ assertMapValuesCleared(message);
+ }
+
+ public void testSerializeAndParse() throws Exception {
+ TestMap.Builder builder = TestMap.newBuilder();
+ setMapValues(builder);
+ TestMap message = builder.build();
+ assertEquals(message.getSerializedSize(), message.toByteString().size());
+ message = TestMap.PARSER.parseFrom(message.toByteString());
+ assertMapValuesSet(message);
+
+ builder = message.toBuilder();
+ updateMapValues(builder);
+ message = builder.build();
+ assertEquals(message.getSerializedSize(), message.toByteString().size());
+ message = TestMap.PARSER.parseFrom(message.toByteString());
+ assertMapValuesUpdated(message);
+
+ builder = message.toBuilder();
+ builder.clear();
+ message = builder.build();
+ assertEquals(message.getSerializedSize(), message.toByteString().size());
+ message = TestMap.PARSER.parseFrom(message.toByteString());
+ assertMapValuesCleared(message);
+ }
+
+ public void testMergeFrom() throws Exception {
+ TestMap.Builder builder = TestMap.newBuilder();
+ setMapValues(builder);
+ TestMap message = builder.build();
+
+ TestMap.Builder other = TestMap.newBuilder();
+ other.mergeFrom(message);
+ assertMapValuesSet(other.build());
+ }
+
+ public void testEqualsAndHashCode() throws Exception {
+ // Test that generated equals() and hashCode() will disregard the order
+ // of map entries when comparing/hashing map fields.
+
+ // We can't control the order of elements in a HashMap. The best we can do
+ // here is to add elements in different order.
+ TestMap.Builder b1 = TestMap.newBuilder();
+ b1.getMutableInt32ToInt32Field().put(1, 2);
+ b1.getMutableInt32ToInt32Field().put(3, 4);
+ b1.getMutableInt32ToInt32Field().put(5, 6);
+ TestMap m1 = b1.build();
+
+ TestMap.Builder b2 = TestMap.newBuilder();
+ b2.getMutableInt32ToInt32Field().put(5, 6);
+ b2.getMutableInt32ToInt32Field().put(1, 2);
+ b2.getMutableInt32ToInt32Field().put(3, 4);
+ TestMap m2 = b2.build();
+
+ assertEquals(m1, m2);
+ assertEquals(m1.hashCode(), m2.hashCode());
+
+ // Make sure we did compare map fields.
+ b2.getMutableInt32ToInt32Field().put(1, 0);
+ m2 = b2.build();
+ assertFalse(m1.equals(m2));
+ // Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed
+ // to be different.
+ }
+
+
+ // The following methods are used to test reflection API.
+
+ private static FieldDescriptor f(String name) {
+ return TestMap.getDescriptor().findFieldByName(name);
+ }
+
+ private static Object getFieldValue(Message mapEntry, String name) {
+ FieldDescriptor field = mapEntry.getDescriptorForType().findFieldByName(name);
+ return mapEntry.getField(field);
+ }
+
+ private static Message.Builder setFieldValue(
+ Message.Builder mapEntry, String name, Object value) {
+ FieldDescriptor field = mapEntry.getDescriptorForType().findFieldByName(name);
+ mapEntry.setField(field, value);
+ return mapEntry;
+ }
+
+ private static void assertHasMapValues(Message message, String name, Map<?, ?> values) {
+ FieldDescriptor field = f(name);
+ for (Object entry : (List<?>) message.getField(field)) {
+ Message mapEntry = (Message) entry;
+ Object key = getFieldValue(mapEntry, "key");
+ Object value = getFieldValue(mapEntry, "value");
+ assertTrue(values.containsKey(key));
+ assertEquals(value, values.get(key));
+ }
+ assertEquals(values.size(), message.getRepeatedFieldCount(field));
+ for (int i = 0; i < message.getRepeatedFieldCount(field); i++) {
+ Message mapEntry = (Message) message.getRepeatedField(field, i);
+ Object key = getFieldValue(mapEntry, "key");
+ Object value = getFieldValue(mapEntry, "value");
+ assertTrue(values.containsKey(key));
+ assertEquals(value, values.get(key));
+ }
+ }
+
+ private static <KeyType, ValueType>
+ Message newMapEntry(Message.Builder builder, String name, KeyType key, ValueType value) {
+ FieldDescriptor field = builder.getDescriptorForType().findFieldByName(name);
+ Message.Builder entryBuilder = builder.newBuilderForField(field);
+ FieldDescriptor keyField = entryBuilder.getDescriptorForType().findFieldByName("key");
+ FieldDescriptor valueField = entryBuilder.getDescriptorForType().findFieldByName("value");
+ entryBuilder.setField(keyField, key);
+ entryBuilder.setField(valueField, value);
+ return entryBuilder.build();
+ }
+
+ private static void setMapValues(Message.Builder builder, String name, Map<?, ?> values) {
+ List<Message> entryList = new ArrayList<Message>();
+ for (Map.Entry<?, ?> entry : values.entrySet()) {
+ entryList.add(newMapEntry(builder, name, entry.getKey(), entry.getValue()));
+ }
+ FieldDescriptor field = builder.getDescriptorForType().findFieldByName(name);
+ builder.setField(field, entryList);
+ }
+
+ private static <KeyType, ValueType>
+ Map<KeyType, ValueType> mapForValues(
+ KeyType key1, ValueType value1, KeyType key2, ValueType value2) {
+ Map<KeyType, ValueType> map = new HashMap<KeyType, ValueType>();
+ map.put(key1, value1);
+ map.put(key2, value2);
+ return map;
+ }
+
+ public void testReflectionApi() throws Exception {
+ // In reflection API, map fields are just repeated message fields.
+ TestMap.Builder builder = TestMap.newBuilder();
+ builder.getMutableInt32ToInt32Field().put(1, 2);
+ builder.getMutableInt32ToInt32Field().put(3, 4);
+ builder.getMutableInt32ToMessageField().put(
+ 11, MessageValue.newBuilder().setValue(22).build());
+ builder.getMutableInt32ToMessageField().put(
+ 33, MessageValue.newBuilder().setValue(44).build());
+ TestMap message = builder.build();
+
+ // Test getField(), getRepeatedFieldCount(), getRepeatedField().
+ assertHasMapValues(message, "int32_to_int32_field",
+ mapForValues(1, 2, 3, 4));
+ assertHasMapValues(message, "int32_to_message_field",
+ mapForValues(
+ 11, MessageValue.newBuilder().setValue(22).build(),
+ 33, MessageValue.newBuilder().setValue(44).build()));
+
+ // Test clearField()
+ builder.clearField(f("int32_to_int32_field"));
+ builder.clearField(f("int32_to_message_field"));
+ message = builder.build();
+ assertEquals(0, message.getInt32ToInt32Field().size());
+ assertEquals(0, message.getInt32ToMessageField().size());
+
+ // Test setField()
+ setMapValues(builder, "int32_to_int32_field",
+ mapForValues(11, 22, 33, 44));
+ setMapValues(builder, "int32_to_message_field",
+ mapForValues(
+ 111, MessageValue.newBuilder().setValue(222).build(),
+ 333, MessageValue.newBuilder().setValue(444).build()));
+ message = builder.build();
+ assertEquals(22, message.getInt32ToInt32Field().get(11).intValue());
+ assertEquals(44, message.getInt32ToInt32Field().get(33).intValue());
+ assertEquals(222, message.getInt32ToMessageField().get(111).getValue());
+ assertEquals(444, message.getInt32ToMessageField().get(333).getValue());
+
+ // Test addRepeatedField
+ builder.addRepeatedField(f("int32_to_int32_field"),
+ newMapEntry(builder, "int32_to_int32_field", 55, 66));
+ builder.addRepeatedField(f("int32_to_message_field"),
+ newMapEntry(builder, "int32_to_message_field", 555,
+ MessageValue.newBuilder().setValue(666).build()));
+ message = builder.build();
+ assertEquals(66, message.getInt32ToInt32Field().get(55).intValue());
+ assertEquals(666, message.getInt32ToMessageField().get(555).getValue());
+
+ // Test addRepeatedField (overriding existing values)
+ builder.addRepeatedField(f("int32_to_int32_field"),
+ newMapEntry(builder, "int32_to_int32_field", 55, 55));
+ builder.addRepeatedField(f("int32_to_message_field"),
+ newMapEntry(builder, "int32_to_message_field", 555,
+ MessageValue.newBuilder().setValue(555).build()));
+ message = builder.build();
+ assertEquals(55, message.getInt32ToInt32Field().get(55).intValue());
+ assertEquals(555, message.getInt32ToMessageField().get(555).getValue());
+
+ // Test setRepeatedField
+ for (int i = 0; i < builder.getRepeatedFieldCount(f("int32_to_int32_field")); i++) {
+ Message mapEntry = (Message) builder.getRepeatedField(f("int32_to_int32_field"), i);
+ int oldKey = ((Integer) getFieldValue(mapEntry, "key")).intValue();
+ int oldValue = ((Integer) getFieldValue(mapEntry, "value")).intValue();
+ // Swap key with value for each entry.
+ Message.Builder mapEntryBuilder = mapEntry.toBuilder();
+ setFieldValue(mapEntryBuilder, "key", oldValue);
+ setFieldValue(mapEntryBuilder, "value", oldKey);
+ builder.setRepeatedField(f("int32_to_int32_field"), i, mapEntryBuilder.build());
+ }
+ message = builder.build();
+ assertEquals(11, message.getInt32ToInt32Field().get(22).intValue());
+ assertEquals(33, message.getInt32ToInt32Field().get(44).intValue());
+ assertEquals(55, message.getInt32ToInt32Field().get(55).intValue());
+ }
+
+ public void testTextFormat() throws Exception {
+ TestMap.Builder builder = TestMap.newBuilder();
+ setMapValues(builder);
+ TestMap message = builder.build();
+
+ String textData = TextFormat.printToString(message);
+
+ builder = TestMap.newBuilder();
+ TextFormat.merge(textData, builder);
+ message = builder.build();
+
+ assertMapValuesSet(message);
+ }
+
+ public void testDynamicMessage() throws Exception {
+ TestMap.Builder builder = TestMap.newBuilder();
+ setMapValues(builder);
+ TestMap message = builder.build();
+
+ Message dynamicDefaultInstance =
+ DynamicMessage.getDefaultInstance(TestMap.getDescriptor());
+ Message dynamicMessage = dynamicDefaultInstance
+ .newBuilderForType().mergeFrom(message.toByteString()).build();
+
+ assertEquals(message, dynamicMessage);
+ assertEquals(message.hashCode(), dynamicMessage.hashCode());
+ }
+
+ public void testReflectionEqualsAndHashCode() throws Exception {
+ // Test that generated equals() and hashCode() will disregard the order
+ // of map entries when comparing/hashing map fields.
+
+ // We use DynamicMessage to test reflection based equals()/hashCode().
+ Message dynamicDefaultInstance =
+ DynamicMessage.getDefaultInstance(TestMap.getDescriptor());
+ FieldDescriptor field = f("int32_to_int32_field");
+
+ Message.Builder b1 = dynamicDefaultInstance.newBuilderForType();
+ b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 1, 2));
+ b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 3, 4));
+ b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 5, 6));
+ Message m1 = b1.build();
+
+ Message.Builder b2 = dynamicDefaultInstance.newBuilderForType();
+ b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 5, 6));
+ b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 1, 2));
+ b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 3, 4));
+ Message m2 = b2.build();
+
+ assertEquals(m1, m2);
+ assertEquals(m1.hashCode(), m2.hashCode());
+
+ // Make sure we did compare map fields.
+ b2.setRepeatedField(field, 0, newMapEntry(b1, "int32_to_int32_field", 0, 0));
+ m2 = b2.build();
+ assertFalse(m1.equals(m2));
+ // Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed
+ // to be different.
+ }
+
+ public void testUnknownEnumValues() throws Exception {
+ TestUnknownEnumValue.Builder builder =
+ TestUnknownEnumValue.newBuilder();
+ builder.getMutableInt32ToInt32Field().put(1, 1);
+ builder.getMutableInt32ToInt32Field().put(2, 54321);
+ ByteString data = builder.build().toByteString();
+
+ TestMap message = TestMap.parseFrom(data);
+ // Entries with unknown enum values will be stored into UnknownFieldSet so
+ // there is only one entry in the map.
+ assertEquals(1, message.getInt32ToEnumField().size());
+ assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(1));
+ // UnknownFieldSet should not be empty.
+ assertFalse(message.getUnknownFields().asMap().isEmpty());
+ // Serializing and parsing should preserve the unknown entry.
+ data = message.toByteString();
+ TestUnknownEnumValue messageWithUnknownEnums =
+ TestUnknownEnumValue.parseFrom(data);
+ assertEquals(2, messageWithUnknownEnums.getInt32ToInt32Field().size());
+ assertEquals(1, messageWithUnknownEnums.getInt32ToInt32Field().get(1).intValue());
+ assertEquals(54321, messageWithUnknownEnums.getInt32ToInt32Field().get(2).intValue());
+ }
+
+}
diff --git a/java/src/test/java/com/google/protobuf/MapTest.java b/java/src/test/java/com/google/protobuf/MapTest.java
new file mode 100644
index 00000000..542a20e7
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/MapTest.java
@@ -0,0 +1,569 @@
+// 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.EnumDescriptor;
+import com.google.protobuf.Descriptors.EnumValueDescriptor;
+import com.google.protobuf.Descriptors.FieldDescriptor;
+import map_test.MapTestProto.TestMap;
+import map_test.MapTestProto.TestMap.MessageValue;
+import map_test.MapTestProto.TestOnChangeEventPropagation;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Unit tests for map fields.
+ */
+public class MapTest extends TestCase {
+ private void setMapValues(TestMap.Builder builder) {
+ builder.getMutableInt32ToInt32Field().put(1, 11);
+ builder.getMutableInt32ToInt32Field().put(2, 22);
+ builder.getMutableInt32ToInt32Field().put(3, 33);
+
+ builder.getMutableInt32ToStringField().put(1, "11");
+ builder.getMutableInt32ToStringField().put(2, "22");
+ builder.getMutableInt32ToStringField().put(3, "33");
+
+ builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("11"));
+ builder.getMutableInt32ToBytesField().put(2, TestUtil.toBytes("22"));
+ builder.getMutableInt32ToBytesField().put(3, TestUtil.toBytes("33"));
+
+ builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.FOO);
+ builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.BAR);
+ builder.getMutableInt32ToEnumField().put(3, TestMap.EnumValue.BAZ);
+
+ builder.getMutableInt32ToMessageField().put(
+ 1, MessageValue.newBuilder().setValue(11).build());
+ builder.getMutableInt32ToMessageField().put(
+ 2, MessageValue.newBuilder().setValue(22).build());
+ builder.getMutableInt32ToMessageField().put(
+ 3, MessageValue.newBuilder().setValue(33).build());
+
+ builder.getMutableStringToInt32Field().put("1", 11);
+ builder.getMutableStringToInt32Field().put("2", 22);
+ builder.getMutableStringToInt32Field().put("3", 33);
+ }
+
+ private void assertMapValuesSet(TestMap message) {
+ assertEquals(3, message.getInt32ToInt32Field().size());
+ assertEquals(11, message.getInt32ToInt32Field().get(1).intValue());
+ assertEquals(22, message.getInt32ToInt32Field().get(2).intValue());
+ assertEquals(33, message.getInt32ToInt32Field().get(3).intValue());
+
+ assertEquals(3, message.getInt32ToStringField().size());
+ assertEquals("11", message.getInt32ToStringField().get(1));
+ assertEquals("22", message.getInt32ToStringField().get(2));
+ assertEquals("33", message.getInt32ToStringField().get(3));
+
+ assertEquals(3, message.getInt32ToBytesField().size());
+ assertEquals(TestUtil.toBytes("11"), message.getInt32ToBytesField().get(1));
+ assertEquals(TestUtil.toBytes("22"), message.getInt32ToBytesField().get(2));
+ assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3));
+
+ assertEquals(3, message.getInt32ToEnumField().size());
+ assertEquals(TestMap.EnumValue.FOO, message.getInt32ToEnumField().get(1));
+ assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(2));
+ assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3));
+
+ assertEquals(3, message.getInt32ToMessageField().size());
+ assertEquals(11, message.getInt32ToMessageField().get(1).getValue());
+ assertEquals(22, message.getInt32ToMessageField().get(2).getValue());
+ assertEquals(33, message.getInt32ToMessageField().get(3).getValue());
+
+ assertEquals(3, message.getStringToInt32Field().size());
+ assertEquals(11, message.getStringToInt32Field().get("1").intValue());
+ assertEquals(22, message.getStringToInt32Field().get("2").intValue());
+ assertEquals(33, message.getStringToInt32Field().get("3").intValue());
+ }
+
+ private void updateMapValues(TestMap.Builder builder) {
+ builder.getMutableInt32ToInt32Field().put(1, 111);
+ builder.getMutableInt32ToInt32Field().remove(2);
+ builder.getMutableInt32ToInt32Field().put(4, 44);
+
+ builder.getMutableInt32ToStringField().put(1, "111");
+ builder.getMutableInt32ToStringField().remove(2);
+ builder.getMutableInt32ToStringField().put(4, "44");
+
+ builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("111"));
+ builder.getMutableInt32ToBytesField().remove(2);
+ builder.getMutableInt32ToBytesField().put(4, TestUtil.toBytes("44"));
+
+ builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.BAR);
+ builder.getMutableInt32ToEnumField().remove(2);
+ builder.getMutableInt32ToEnumField().put(4, TestMap.EnumValue.QUX);
+
+ builder.getMutableInt32ToMessageField().put(
+ 1, MessageValue.newBuilder().setValue(111).build());
+ builder.getMutableInt32ToMessageField().remove(2);
+ builder.getMutableInt32ToMessageField().put(
+ 4, MessageValue.newBuilder().setValue(44).build());
+
+ builder.getMutableStringToInt32Field().put("1", 111);
+ builder.getMutableStringToInt32Field().remove("2");
+ builder.getMutableStringToInt32Field().put("4", 44);
+ }
+
+ private void assertMapValuesUpdated(TestMap message) {
+ assertEquals(3, message.getInt32ToInt32Field().size());
+ assertEquals(111, message.getInt32ToInt32Field().get(1).intValue());
+ assertEquals(33, message.getInt32ToInt32Field().get(3).intValue());
+ assertEquals(44, message.getInt32ToInt32Field().get(4).intValue());
+
+ assertEquals(3, message.getInt32ToStringField().size());
+ assertEquals("111", message.getInt32ToStringField().get(1));
+ assertEquals("33", message.getInt32ToStringField().get(3));
+ assertEquals("44", message.getInt32ToStringField().get(4));
+
+ assertEquals(3, message.getInt32ToBytesField().size());
+ assertEquals(TestUtil.toBytes("111"), message.getInt32ToBytesField().get(1));
+ assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3));
+ assertEquals(TestUtil.toBytes("44"), message.getInt32ToBytesField().get(4));
+
+ assertEquals(3, message.getInt32ToEnumField().size());
+ assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(1));
+ assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3));
+ assertEquals(TestMap.EnumValue.QUX, message.getInt32ToEnumField().get(4));
+
+ assertEquals(3, message.getInt32ToMessageField().size());
+ assertEquals(111, message.getInt32ToMessageField().get(1).getValue());
+ assertEquals(33, message.getInt32ToMessageField().get(3).getValue());
+ assertEquals(44, message.getInt32ToMessageField().get(4).getValue());
+
+ assertEquals(3, message.getStringToInt32Field().size());
+ assertEquals(111, message.getStringToInt32Field().get("1").intValue());
+ assertEquals(33, message.getStringToInt32Field().get("3").intValue());
+ assertEquals(44, message.getStringToInt32Field().get("4").intValue());
+ }
+
+ private void assertMapValuesCleared(TestMap message) {
+ assertEquals(0, message.getInt32ToInt32Field().size());
+ assertEquals(0, message.getInt32ToStringField().size());
+ assertEquals(0, message.getInt32ToBytesField().size());
+ assertEquals(0, message.getInt32ToEnumField().size());
+ assertEquals(0, message.getInt32ToMessageField().size());
+ assertEquals(0, message.getStringToInt32Field().size());
+ }
+
+ public void testGettersAndSetters() throws Exception {
+ TestMap.Builder builder = TestMap.newBuilder();
+ TestMap message = builder.build();
+ assertMapValuesCleared(message);
+
+ builder = message.toBuilder();
+ setMapValues(builder);
+ message = builder.build();
+ assertMapValuesSet(message);
+
+ builder = message.toBuilder();
+ updateMapValues(builder);
+ message = builder.build();
+ assertMapValuesUpdated(message);
+
+ builder = message.toBuilder();
+ builder.clear();
+ message = builder.build();
+ assertMapValuesCleared(message);
+ }
+
+ public void testSerializeAndParse() throws Exception {
+ TestMap.Builder builder = TestMap.newBuilder();
+ setMapValues(builder);
+ TestMap message = builder.build();
+ assertEquals(message.getSerializedSize(), message.toByteString().size());
+ message = TestMap.PARSER.parseFrom(message.toByteString());
+ assertMapValuesSet(message);
+
+ builder = message.toBuilder();
+ updateMapValues(builder);
+ message = builder.build();
+ assertEquals(message.getSerializedSize(), message.toByteString().size());
+ message = TestMap.PARSER.parseFrom(message.toByteString());
+ assertMapValuesUpdated(message);
+
+ builder = message.toBuilder();
+ builder.clear();
+ message = builder.build();
+ assertEquals(message.getSerializedSize(), message.toByteString().size());
+ message = TestMap.PARSER.parseFrom(message.toByteString());
+ assertMapValuesCleared(message);
+ }
+
+ public void testMergeFrom() throws Exception {
+ TestMap.Builder builder = TestMap.newBuilder();
+ setMapValues(builder);
+ TestMap message = builder.build();
+
+ TestMap.Builder other = TestMap.newBuilder();
+ other.mergeFrom(message);
+ assertMapValuesSet(other.build());
+ }
+
+ public void testEqualsAndHashCode() throws Exception {
+ // Test that generated equals() and hashCode() will disregard the order
+ // of map entries when comparing/hashing map fields.
+
+ // We can't control the order of elements in a HashMap. The best we can do
+ // here is to add elements in different order.
+ TestMap.Builder b1 = TestMap.newBuilder();
+ b1.getMutableInt32ToInt32Field().put(1, 2);
+ b1.getMutableInt32ToInt32Field().put(3, 4);
+ b1.getMutableInt32ToInt32Field().put(5, 6);
+ TestMap m1 = b1.build();
+
+ TestMap.Builder b2 = TestMap.newBuilder();
+ b2.getMutableInt32ToInt32Field().put(5, 6);
+ b2.getMutableInt32ToInt32Field().put(1, 2);
+ b2.getMutableInt32ToInt32Field().put(3, 4);
+ TestMap m2 = b2.build();
+
+ assertEquals(m1, m2);
+ assertEquals(m1.hashCode(), m2.hashCode());
+
+ // Make sure we did compare map fields.
+ b2.getMutableInt32ToInt32Field().put(1, 0);
+ m2 = b2.build();
+ assertFalse(m1.equals(m2));
+ // Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed
+ // to be different.
+ }
+
+
+ public void testNestedBuilderOnChangeEventPropagation() {
+ TestOnChangeEventPropagation.Builder parent =
+ TestOnChangeEventPropagation.newBuilder();
+ parent.getOptionalMessageBuilder().getMutableInt32ToInt32Field().put(1, 2);
+ TestOnChangeEventPropagation message = parent.build();
+ assertEquals(2, message.getOptionalMessage().getInt32ToInt32Field().get(1).intValue());
+
+ // Make a change using nested builder.
+ parent.getOptionalMessageBuilder().getMutableInt32ToInt32Field().put(1, 3);
+
+ // Should be able to observe the change.
+ message = parent.build();
+ assertEquals(3, message.getOptionalMessage().getInt32ToInt32Field().get(1).intValue());
+
+ // Make another change using mergeFrom()
+ TestMap.Builder other = TestMap.newBuilder();
+ other.getMutableInt32ToInt32Field().put(1, 4);
+ parent.getOptionalMessageBuilder().mergeFrom(other.build());
+
+ // Should be able to observe the change.
+ message = parent.build();
+ assertEquals(4, message.getOptionalMessage().getInt32ToInt32Field().get(1).intValue());
+
+ // Make yet another change by clearing the nested builder.
+ parent.getOptionalMessageBuilder().clear();
+
+ // Should be able to observe the change.
+ message = parent.build();
+ assertEquals(0, message.getOptionalMessage().getInt32ToInt32Field().size());
+ }
+
+ // The following methods are used to test reflection API.
+
+ private static FieldDescriptor f(String name) {
+ return TestMap.getDescriptor().findFieldByName(name);
+ }
+
+ private static Object getFieldValue(Message mapEntry, String name) {
+ FieldDescriptor field = mapEntry.getDescriptorForType().findFieldByName(name);
+ return mapEntry.getField(field);
+ }
+
+ private static Message.Builder setFieldValue(
+ Message.Builder mapEntry, String name, Object value) {
+ FieldDescriptor field = mapEntry.getDescriptorForType().findFieldByName(name);
+ mapEntry.setField(field, value);
+ return mapEntry;
+ }
+
+ private static void assertHasMapValues(Message message, String name, Map<?, ?> values) {
+ FieldDescriptor field = f(name);
+ for (Object entry : (List<?>) message.getField(field)) {
+ Message mapEntry = (Message) entry;
+ Object key = getFieldValue(mapEntry, "key");
+ Object value = getFieldValue(mapEntry, "value");
+ assertTrue(values.containsKey(key));
+ assertEquals(value, values.get(key));
+ }
+ assertEquals(values.size(), message.getRepeatedFieldCount(field));
+ for (int i = 0; i < message.getRepeatedFieldCount(field); i++) {
+ Message mapEntry = (Message) message.getRepeatedField(field, i);
+ Object key = getFieldValue(mapEntry, "key");
+ Object value = getFieldValue(mapEntry, "value");
+ assertTrue(values.containsKey(key));
+ assertEquals(value, values.get(key));
+ }
+ }
+
+ private static <KeyType, ValueType>
+ Message newMapEntry(Message.Builder builder, String name, KeyType key, ValueType value) {
+ FieldDescriptor field = builder.getDescriptorForType().findFieldByName(name);
+ Message.Builder entryBuilder = builder.newBuilderForField(field);
+ FieldDescriptor keyField = entryBuilder.getDescriptorForType().findFieldByName("key");
+ FieldDescriptor valueField = entryBuilder.getDescriptorForType().findFieldByName("value");
+ entryBuilder.setField(keyField, key);
+ entryBuilder.setField(valueField, value);
+ return entryBuilder.build();
+ }
+
+ private static void setMapValues(Message.Builder builder, String name, Map<?, ?> values) {
+ List<Message> entryList = new ArrayList<Message>();
+ for (Map.Entry<?, ?> entry : values.entrySet()) {
+ entryList.add(newMapEntry(builder, name, entry.getKey(), entry.getValue()));
+ }
+ FieldDescriptor field = builder.getDescriptorForType().findFieldByName(name);
+ builder.setField(field, entryList);
+ }
+
+ private static <KeyType, ValueType>
+ Map<KeyType, ValueType> mapForValues(
+ KeyType key1, ValueType value1, KeyType key2, ValueType value2) {
+ Map<KeyType, ValueType> map = new HashMap<KeyType, ValueType>();
+ map.put(key1, value1);
+ map.put(key2, value2);
+ return map;
+ }
+
+ public void testReflectionApi() throws Exception {
+ // In reflection API, map fields are just repeated message fields.
+ TestMap.Builder builder = TestMap.newBuilder();
+ builder.getMutableInt32ToInt32Field().put(1, 2);
+ builder.getMutableInt32ToInt32Field().put(3, 4);
+ builder.getMutableInt32ToMessageField().put(
+ 11, MessageValue.newBuilder().setValue(22).build());
+ builder.getMutableInt32ToMessageField().put(
+ 33, MessageValue.newBuilder().setValue(44).build());
+ TestMap message = builder.build();
+
+ // Test getField(), getRepeatedFieldCount(), getRepeatedField().
+ assertHasMapValues(message, "int32_to_int32_field",
+ mapForValues(1, 2, 3, 4));
+ assertHasMapValues(message, "int32_to_message_field",
+ mapForValues(
+ 11, MessageValue.newBuilder().setValue(22).build(),
+ 33, MessageValue.newBuilder().setValue(44).build()));
+
+ // Test clearField()
+ builder.clearField(f("int32_to_int32_field"));
+ builder.clearField(f("int32_to_message_field"));
+ message = builder.build();
+ assertEquals(0, message.getInt32ToInt32Field().size());
+ assertEquals(0, message.getInt32ToMessageField().size());
+
+ // Test setField()
+ setMapValues(builder, "int32_to_int32_field",
+ mapForValues(11, 22, 33, 44));
+ setMapValues(builder, "int32_to_message_field",
+ mapForValues(
+ 111, MessageValue.newBuilder().setValue(222).build(),
+ 333, MessageValue.newBuilder().setValue(444).build()));
+ message = builder.build();
+ assertEquals(22, message.getInt32ToInt32Field().get(11).intValue());
+ assertEquals(44, message.getInt32ToInt32Field().get(33).intValue());
+ assertEquals(222, message.getInt32ToMessageField().get(111).getValue());
+ assertEquals(444, message.getInt32ToMessageField().get(333).getValue());
+
+ // Test addRepeatedField
+ builder.addRepeatedField(f("int32_to_int32_field"),
+ newMapEntry(builder, "int32_to_int32_field", 55, 66));
+ builder.addRepeatedField(f("int32_to_message_field"),
+ newMapEntry(builder, "int32_to_message_field", 555,
+ MessageValue.newBuilder().setValue(666).build()));
+ message = builder.build();
+ assertEquals(66, message.getInt32ToInt32Field().get(55).intValue());
+ assertEquals(666, message.getInt32ToMessageField().get(555).getValue());
+
+ // Test addRepeatedField (overriding existing values)
+ builder.addRepeatedField(f("int32_to_int32_field"),
+ newMapEntry(builder, "int32_to_int32_field", 55, 55));
+ builder.addRepeatedField(f("int32_to_message_field"),
+ newMapEntry(builder, "int32_to_message_field", 555,
+ MessageValue.newBuilder().setValue(555).build()));
+ message = builder.build();
+ assertEquals(55, message.getInt32ToInt32Field().get(55).intValue());
+ assertEquals(555, message.getInt32ToMessageField().get(555).getValue());
+
+ // Test setRepeatedField
+ for (int i = 0; i < builder.getRepeatedFieldCount(f("int32_to_int32_field")); i++) {
+ Message mapEntry = (Message) builder.getRepeatedField(f("int32_to_int32_field"), i);
+ int oldKey = ((Integer) getFieldValue(mapEntry, "key")).intValue();
+ int oldValue = ((Integer) getFieldValue(mapEntry, "value")).intValue();
+ // Swap key with value for each entry.
+ Message.Builder mapEntryBuilder = mapEntry.toBuilder();
+ setFieldValue(mapEntryBuilder, "key", oldValue);
+ setFieldValue(mapEntryBuilder, "value", oldKey);
+ builder.setRepeatedField(f("int32_to_int32_field"), i, mapEntryBuilder.build());
+ }
+ message = builder.build();
+ assertEquals(11, message.getInt32ToInt32Field().get(22).intValue());
+ assertEquals(33, message.getInt32ToInt32Field().get(44).intValue());
+ assertEquals(55, message.getInt32ToInt32Field().get(55).intValue());
+ }
+
+ public void testTextFormat() throws Exception {
+ TestMap.Builder builder = TestMap.newBuilder();
+ setMapValues(builder);
+ TestMap message = builder.build();
+
+ String textData = TextFormat.printToString(message);
+
+ builder = TestMap.newBuilder();
+ TextFormat.merge(textData, builder);
+ message = builder.build();
+
+ assertMapValuesSet(message);
+ }
+
+ public void testDynamicMessage() throws Exception {
+ TestMap.Builder builder = TestMap.newBuilder();
+ setMapValues(builder);
+ TestMap message = builder.build();
+
+ Message dynamicDefaultInstance =
+ DynamicMessage.getDefaultInstance(TestMap.getDescriptor());
+ Message dynamicMessage = dynamicDefaultInstance
+ .newBuilderForType().mergeFrom(message.toByteString()).build();
+
+ assertEquals(message, dynamicMessage);
+ assertEquals(message.hashCode(), dynamicMessage.hashCode());
+ }
+
+ public void testReflectionEqualsAndHashCode() throws Exception {
+ // Test that generated equals() and hashCode() will disregard the order
+ // of map entries when comparing/hashing map fields.
+
+ // We use DynamicMessage to test reflection based equals()/hashCode().
+ Message dynamicDefaultInstance =
+ DynamicMessage.getDefaultInstance(TestMap.getDescriptor());
+ FieldDescriptor field = f("int32_to_int32_field");
+
+ Message.Builder b1 = dynamicDefaultInstance.newBuilderForType();
+ b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 1, 2));
+ b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 3, 4));
+ b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 5, 6));
+ Message m1 = b1.build();
+
+ Message.Builder b2 = dynamicDefaultInstance.newBuilderForType();
+ b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 5, 6));
+ b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 1, 2));
+ b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 3, 4));
+ Message m2 = b2.build();
+
+ assertEquals(m1, m2);
+ assertEquals(m1.hashCode(), m2.hashCode());
+
+ // Make sure we did compare map fields.
+ b2.setRepeatedField(field, 0, newMapEntry(b1, "int32_to_int32_field", 0, 0));
+ m2 = b2.build();
+ assertFalse(m1.equals(m2));
+ // Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed
+ // to be different.
+ }
+
+ public void testUnknownEnumValues() throws Exception {
+ TestMap.Builder builder = TestMap.newBuilder();
+ builder.getMutableInt32ToEnumFieldValue().put(0, 0);
+ builder.getMutableInt32ToEnumFieldValue().put(1, 1);
+ builder.getMutableInt32ToEnumFieldValue().put(2, 1000); // unknown value.
+ TestMap message = builder.build();
+
+ assertEquals(TestMap.EnumValue.FOO,
+ message.getInt32ToEnumField().get(0));
+ assertEquals(TestMap.EnumValue.BAR,
+ message.getInt32ToEnumField().get(1));
+ assertEquals(TestMap.EnumValue.UNRECOGNIZED,
+ message.getInt32ToEnumField().get(2));
+ assertEquals(1000, message.getInt32ToEnumFieldValue().get(2).intValue());
+
+ // Unknown enum values should be preserved after:
+ // 1. Serialization and parsing.
+ // 2. toBuild().
+ // 3. mergeFrom().
+ message = TestMap.parseFrom(message.toByteString());
+ assertEquals(1000, message.getInt32ToEnumFieldValue().get(2).intValue());
+ builder = message.toBuilder();
+ assertEquals(1000, builder.getInt32ToEnumFieldValue().get(2).intValue());
+ builder = TestMap.newBuilder().mergeFrom(message);
+ assertEquals(1000, builder.getInt32ToEnumFieldValue().get(2).intValue());
+
+ // hashCode()/equals() should take unknown enum values into account.
+ builder.getMutableInt32ToEnumFieldValue().put(2, 1001);
+ TestMap message2 = builder.build();
+ assertFalse(message.hashCode() == message2.hashCode());
+ assertFalse(message.equals(message2));
+ // Unknown values will be converted to UNRECOGNIZED so the resulted enum map
+ // should be the same.
+ assertTrue(message.getInt32ToEnumField().equals(message2.getInt32ToEnumField()));
+ }
+
+ public void testUnknownEnumValuesInReflectionApi() throws Exception {
+ Descriptor descriptor = TestMap.getDescriptor();
+ EnumDescriptor enumDescriptor = TestMap.EnumValue.getDescriptor();
+ FieldDescriptor field = descriptor.findFieldByName("int32_to_enum_field");
+
+ Map<Integer, Integer> data = new HashMap<Integer, Integer>();
+ data.put(0, 0);
+ data.put(1, 1);
+ data.put(2, 1000); // unknown value.
+
+ TestMap.Builder builder = TestMap.newBuilder();
+ for (Map.Entry<Integer, Integer> entry : data.entrySet()) {
+ builder.getMutableInt32ToEnumFieldValue().put(entry.getKey(), entry.getValue());
+ }
+
+ // Try to read unknown enum values using reflection API.
+ for (int i = 0; i < builder.getRepeatedFieldCount(field); i++) {
+ Message mapEntry = (Message) builder.getRepeatedField(field, i);
+ int key = ((Integer) getFieldValue(mapEntry, "key")).intValue();
+ int value = ((EnumValueDescriptor) getFieldValue(mapEntry, "value")).getNumber();
+ assertEquals(data.get(key).intValue(), value);
+ Message.Builder mapEntryBuilder = mapEntry.toBuilder();
+ // Increase the value by 1.
+ setFieldValue(mapEntryBuilder, "value",
+ enumDescriptor.findValueByNumberCreatingIfUnknown(value + 1));
+ builder.setRepeatedField(field, i, mapEntryBuilder.build());
+ }
+
+ // Verify that enum values have been successfully updated.
+ TestMap message = builder.build();
+ for (Map.Entry<Integer, Integer> entry : message.getInt32ToEnumFieldValue().entrySet()) {
+ assertEquals(data.get(entry.getKey()) + 1, entry.getValue().intValue());
+ }
+ }
+}
diff --git a/java/src/test/java/com/google/protobuf/TestBadIdentifiers.java b/java/src/test/java/com/google/protobuf/TestBadIdentifiers.java
index 614ac7fe..2c60fe0e 100644
--- a/java/src/test/java/com/google/protobuf/TestBadIdentifiers.java
+++ b/java/src/test/java/com/google/protobuf/TestBadIdentifiers.java
@@ -72,7 +72,7 @@ public class TestBadIdentifiers extends TestCase {
assertEquals(0, message.getMessageField5Count());
assertEquals(0, message.getInt32FieldCount11());
- assertEquals(1, message.getEnumFieldCount12().getNumber());
+ assertEquals(0, message.getEnumFieldCount12().getNumber());
assertEquals("", message.getStringFieldCount13());
assertEquals(ByteString.EMPTY, message.getBytesFieldCount14());
assertEquals(0, message.getMessageFieldCount15().getSerializedSize());
diff --git a/java/src/test/java/com/google/protobuf/TextFormatTest.java b/java/src/test/java/com/google/protobuf/TextFormatTest.java
index 152bdadc..82f9582f 100644
--- a/java/src/test/java/com/google/protobuf/TextFormatTest.java
+++ b/java/src/test/java/com/google/protobuf/TextFormatTest.java
@@ -73,7 +73,7 @@ public class TextFormatTest extends TestCase {
private static String exoticText =
"repeated_int32: -1\n" +
"repeated_int32: -2147483648\n" +
- "repeated_int64: -1\n" +
+ "repeated_int64: -1,\n" +
"repeated_int64: -9223372036854775808\n" +
"repeated_uint32: 4294967295\n" +
"repeated_uint32: 2147483648\n" +
@@ -101,7 +101,7 @@ public class TextFormatTest extends TestCase {
private static String canonicalExoticText =
exoticText.replace(": .", ": 0.").replace(": -.", ": -0.") // short-form double
- .replace("23e", "23E").replace("E+", "E").replace("0.23E17", "2.3E16");
+ .replace("23e", "23E").replace("E+", "E").replace("0.23E17", "2.3E16").replace(",", "");
private String messageSetText =
"[protobuf_unittest.TestMessageSetExtension1] {\n" +
@@ -119,6 +119,7 @@ public class TextFormatTest extends TestCase {
" i: 456\n" +
"}\n";
+
private final TextFormat.Parser parserWithOverwriteForbidden =
TextFormat.Parser.newBuilder()
.setSingularOverwritePolicy(
@@ -460,6 +461,7 @@ public class TextFormatTest extends TestCase {
}
}
+
private void assertParseErrorWithOverwriteForbidden(String error,
String text) {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
@@ -553,10 +555,10 @@ public class TextFormatTest extends TestCase {
public void testEscape() throws Exception {
// Escape sequences.
- assertEquals("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"",
- TextFormat.escapeBytes(bytes("\0\001\007\b\f\n\r\t\013\\\'\"")));
- assertEquals("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"",
- TextFormat.escapeText("\0\001\007\b\f\n\r\t\013\\\'\""));
+ assertEquals("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"\\177",
+ TextFormat.escapeBytes(bytes("\0\001\007\b\f\n\r\t\013\\\'\"\177")));
+ assertEquals("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"\\177",
+ TextFormat.escapeText("\0\001\007\b\f\n\r\t\013\\\'\"\177"));
assertEquals(bytes("\0\001\007\b\f\n\r\t\013\\\'\""),
TextFormat.unescapeBytes("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\""));
assertEquals("\0\001\007\b\f\n\r\t\013\\\'\"",
@@ -900,6 +902,7 @@ public class TextFormatTest extends TestCase {
.build()));
}
+
public void testParseNonRepeatedFields() throws Exception {
assertParseSuccessWithOverwriteForbidden(
"repeated_int32: 1\n" +
diff --git a/java/src/test/java/com/google/protobuf/UnknownEnumValueTest.java b/java/src/test/java/com/google/protobuf/UnknownEnumValueTest.java
new file mode 100644
index 00000000..8f45976f
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/UnknownEnumValueTest.java
@@ -0,0 +1,255 @@
+// 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.EnumDescriptor;
+import com.google.protobuf.Descriptors.EnumValueDescriptor;
+import com.google.protobuf.Descriptors.FieldDescriptor;
+import com.google.protobuf.FieldPresenceTestProto.TestAllTypes;
+import com.google.protobuf.TextFormat.ParseException;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for protos that keep unknown enum values rather than discard
+ * them as unknown fields.
+ */
+public class UnknownEnumValueTest extends TestCase {
+ public void testUnknownEnumValues() throws Exception {
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+ builder.setOptionalNestedEnumValue(4321);
+ builder.addRepeatedNestedEnumValue(5432);
+ builder.addPackedNestedEnumValue(6543);
+ TestAllTypes message = builder.build();
+ assertEquals(4321, message.getOptionalNestedEnumValue());
+ assertEquals(5432, message.getRepeatedNestedEnumValue(0));
+ assertEquals(5432, message.getRepeatedNestedEnumValueList().get(0).intValue());
+ assertEquals(6543, message.getPackedNestedEnumValue(0));
+ // Returns UNRECOGNIZED if an enum type is requested.
+ assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getOptionalNestedEnum());
+ assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getRepeatedNestedEnum(0));
+ assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getRepeatedNestedEnumList().get(0));
+ assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getPackedNestedEnum(0));
+
+ // Test serialization and parsing.
+ ByteString data = message.toByteString();
+ message = TestAllTypes.parseFrom(data);
+ assertEquals(4321, message.getOptionalNestedEnumValue());
+ assertEquals(5432, message.getRepeatedNestedEnumValue(0));
+ assertEquals(5432, message.getRepeatedNestedEnumValueList().get(0).intValue());
+ assertEquals(6543, message.getPackedNestedEnumValue(0));
+ // Returns UNRECOGNIZED if an enum type is requested.
+ assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getOptionalNestedEnum());
+ assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getRepeatedNestedEnum(0));
+ assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getRepeatedNestedEnumList().get(0));
+ assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getPackedNestedEnum(0));
+
+ // Test toBuilder().
+ builder = message.toBuilder();
+ assertEquals(4321, builder.getOptionalNestedEnumValue());
+ assertEquals(5432, builder.getRepeatedNestedEnumValue(0));
+ assertEquals(5432, builder.getRepeatedNestedEnumValueList().get(0).intValue());
+ assertEquals(6543, builder.getPackedNestedEnumValue(0));
+ // Returns UNRECOGNIZED if an enum type is requested.
+ assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getOptionalNestedEnum());
+ assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getRepeatedNestedEnum(0));
+ assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getRepeatedNestedEnumList().get(0));
+ assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getPackedNestedEnum(0));
+
+ // Test mergeFrom().
+ builder = TestAllTypes.newBuilder().mergeFrom(message);
+ assertEquals(4321, builder.getOptionalNestedEnumValue());
+ assertEquals(5432, builder.getRepeatedNestedEnumValue(0));
+ assertEquals(5432, builder.getRepeatedNestedEnumValueList().get(0).intValue());
+ assertEquals(6543, builder.getPackedNestedEnumValue(0));
+ // Returns UNRECOGNIZED if an enum type is requested.
+ assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getOptionalNestedEnum());
+ assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getRepeatedNestedEnum(0));
+ assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getRepeatedNestedEnumList().get(0));
+ assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getPackedNestedEnum(0));
+
+ // Test equals() and hashCode()
+ TestAllTypes sameMessage = builder.build();
+ assertEquals(message, sameMessage);
+ assertEquals(message.hashCode(), sameMessage.hashCode());
+
+ // Getting the numeric value of UNRECOGNIZED will throw an exception.
+ try {
+ TestAllTypes.NestedEnum.UNRECOGNIZED.getNumber();
+ fail("Exception is expected.");
+ } catch (IllegalArgumentException e) {
+ // Expected.
+ }
+
+ // Setting an enum field to an UNRECOGNIZED value will throw an exception.
+ try {
+ builder.setOptionalNestedEnum(builder.getOptionalNestedEnum());
+ fail("Exception is expected.");
+ } catch (IllegalArgumentException e) {
+ // Expected.
+ }
+ try {
+ builder.addRepeatedNestedEnum(builder.getOptionalNestedEnum());
+ fail("Exception is expected.");
+ } catch (IllegalArgumentException e) {
+ // Expected.
+ }
+ }
+
+ public void testUnknownEnumValueInReflectionApi() throws Exception {
+ Descriptor descriptor = TestAllTypes.getDescriptor();
+ FieldDescriptor optionalNestedEnumField = descriptor.findFieldByName("optional_nested_enum");
+ FieldDescriptor repeatedNestedEnumField = descriptor.findFieldByName("repeated_nested_enum");
+ FieldDescriptor packedNestedEnumField = descriptor.findFieldByName("packed_nested_enum");
+ EnumDescriptor enumType = TestAllTypes.NestedEnum.getDescriptor();
+
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+ builder.setField(optionalNestedEnumField,
+ enumType.findValueByNumberCreatingIfUnknown(4321));
+ builder.addRepeatedField(repeatedNestedEnumField,
+ enumType.findValueByNumberCreatingIfUnknown(5432));
+ builder.addRepeatedField(packedNestedEnumField,
+ enumType.findValueByNumberCreatingIfUnknown(6543));
+ TestAllTypes message = builder.build();
+
+ // Getters will return unknown enum values as EnumValueDescriptor.
+ EnumValueDescriptor unknown4321 =
+ (EnumValueDescriptor) message.getField(optionalNestedEnumField);
+ EnumValueDescriptor unknown5432 =
+ (EnumValueDescriptor) message.getRepeatedField(repeatedNestedEnumField, 0);
+ EnumValueDescriptor unknown6543 =
+ (EnumValueDescriptor) message.getRepeatedField(packedNestedEnumField, 0);
+ assertEquals(4321, unknown4321.getNumber());
+ assertEquals(5432, unknown5432.getNumber());
+ assertEquals(6543, unknown6543.getNumber());
+
+ // Unknown EnumValueDescriptor will map to UNRECOGNIZED.
+ assertEquals(
+ TestAllTypes.NestedEnum.valueOf(unknown4321),
+ TestAllTypes.NestedEnum.UNRECOGNIZED);
+ assertEquals(
+ TestAllTypes.NestedEnum.valueOf(unknown5432),
+ TestAllTypes.NestedEnum.UNRECOGNIZED);
+ assertEquals(
+ TestAllTypes.NestedEnum.valueOf(unknown6543),
+ TestAllTypes.NestedEnum.UNRECOGNIZED);
+
+ // Setters also accept unknown EnumValueDescriptor.
+ builder.setField(optionalNestedEnumField, unknown6543);
+ builder.setRepeatedField(repeatedNestedEnumField, 0, unknown4321);
+ builder.setRepeatedField(packedNestedEnumField, 0, unknown5432);
+ message = builder.build();
+ // Like other descriptors, unknown EnumValueDescriptor can be compared by
+ // object identity.
+ assertTrue(unknown6543 == message.getField(optionalNestedEnumField));
+ assertTrue(unknown4321 == message.getRepeatedField(repeatedNestedEnumField, 0));
+ assertTrue(unknown5432 == message.getRepeatedField(packedNestedEnumField, 0));
+ }
+
+ public void testUnknownEnumValueWithDynamicMessage() throws Exception {
+ Descriptor descriptor = TestAllTypes.getDescriptor();
+ FieldDescriptor optionalNestedEnumField = descriptor.findFieldByName("optional_nested_enum");
+ FieldDescriptor repeatedNestedEnumField = descriptor.findFieldByName("repeated_nested_enum");
+ FieldDescriptor packedNestedEnumField = descriptor.findFieldByName("packed_nested_enum");
+ EnumDescriptor enumType = TestAllTypes.NestedEnum.getDescriptor();
+
+ Message dynamicMessageDefaultInstance = DynamicMessage.getDefaultInstance(descriptor);
+
+ Message.Builder builder = dynamicMessageDefaultInstance.newBuilderForType();
+ builder.setField(optionalNestedEnumField,
+ enumType.findValueByNumberCreatingIfUnknown(4321));
+ builder.addRepeatedField(repeatedNestedEnumField,
+ enumType.findValueByNumberCreatingIfUnknown(5432));
+ builder.addRepeatedField(packedNestedEnumField,
+ enumType.findValueByNumberCreatingIfUnknown(6543));
+ Message message = builder.build();
+ assertEquals(4321,
+ ((EnumValueDescriptor) message.getField(optionalNestedEnumField)).getNumber());
+ assertEquals(5432,
+ ((EnumValueDescriptor) message.getRepeatedField(repeatedNestedEnumField, 0)).getNumber());
+ assertEquals(6543,
+ ((EnumValueDescriptor) message.getRepeatedField(packedNestedEnumField, 0)).getNumber());
+
+ // Test reflection based serialization/parsing implementation.
+ ByteString data = message.toByteString();
+ message = dynamicMessageDefaultInstance
+ .newBuilderForType()
+ .mergeFrom(data)
+ .build();
+ assertEquals(4321,
+ ((EnumValueDescriptor) message.getField(optionalNestedEnumField)).getNumber());
+ assertEquals(5432,
+ ((EnumValueDescriptor) message.getRepeatedField(repeatedNestedEnumField, 0)).getNumber());
+ assertEquals(6543,
+ ((EnumValueDescriptor) message.getRepeatedField(packedNestedEnumField, 0)).getNumber());
+
+ // Test reflection based equals()/hashCode().
+ builder = dynamicMessageDefaultInstance.newBuilderForType();
+ builder.setField(optionalNestedEnumField,
+ enumType.findValueByNumberCreatingIfUnknown(4321));
+ builder.addRepeatedField(repeatedNestedEnumField,
+ enumType.findValueByNumberCreatingIfUnknown(5432));
+ builder.addRepeatedField(packedNestedEnumField,
+ enumType.findValueByNumberCreatingIfUnknown(6543));
+ Message sameMessage = builder.build();
+ assertEquals(message, sameMessage);
+ assertEquals(message.hashCode(), sameMessage.hashCode());
+ builder.setField(optionalNestedEnumField,
+ enumType.findValueByNumberCreatingIfUnknown(0));
+ Message differentMessage = builder.build();
+ assertFalse(message.equals(differentMessage));
+ }
+
+ public void testUnknownEnumValuesInTextFormat() {
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+ builder.setOptionalNestedEnumValue(4321);
+ builder.addRepeatedNestedEnumValue(5432);
+ builder.addPackedNestedEnumValue(6543);
+ TestAllTypes message = builder.build();
+
+ // We can print a message with unknown enum values.
+ String textData = TextFormat.printToString(message);
+ assertEquals(
+ "optional_nested_enum: UNKNOWN_ENUM_VALUE_NestedEnum_4321\n"
+ + "repeated_nested_enum: UNKNOWN_ENUM_VALUE_NestedEnum_5432\n"
+ + "packed_nested_enum: UNKNOWN_ENUM_VALUE_NestedEnum_6543\n", textData);
+
+ // Parsing unknown enum values will fail just like parsing other kinds of
+ // unknown fields.
+ try {
+ TextFormat.merge(textData, builder);
+ fail();
+ } catch (ParseException e) {
+ // expected.
+ }
+ }
+}
diff --git a/java/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java b/java/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java
new file mode 100644
index 00000000..6372b7a7
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java
@@ -0,0 +1,317 @@
+// 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 protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash;
+import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Bar;
+import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Foo;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Tests for {@link UnknownFieldSetLite}.
+ *
+ * @author dweis@google.com (Daniel Weis)
+ */
+public class UnknownFieldSetLiteTest extends TestCase {
+
+ public void testNoDataIsDefaultInstance() {
+ assertSame(
+ UnknownFieldSetLite.getDefaultInstance(),
+ UnknownFieldSetLite.newBuilder()
+ .build());
+ }
+
+ public void testDefaultInstance() {
+ UnknownFieldSetLite unknownFields = UnknownFieldSetLite.getDefaultInstance();
+
+ assertEquals(0, unknownFields.getSerializedSize());
+ assertEquals(ByteString.EMPTY, toByteString(unknownFields));
+ }
+
+ public void testMergeFieldFrom() throws IOException {
+ Foo foo = Foo.newBuilder()
+ .setValue(2)
+ .build();
+
+ CodedInputStream input = CodedInputStream.newInstance(foo.toByteArray());
+
+ UnknownFieldSetLite.Builder builder = UnknownFieldSetLite.newBuilder();
+ builder.mergeFieldFrom(input.readTag(), input);
+
+ assertEquals(foo.toByteString(), toByteString(builder.build()));
+ }
+
+ public void testSerializedSize() throws IOException {
+ Foo foo = Foo.newBuilder()
+ .setValue(2)
+ .build();
+
+ CodedInputStream input = CodedInputStream.newInstance(foo.toByteArray());
+
+ UnknownFieldSetLite.Builder builder = UnknownFieldSetLite.newBuilder();
+ builder.mergeFieldFrom(input.readTag(), input);
+
+ assertEquals(foo.toByteString().size(), builder.build().getSerializedSize());
+ }
+
+ public void testMergeVarintField() throws IOException {
+ UnknownFieldSetLite.Builder builder = UnknownFieldSetLite.newBuilder();
+ builder.mergeVarintField(10, 2);
+
+ CodedInputStream input =
+ CodedInputStream.newInstance(toByteString(builder.build()).toByteArray());
+
+ int tag = input.readTag();
+ assertEquals(10, WireFormat.getTagFieldNumber(tag));
+ assertEquals(WireFormat.WIRETYPE_VARINT, WireFormat.getTagWireType(tag));
+ assertEquals(2, input.readUInt64());
+ assertTrue(input.isAtEnd());
+ }
+
+ public void testMergeVarintField_negative() throws IOException {
+ UnknownFieldSetLite.Builder builder = UnknownFieldSetLite.newBuilder();
+ builder.mergeVarintField(10, -6);
+
+ CodedInputStream input =
+ CodedInputStream.newInstance(toByteString(builder.build()).toByteArray());
+
+ int tag = input.readTag();
+ assertEquals(10, WireFormat.getTagFieldNumber(tag));
+ assertEquals(WireFormat.WIRETYPE_VARINT, WireFormat.getTagWireType(tag));
+ assertEquals(-6, input.readUInt64());
+ assertTrue(input.isAtEnd());
+ }
+
+ public void testEqualsAndHashCode() {
+ UnknownFieldSetLite.Builder builder1 = UnknownFieldSetLite.newBuilder();
+ builder1.mergeVarintField(10, 2);
+ UnknownFieldSetLite unknownFields1 = builder1.build();
+
+ UnknownFieldSetLite.Builder builder2 = UnknownFieldSetLite.newBuilder();
+ builder2.mergeVarintField(10, 2);
+ UnknownFieldSetLite unknownFields2 = builder2.build();
+
+ assertEquals(unknownFields1, unknownFields2);
+ assertEquals(unknownFields1.hashCode(), unknownFields2.hashCode());
+ assertFalse(unknownFields1.equals(UnknownFieldSetLite.getDefaultInstance()));
+ assertFalse(unknownFields1.hashCode() == UnknownFieldSetLite.getDefaultInstance().hashCode());
+ }
+
+ public void testConcat() throws IOException {
+ UnknownFieldSetLite.Builder builder = UnknownFieldSetLite.newBuilder();
+ builder.mergeVarintField(10, 2);
+ UnknownFieldSetLite unknownFields = builder.build();
+
+ unknownFields = UnknownFieldSetLite.concat(unknownFields, unknownFields);
+
+ CodedInputStream input =
+ CodedInputStream.newInstance(toByteString(unknownFields).toByteArray());
+
+ int tag = input.readTag();
+ assertEquals(10, WireFormat.getTagFieldNumber(tag));
+ assertEquals(WireFormat.WIRETYPE_VARINT, WireFormat.getTagWireType(tag));
+ assertEquals(2, input.readUInt64());
+ assertFalse(input.isAtEnd());
+ input.readTag();
+ assertEquals(10, WireFormat.getTagFieldNumber(tag));
+ assertEquals(WireFormat.WIRETYPE_VARINT, WireFormat.getTagWireType(tag));
+ assertEquals(2, input.readUInt64());
+ assertTrue(input.isAtEnd());
+ }
+
+ public void testConcat_empty() {
+ UnknownFieldSetLite unknownFields = UnknownFieldSetLite.concat(
+ UnknownFieldSetLite.getDefaultInstance(), UnknownFieldSetLite.getDefaultInstance());
+
+ assertEquals(0, unknownFields.getSerializedSize());
+ assertEquals(ByteString.EMPTY, toByteString(unknownFields));
+ }
+
+ public void testBuilderReuse() throws IOException {
+ UnknownFieldSetLite.Builder builder = UnknownFieldSetLite.newBuilder();
+ builder.mergeVarintField(10, 2);
+ builder.build();
+
+ try {
+ builder.build();
+ fail();
+ } catch (IllegalStateException e) {
+ // Expected.
+ }
+
+ try {
+ builder.mergeFieldFrom(0, CodedInputStream.newInstance(new byte[0]));
+ fail();
+ } catch (IllegalStateException e) {
+ // Expected.
+ }
+
+ try {
+ builder.mergeVarintField(5, 1);
+ fail();
+ } catch (IllegalStateException e) {
+ // Expected.
+ }
+ }
+
+ public void testBuilderReuse_empty() {
+ UnknownFieldSetLite.Builder builder = UnknownFieldSetLite.newBuilder();
+ builder.build();
+
+ try {
+ builder.build();
+ fail();
+ } catch (IllegalStateException e) {
+ // Expected.
+ }
+ }
+
+ public void testRoundTrips() throws InvalidProtocolBufferException {
+ Foo foo = Foo.newBuilder()
+ .setValue(1)
+ .setExtension(Bar.fooExt, Bar.newBuilder()
+ .setName("name")
+ .build())
+ .setExtension(LiteEqualsAndHash.varint, 22)
+ .setExtension(LiteEqualsAndHash.fixed32, 44)
+ .setExtension(LiteEqualsAndHash.fixed64, 66L)
+ .setExtension(LiteEqualsAndHash.myGroup, LiteEqualsAndHash.MyGroup.newBuilder()
+ .setGroupValue("value")
+ .build())
+ .build();
+
+ Foo copy = Foo.parseFrom(foo.toByteArray());
+
+ assertEquals(foo.getSerializedSize(), copy.getSerializedSize());
+ assertFalse(foo.equals(copy));
+
+ Foo secondCopy = Foo.parseFrom(foo.toByteArray());
+ assertEquals(copy, secondCopy);
+
+ ExtensionRegistryLite extensionRegistry = ExtensionRegistryLite.newInstance();
+ LiteEqualsAndHash.registerAllExtensions(extensionRegistry);
+ Foo copyOfCopy = Foo.parseFrom(copy.toByteArray(), extensionRegistry);
+
+ assertEquals(foo, copyOfCopy);
+ }
+
+ public void testMalformedBytes() {
+ try {
+ Foo.parseFrom("this is a malformed protocol buffer".getBytes(StandardCharsets.UTF_8));
+ fail();
+ } catch (InvalidProtocolBufferException e) {
+ // Expected.
+ }
+ }
+
+ public void testMissingStartGroupTag() throws IOException {
+ ByteString.Output byteStringOutput = ByteString.newOutput();
+ CodedOutputStream output = CodedOutputStream.newInstance(byteStringOutput);
+ output.writeGroupNoTag(Foo.newBuilder().setValue(11).build());
+ output.writeTag(100, WireFormat.WIRETYPE_END_GROUP);
+ output.flush();
+
+ try {
+ Foo.parseFrom(byteStringOutput.toByteString());
+ fail();
+ } catch (InvalidProtocolBufferException e) {
+ // Expected.
+ }
+ }
+
+ public void testMissingEndGroupTag() throws IOException {
+ ByteString.Output byteStringOutput = ByteString.newOutput();
+ CodedOutputStream output = CodedOutputStream.newInstance(byteStringOutput);
+ output.writeTag(100, WireFormat.WIRETYPE_START_GROUP);
+ output.writeGroupNoTag(Foo.newBuilder().setValue(11).build());
+ output.flush();
+
+ try {
+ Foo.parseFrom(byteStringOutput.toByteString());
+ fail();
+ } catch (InvalidProtocolBufferException e) {
+ // Expected.
+ }
+ }
+
+ public void testMismatchingGroupTags() throws IOException {
+ ByteString.Output byteStringOutput = ByteString.newOutput();
+ CodedOutputStream output = CodedOutputStream.newInstance(byteStringOutput);
+ output.writeTag(100, WireFormat.WIRETYPE_START_GROUP);
+ output.writeGroupNoTag(Foo.newBuilder().setValue(11).build());
+ output.writeTag(101, WireFormat.WIRETYPE_END_GROUP);
+ output.flush();
+
+ try {
+ Foo.parseFrom(byteStringOutput.toByteString());
+ fail();
+ } catch (InvalidProtocolBufferException e) {
+ // Expected.
+ }
+ }
+
+ public void testTruncatedInput() {
+ Foo foo = Foo.newBuilder()
+ .setValue(1)
+ .setExtension(Bar.fooExt, Bar.newBuilder()
+ .setName("name")
+ .build())
+ .setExtension(LiteEqualsAndHash.varint, 22)
+ .setExtension(LiteEqualsAndHash.myGroup, LiteEqualsAndHash.MyGroup.newBuilder()
+ .setGroupValue("value")
+ .build())
+ .build();
+
+ try {
+ Foo.parseFrom(foo.toByteString().substring(0, foo.toByteString().size() - 10));
+ fail();
+ } catch (InvalidProtocolBufferException e) {
+ // Expected.
+ }
+ }
+
+ private ByteString toByteString(UnknownFieldSetLite unknownFields) {
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ CodedOutputStream output = CodedOutputStream.newInstance(byteArrayOutputStream);
+ try {
+ unknownFields.writeTo(output);
+ output.flush();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return ByteString.copyFrom(byteArrayOutputStream.toByteArray());
+ }
+}
diff --git a/java/src/test/java/com/google/protobuf/field_presence_test.proto b/java/src/test/java/com/google/protobuf/field_presence_test.proto
new file mode 100644
index 00000000..8d8ea8ef
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/field_presence_test.proto
@@ -0,0 +1,93 @@
+// 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.
+
+syntax = "proto3";
+
+package field_presence_test;
+
+import "google/protobuf/unittest.proto";
+
+option java_package = "com.google.protobuf";
+option java_outer_classname = "FieldPresenceTestProto";
+option java_generate_equals_and_hash = true;
+
+message TestAllTypes {
+ enum NestedEnum {
+ FOO = 0;
+ BAR = 1;
+ BAZ = 2;
+ }
+ message NestedMessage {
+ optional int32 value = 1;
+ }
+
+ optional int32 optional_int32 = 1;
+ optional string optional_string = 2;
+ optional bytes optional_bytes = 3;
+ optional NestedEnum optional_nested_enum = 4;
+ optional NestedMessage optional_nested_message = 5;
+ optional protobuf_unittest.TestRequired optional_proto2_message = 6;
+
+ oneof oneof_field {
+ int32 oneof_int32 = 11;
+ uint32 oneof_uint32 = 12;
+ string oneof_string = 13;
+ bytes oneof_bytes = 14;
+ NestedEnum oneof_nested_enum = 15;
+ NestedMessage oneof_nested_message = 16;
+ protobuf_unittest.TestRequired oneof_proto2_message = 17;
+ }
+
+ repeated int32 repeated_int32 = 21;
+ repeated string repeated_string = 22;
+ repeated bytes repeated_bytes = 23;
+ repeated NestedEnum repeated_nested_enum = 24;
+ repeated NestedMessage repeated_nested_message = 25;
+ repeated protobuf_unittest.TestRequired repeated_proto2_message = 26;
+ repeated NestedEnum packed_nested_enum = 27 [packed = true];
+}
+
+message TestOptionalFieldsOnly {
+ optional int32 optional_int32 = 1;
+ optional string optional_string = 2;
+ optional bytes optional_bytes = 3;
+ optional TestAllTypes.NestedEnum optional_nested_enum = 4;
+ optional TestAllTypes.NestedMessage optional_nested_message = 5;
+ optional protobuf_unittest.TestRequired optional_proto2_message = 6;
+}
+
+message TestRepeatedFieldsOnly {
+ repeated int32 repeated_int32 = 21;
+ repeated string repeated_string = 22;
+ repeated bytes repeated_bytes = 23;
+ repeated TestAllTypes.NestedEnum repeated_nested_enum = 24;
+ repeated TestAllTypes.NestedMessage repeated_nested_message = 25;
+ repeated protobuf_unittest.TestRequired repeated_proto2_message = 26;
+}
diff --git a/java/src/test/java/com/google/protobuf/lazy_fields_lite.proto b/java/src/test/java/com/google/protobuf/lazy_fields_lite.proto
index baed4e10..015dc267 100644
--- a/java/src/test/java/com/google/protobuf/lazy_fields_lite.proto
+++ b/java/src/test/java/com/google/protobuf/lazy_fields_lite.proto
@@ -32,6 +32,7 @@
//
// A proto file with lazy fields
+syntax = "proto2";
package protobuf_unittest;
diff --git a/java/src/test/java/com/google/protobuf/lite_equals_and_hash.proto b/java/src/test/java/com/google/protobuf/lite_equals_and_hash.proto
index 68615672..86837250 100644
--- a/java/src/test/java/com/google/protobuf/lite_equals_and_hash.proto
+++ b/java/src/test/java/com/google/protobuf/lite_equals_and_hash.proto
@@ -30,6 +30,7 @@
// Author: pbogle@google.com (Phil Bogle)
+syntax = "proto2";
package protobuf_unittest.lite_equals_and_hash;
@@ -41,9 +42,15 @@ option optimize_for = LITE_RUNTIME;
message Foo {
optional int32 value = 1;
repeated Bar bar = 2;
+
+ extensions 100 to max;
}
message Bar {
+ extend Foo {
+ optional Bar foo_ext = 100;
+ }
+
optional string name = 1;
}
@@ -53,3 +60,13 @@ message BarPrime {
message Empty {
}
+
+extend Foo {
+ optional int32 varint = 101;
+ optional fixed32 fixed32 = 102;
+ optional fixed64 fixed64 = 103;
+ optional group MyGroup = 104 {
+ optional string group_value = 1;
+ }
+}
+
diff --git a/java/src/test/java/com/google/protobuf/map_for_proto2_lite_test.proto b/java/src/test/java/com/google/protobuf/map_for_proto2_lite_test.proto
new file mode 100644
index 00000000..4f23a4a8
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/map_for_proto2_lite_test.proto
@@ -0,0 +1,63 @@
+// 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.
+
+syntax = "proto2";
+
+
+option java_outer_classname = "MapForProto2TestProto";
+option java_generate_equals_and_hash = true;
+
+message TestMap {
+ message MessageValue {
+ optional int32 value = 1;
+ }
+ enum EnumValue {
+ FOO = 0;
+ BAR = 1;
+ BAZ = 2;
+ QUX = 3;
+ }
+
+ map<int32, int32> int32_to_int32_field = 1;
+ map<int32, string> int32_to_string_field = 2;
+ map<int32, bytes> int32_to_bytes_field = 3;
+ map<int32, EnumValue> int32_to_enum_field = 4;
+ map<int32, MessageValue> int32_to_message_field = 5;
+ map<string, int32> string_to_int32_field = 6;
+}
+
+message TestUnknownEnumValue {
+ // Wire-compatible with TestMap.int32_to_enum_field so we can test the
+ // parsing behavior of TestMap regarding unknown enum values.
+ map<int32, int32> int32_to_int32_field = 4;
+}
+package map_for_proto2_lite_test;
+option java_package = "map_lite_test";
+option optimize_for = LITE_RUNTIME;
diff --git a/java/src/test/java/com/google/protobuf/map_for_proto2_test.proto b/java/src/test/java/com/google/protobuf/map_for_proto2_test.proto
new file mode 100644
index 00000000..36dfbd10
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/map_for_proto2_test.proto
@@ -0,0 +1,62 @@
+// 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.
+
+syntax = "proto2";
+
+package map_for_proto2_test;
+
+option java_package = "map_test";
+option java_outer_classname = "MapForProto2TestProto";
+option java_generate_equals_and_hash = true;
+
+message TestMap {
+ message MessageValue {
+ optional int32 value = 1;
+ }
+ enum EnumValue {
+ FOO = 0;
+ BAR = 1;
+ BAZ = 2;
+ QUX = 3;
+ }
+
+ map<int32, int32> int32_to_int32_field = 1;
+ map<int32, string> int32_to_string_field = 2;
+ map<int32, bytes> int32_to_bytes_field = 3;
+ map<int32, EnumValue> int32_to_enum_field = 4;
+ map<int32, MessageValue> int32_to_message_field = 5;
+ map<string, int32> string_to_int32_field = 6;
+}
+
+message TestUnknownEnumValue {
+ // Wire-compatible with TestMap.int32_to_enum_field so we can test the
+ // parsing behavior of TestMap regarding unknown enum values.
+ map<int32, int32> int32_to_int32_field = 4;
+}
diff --git a/java/src/test/java/com/google/protobuf/map_test.proto b/java/src/test/java/com/google/protobuf/map_test.proto
new file mode 100644
index 00000000..105ee3f8
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/map_test.proto
@@ -0,0 +1,63 @@
+// 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.
+
+syntax = "proto3";
+
+package map_test;
+
+option java_package = "map_test";
+option java_outer_classname = "MapTestProto";
+option java_generate_equals_and_hash = true;
+
+message TestMap {
+ message MessageValue {
+ optional int32 value = 1;
+ }
+ enum EnumValue {
+ FOO = 0;
+ BAR = 1;
+ BAZ = 2;
+ QUX = 3;
+ }
+
+ map<int32, int32> int32_to_int32_field = 1;
+ map<int32, string> int32_to_string_field = 2;
+ map<int32, bytes> int32_to_bytes_field = 3;
+ map<int32, EnumValue> int32_to_enum_field = 4;
+ map<int32, MessageValue> int32_to_message_field = 5;
+ map<string, int32> string_to_int32_field = 6;
+}
+
+// Used to test that a nested bulider containing map fields will properly
+// propagate the onChange event and mark its parent dirty when a change
+// is made to a map field.
+message TestOnChangeEventPropagation {
+ optional TestMap optional_message = 1;
+}
diff --git a/java/src/test/java/com/google/protobuf/multiple_files_test.proto b/java/src/test/java/com/google/protobuf/multiple_files_test.proto
index 7ec89a05..92790506 100644
--- a/java/src/test/java/com/google/protobuf/multiple_files_test.proto
+++ b/java/src/test/java/com/google/protobuf/multiple_files_test.proto
@@ -32,6 +32,7 @@
//
// A proto file which tests the java_multiple_files option.
+syntax = "proto2";
// Some generic_services option(s) added automatically.
// See: http://go/proto2-generic-services-default
diff --git a/java/src/test/java/com/google/protobuf/nested_builders_test.proto b/java/src/test/java/com/google/protobuf/nested_builders_test.proto
index 8ab4a433..a5dd66d8 100644
--- a/java/src/test/java/com/google/protobuf/nested_builders_test.proto
+++ b/java/src/test/java/com/google/protobuf/nested_builders_test.proto
@@ -30,6 +30,7 @@
// Author: jonp@google.com (Jon Perlow)
//
+syntax = "proto2";
package protobuf_unittest;
diff --git a/java/src/test/java/com/google/protobuf/nested_extension.proto b/java/src/test/java/com/google/protobuf/nested_extension.proto
index 86492867..704e649e 100644
--- a/java/src/test/java/com/google/protobuf/nested_extension.proto
+++ b/java/src/test/java/com/google/protobuf/nested_extension.proto
@@ -33,6 +33,7 @@
// A proto file with nested extensions. Note that this must be defined in
// a separate file to properly test the initialization of the outer class.
+syntax = "proto2";
import "com/google/protobuf/non_nested_extension.proto";
diff --git a/java/src/test/java/com/google/protobuf/nested_extension_lite.proto b/java/src/test/java/com/google/protobuf/nested_extension_lite.proto
index 9c686829..a95c38b2 100644
--- a/java/src/test/java/com/google/protobuf/nested_extension_lite.proto
+++ b/java/src/test/java/com/google/protobuf/nested_extension_lite.proto
@@ -34,6 +34,7 @@
// this must be defined in a separate file to properly test the initialization
// of the outer class.
+syntax = "proto2";
package protobuf_unittest;
diff --git a/java/src/test/java/com/google/protobuf/non_nested_extension.proto b/java/src/test/java/com/google/protobuf/non_nested_extension.proto
index cb2f8b00..31fac552 100644
--- a/java/src/test/java/com/google/protobuf/non_nested_extension.proto
+++ b/java/src/test/java/com/google/protobuf/non_nested_extension.proto
@@ -32,6 +32,7 @@
//
// A proto file with extensions.
+syntax = "proto2";
package protobuf_unittest;
diff --git a/java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto b/java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto
index c1f744b8..37c369ed 100644
--- a/java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto
+++ b/java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto
@@ -32,6 +32,7 @@
//
// A proto file with extensions for a MessageLite messages.
+syntax = "proto2";
package protobuf_unittest;
diff --git a/java/src/test/java/com/google/protobuf/outer_class_name_test.proto b/java/src/test/java/com/google/protobuf/outer_class_name_test.proto
index c5114f89..42083681 100644
--- a/java/src/test/java/com/google/protobuf/outer_class_name_test.proto
+++ b/java/src/test/java/com/google/protobuf/outer_class_name_test.proto
@@ -28,6 +28,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+syntax = "proto2";
+
package protobuf_unittest;
diff --git a/java/src/test/java/com/google/protobuf/outer_class_name_test2.proto b/java/src/test/java/com/google/protobuf/outer_class_name_test2.proto
index 741cd281..3e5956b0 100644
--- a/java/src/test/java/com/google/protobuf/outer_class_name_test2.proto
+++ b/java/src/test/java/com/google/protobuf/outer_class_name_test2.proto
@@ -28,6 +28,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+syntax = "proto2";
+
package protobuf_unittest;
diff --git a/java/src/test/java/com/google/protobuf/outer_class_name_test3.proto b/java/src/test/java/com/google/protobuf/outer_class_name_test3.proto
index 5d5d4ac9..74a8ba3c 100644
--- a/java/src/test/java/com/google/protobuf/outer_class_name_test3.proto
+++ b/java/src/test/java/com/google/protobuf/outer_class_name_test3.proto
@@ -28,6 +28,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+syntax = "proto2";
+
package protobuf_unittest;
diff --git a/java/src/test/java/com/google/protobuf/test_bad_identifiers.proto b/java/src/test/java/com/google/protobuf/test_bad_identifiers.proto
index 202e8c91..67035fd5 100644
--- a/java/src/test/java/com/google/protobuf/test_bad_identifiers.proto
+++ b/java/src/test/java/com/google/protobuf/test_bad_identifiers.proto
@@ -33,6 +33,7 @@
// This file tests that various identifiers work as field and type names even
// though the same identifiers are used internally by the java code generator.
+syntax = "proto2";
// Some generic_services option(s) added automatically.
// See: http://go/proto2-generic-services-default
@@ -59,12 +60,14 @@ message Descriptor {
}
optional NestedDescriptor nested_descriptor = 2;
enum NestedEnum {
+ UNKNOWN = 0;
FOO = 1;
}
}
message Parser {
enum ParserEnum {
+ UNKNOWN = 0;
PARSER = 1;
}
optional ParserEnum parser = 1;
@@ -72,6 +75,7 @@ message Parser {
message Deprecated {
enum TestEnum {
+ UNKNOWN = 0;
FOO = 1;
// Test if @Deprecated annotation conflicts with Deprecated message name.
@@ -118,6 +122,7 @@ service TestConflictingMethodNames {
message TestConflictingFieldNames {
enum TestEnum {
+ UNKNOWN = 0;
FOO = 1;
}
message TestMessage {
@@ -142,16 +147,23 @@ message TestConflictingFieldNames {
// This field conflicts with "int32_field" as they both generate
// the method getInt32FieldList().
- required int32 int32_field_list = 31;
+ required int32 int32_field_list = 31; // NO_PROTO3
- extensions 1000 to max;
+ extensions 1000 to max; // NO_PROTO3
repeated int64 int64_field = 41;
- extend TestConflictingFieldNames {
+ extend TestConflictingFieldNames { // NO_PROTO3
// We don't generate accessors for extensions so the following extension
// fields don't conflict with the repeated field "int64_field".
- optional int64 int64_field_count = 1001;
- optional int64 int64_field_list = 1002;
- }
+ optional int64 int64_field_count = 1001; // NO_PROTO3
+ optional int64 int64_field_list = 1002; // NO_PROTO3
+ } // NO_PROTO3
}
+message TestMapField {
+ message MapField {}
+ message Pair {}
+ message Message {}
+
+ map<int32, int32> map_field = 1;
+}
diff --git a/java/src/test/java/com/google/protobuf/test_check_utf8.proto b/java/src/test/java/com/google/protobuf/test_check_utf8.proto
index 206946d8..119c1dcb 100644
--- a/java/src/test/java/com/google/protobuf/test_check_utf8.proto
+++ b/java/src/test/java/com/google/protobuf/test_check_utf8.proto
@@ -31,6 +31,7 @@
// Author: Jacob Butcher (jbaum@google.com)
//
// Test file option java_string_check_utf8.
+syntax = "proto2";
package proto2_test_check_utf8;
diff --git a/java/src/test/java/com/google/protobuf/test_check_utf8_size.proto b/java/src/test/java/com/google/protobuf/test_check_utf8_size.proto
index fa057a88..f06d76d6 100644
--- a/java/src/test/java/com/google/protobuf/test_check_utf8_size.proto
+++ b/java/src/test/java/com/google/protobuf/test_check_utf8_size.proto
@@ -31,6 +31,7 @@
// Author: Jacob Butcher (jbaum@google.com)
//
// Test file option java_string_check_utf8.
+syntax = "proto2";
package proto2_test_check_utf8_size;
diff --git a/java/src/test/java/com/google/protobuf/test_custom_options.proto b/java/src/test/java/com/google/protobuf/test_custom_options.proto
index f6a5ecd4..f8efd455 100644
--- a/java/src/test/java/com/google/protobuf/test_custom_options.proto
+++ b/java/src/test/java/com/google/protobuf/test_custom_options.proto
@@ -32,6 +32,7 @@
//
// Test that custom options defined in a proto file's dependencies are properly
// initialized.
+syntax = "proto2";
package protobuf_unittest;
diff --git a/java/src/test/java/com/google/protobuf/test_extra_interfaces.proto b/java/src/test/java/com/google/protobuf/test_extra_interfaces.proto
index 72e05a36..645f57b4 100644
--- a/java/src/test/java/com/google/protobuf/test_extra_interfaces.proto
+++ b/java/src/test/java/com/google/protobuf/test_extra_interfaces.proto
@@ -29,6 +29,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: Darick Tong (darick@google.com)
+syntax = "proto2";
package protobuf_unittest;