down integrate to svn

pull/1/head
jieluo@google.com 11 years ago
parent c5553a3d18
commit 4de8f55113
  1. 22
      Makefile.am
  2. 2
      examples/Makefile
  3. 10
      java/pom.xml
  4. 730
      java/src/main/java/com/google/protobuf/AbstractMessage.java
  5. 24
      java/src/main/java/com/google/protobuf/AbstractMessageLite.java
  6. 8
      java/src/main/java/com/google/protobuf/AbstractParser.java
  7. 52
      java/src/main/java/com/google/protobuf/ByteString.java
  8. 671
      java/src/main/java/com/google/protobuf/CodedInputStream.java
  9. 230
      java/src/main/java/com/google/protobuf/CodedOutputStream.java
  10. 364
      java/src/main/java/com/google/protobuf/Descriptors.java
  11. 118
      java/src/main/java/com/google/protobuf/DynamicMessage.java
  12. 96
      java/src/main/java/com/google/protobuf/Extension.java
  13. 186
      java/src/main/java/com/google/protobuf/ExtensionRegistry.java
  14. 100
      java/src/main/java/com/google/protobuf/FieldSet.java
  15. 388
      java/src/main/java/com/google/protobuf/GeneratedMessage.java
  16. 228
      java/src/main/java/com/google/protobuf/GeneratedMessageLite.java
  17. 238
      java/src/main/java/com/google/protobuf/Internal.java
  18. 8
      java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java
  19. 106
      java/src/main/java/com/google/protobuf/LazyField.java
  20. 215
      java/src/main/java/com/google/protobuf/LazyStringArrayList.java
  21. 110
      java/src/main/java/com/google/protobuf/LazyStringList.java
  22. 25
      java/src/main/java/com/google/protobuf/LiteralByteString.java
  23. 7
      java/src/main/java/com/google/protobuf/Message.java
  24. 1
      java/src/main/java/com/google/protobuf/MessageLite.java
  25. 16
      java/src/main/java/com/google/protobuf/MessageOrBuilder.java
  26. 931
      java/src/main/java/com/google/protobuf/MessageReflection.java
  27. 2
      java/src/main/java/com/google/protobuf/Parser.java
  28. 48
      java/src/main/java/com/google/protobuf/ProtocolStringList.java
  29. 14
      java/src/main/java/com/google/protobuf/RopeByteString.java
  30. 5
      java/src/main/java/com/google/protobuf/RpcUtil.java
  31. 792
      java/src/main/java/com/google/protobuf/TextFormat.java
  32. 17
      java/src/main/java/com/google/protobuf/UnknownFieldSet.java
  33. 57
      java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java
  34. 18
      java/src/test/java/com/google/protobuf/AbstractMessageTest.java
  35. 67
      java/src/test/java/com/google/protobuf/ByteStringTest.java
  36. 141
      java/src/test/java/com/google/protobuf/CheckUtf8Test.java
  37. 279
      java/src/test/java/com/google/protobuf/CodedInputStreamTest.java
  38. 84
      java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java
  39. 66
      java/src/test/java/com/google/protobuf/DescriptorsTest.java
  40. 46
      java/src/test/java/com/google/protobuf/DynamicMessageTest.java
  41. 371
      java/src/test/java/com/google/protobuf/GeneratedMessageTest.java
  42. 134
      java/src/test/java/com/google/protobuf/LazyFieldLiteTest.java
  43. 121
      java/src/test/java/com/google/protobuf/LazyFieldTest.java
  44. 319
      java/src/test/java/com/google/protobuf/LazyMessageLiteTest.java
  45. 12
      java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java
  46. 85
      java/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java
  47. 24
      java/src/test/java/com/google/protobuf/ParserTest.java
  48. 33
      java/src/test/java/com/google/protobuf/TestBadIdentifiers.java
  49. 175
      java/src/test/java/com/google/protobuf/TestUtil.java
  50. 325
      java/src/test/java/com/google/protobuf/TextFormatTest.java
  51. 216
      java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java
  52. 75
      java/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java
  53. 30
      java/src/test/java/com/google/protobuf/WireFormatTest.java
  54. 61
      java/src/test/java/com/google/protobuf/lazy_fields_lite.proto
  55. 42
      java/src/test/java/com/google/protobuf/lite_equals_and_hash.proto
  56. 8
      java/src/test/java/com/google/protobuf/multiple_files_test.proto
  57. 1
      java/src/test/java/com/google/protobuf/nested_extension.proto
  58. 1
      java/src/test/java/com/google/protobuf/non_nested_extension.proto
  59. 38
      java/src/test/java/com/google/protobuf/outer_class_name_test.proto
  60. 42
      java/src/test/java/com/google/protobuf/outer_class_name_test2.proto
  61. 43
      java/src/test/java/com/google/protobuf/outer_class_name_test3.proto
  62. 49
      java/src/test/java/com/google/protobuf/test_bad_identifiers.proto
  63. 50
      java/src/test/java/com/google/protobuf/test_check_utf8.proto
  64. 51
      java/src/test/java/com/google/protobuf/test_check_utf8_size.proto
  65. 43
      java/src/test/java/com/google/protobuf/test_custom_options.proto
  66. 71
      java/src/test/java/com/google/protobuf/test_extra_interfaces.proto
  67. 1717
      python/google/protobuf/pyext/python-proto2.cc
  68. 337
      python/google/protobuf/pyext/python_descriptor.cc
  69. 38
      python/setup.py
  70. 24
      src/Makefile.am
  71. 8
      src/google/protobuf/compiler/code_generator.cc
  72. 3
      src/google/protobuf/compiler/code_generator.h
  73. 194
      src/google/protobuf/compiler/command_line_interface.cc
  74. 29
      src/google/protobuf/compiler/command_line_interface.h
  75. 127
      src/google/protobuf/compiler/command_line_interface_unittest.cc
  76. 8
      src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc
  77. 52
      src/google/protobuf/compiler/cpp/cpp_enum.cc
  78. 2
      src/google/protobuf/compiler/cpp/cpp_enum.h
  79. 73
      src/google/protobuf/compiler/cpp/cpp_enum_field.cc
  80. 19
      src/google/protobuf/compiler/cpp/cpp_enum_field.h
  81. 28
      src/google/protobuf/compiler/cpp/cpp_field.cc
  82. 10
      src/google/protobuf/compiler/cpp/cpp_field.h
  83. 61
      src/google/protobuf/compiler/cpp/cpp_file.cc
  84. 2
      src/google/protobuf/compiler/cpp/cpp_file.h
  85. 5
      src/google/protobuf/compiler/cpp/cpp_generator.cc
  86. 120
      src/google/protobuf/compiler/cpp/cpp_helpers.cc
  87. 24
      src/google/protobuf/compiler/cpp/cpp_helpers.h
  88. 985
      src/google/protobuf/compiler/cpp/cpp_message.cc
  89. 6
      src/google/protobuf/compiler/cpp/cpp_message.h
  90. 83
      src/google/protobuf/compiler/cpp/cpp_message_field.cc
  91. 19
      src/google/protobuf/compiler/cpp/cpp_message_field.h
  92. 18
      src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc
  93. 64
      src/google/protobuf/compiler/cpp/cpp_primitive_field.cc
  94. 20
      src/google/protobuf/compiler/cpp/cpp_primitive_field.h
  95. 1
      src/google/protobuf/compiler/cpp/cpp_service.h
  96. 183
      src/google/protobuf/compiler/cpp/cpp_string_field.cc
  97. 21
      src/google/protobuf/compiler/cpp/cpp_string_field.h
  98. 9
      src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto
  99. 732
      src/google/protobuf/compiler/cpp/cpp_unittest.cc
  100. 39
      src/google/protobuf/compiler/importer.cc
  101. Some files were not shown because too many files have changed in this diff Show More

@ -78,6 +78,7 @@ EXTRA_DIST = \
java/src/main/java/com/google/protobuf/CodedOutputStream.java \
java/src/main/java/com/google/protobuf/Descriptors.java \
java/src/main/java/com/google/protobuf/DynamicMessage.java \
java/src/main/java/com/google/protobuf/Extension.java \
java/src/main/java/com/google/protobuf/ExtensionRegistry.java \
java/src/main/java/com/google/protobuf/ExtensionRegistryLite.java \
java/src/main/java/com/google/protobuf/FieldSet.java \
@ -86,6 +87,7 @@ EXTRA_DIST = \
java/src/main/java/com/google/protobuf/Internal.java \
java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java \
java/src/main/java/com/google/protobuf/LazyField.java \
java/src/main/java/com/google/protobuf/LazyFieldLite.java \
java/src/main/java/com/google/protobuf/LazyStringArrayList.java \
java/src/main/java/com/google/protobuf/LazyStringList.java \
java/src/main/java/com/google/protobuf/LiteralByteString.java \
@ -93,8 +95,10 @@ EXTRA_DIST = \
java/src/main/java/com/google/protobuf/MessageLite.java \
java/src/main/java/com/google/protobuf/MessageLiteOrBuilder.java \
java/src/main/java/com/google/protobuf/MessageOrBuilder.java \
java/src/main/java/com/google/protobuf/MessageReflection.java \
java/src/main/java/com/google/protobuf/Parser.java \
java/src/main/java/com/google/protobuf/ProtocolMessageEnum.java \
java/src/main/java/com/google/protobuf/ProtocolStringList.java \
java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java \
java/src/main/java/com/google/protobuf/RopeByteString.java \
java/src/main/java/com/google/protobuf/RpcCallback.java \
@ -114,6 +118,7 @@ EXTRA_DIST = \
java/src/test/java/com/google/protobuf/AbstractMessageTest.java \
java/src/test/java/com/google/protobuf/BoundedByteStringTest.java \
java/src/test/java/com/google/protobuf/ByteStringTest.java \
java/src/test/java/com/google/protobuf/CheckUtf8Test.java \
java/src/test/java/com/google/protobuf/CodedInputStreamTest.java \
java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java \
java/src/test/java/com/google/protobuf/DeprecatedFieldTest.java \
@ -123,8 +128,12 @@ EXTRA_DIST = \
java/src/test/java/com/google/protobuf/GeneratedMessageTest.java \
java/src/test/java/com/google/protobuf/IsValidUtf8Test.java \
java/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java \
java/src/test/java/com/google/protobuf/LazyFieldTest.java \
java/src/test/java/com/google/protobuf/LazyFieldLiteTest.java \
java/src/test/java/com/google/protobuf/LazyMessageLiteTest.java \
java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java \
java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java \
java/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java \
java/src/test/java/com/google/protobuf/LiteralByteStringTest.java \
java/src/test/java/com/google/protobuf/LiteTest.java \
java/src/test/java/com/google/protobuf/MessageTest.java \
@ -142,13 +151,21 @@ EXTRA_DIST = \
java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java \
java/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java \
java/src/test/java/com/google/protobuf/WireFormatTest.java \
java/src/test/java/com/google/protobuf/lazy_fields_lite.proto \
java/src/test/java/com/google/protobuf/lite_equals_and_hash.proto \
java/src/test/java/com/google/protobuf/multiple_files_test.proto \
java/src/test/java/com/google/protobuf/nested_builders_test.proto \
java/src/test/java/com/google/protobuf/nested_extension_lite.proto \
java/src/test/java/com/google/protobuf/nested_extension.proto \
java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto \
java/src/test/java/com/google/protobuf/non_nested_extension.proto \
java/src/test/java/com/google/protobuf/outer_class_name_test.proto \
java/src/test/java/com/google/protobuf/outer_class_name_test2.proto \
java/src/test/java/com/google/protobuf/outer_class_name_test3.proto \
java/src/test/java/com/google/protobuf/test_bad_identifiers.proto \
java/src/test/java/com/google/protobuf/test_check_utf8.proto \
java/src/test/java/com/google/protobuf/test_check_utf8_size.proto \
java/src/test/java/com/google/protobuf/test_custom_options.proto \
java/pom.xml \
java/README.txt \
python/google/protobuf/internal/generator_test.py \
@ -182,11 +199,6 @@ EXTRA_DIST = \
python/google/protobuf/internal/wire_format.py \
python/google/protobuf/internal/wire_format_test.py \
python/google/protobuf/internal/__init__.py \
python/google/protobuf/pyext/python-proto2.cc \
python/google/protobuf/pyext/python_descriptor.cc \
python/google/protobuf/pyext/python_descriptor.h \
python/google/protobuf/pyext/python_protobuf.cc \
python/google/protobuf/pyext/python_protobuf.h \
python/google/protobuf/descriptor.py \
python/google/protobuf/descriptor_database.py \
python/google/protobuf/descriptor_pool.py \

@ -30,7 +30,7 @@ list_people_cpp: list_people.cc protoc_middleman
c++ list_people.cc addressbook.pb.cc -o list_people_cpp `pkg-config --cflags --libs protobuf`
javac_middleman: AddPerson.java ListPeople.java protoc_middleman
javac AddPerson.java ListPeople.java com/example/tutorial/AddressBookProtos.java
javac AddPerson.java ListPeople.java com/example/tutorial/AddressBookProtos.java com/example/tutorial/AddressBookProtosInternalDescriptors.java
@touch javac_middleman
add_person_java: javac_middleman

@ -104,6 +104,8 @@
<arg value="../src/google/protobuf/unittest_import.proto" />
<arg value="../src/google/protobuf/unittest_import_public.proto" />
<arg value="../src/google/protobuf/unittest_mset.proto" />
<arg value="src/test/java/com/google/protobuf/lazy_fields_lite.proto" />
<arg value="src/test/java/com/google/protobuf/lite_equals_and_hash.proto" />
<arg
value="src/test/java/com/google/protobuf/multiple_files_test.proto" />
<arg value="src/test/java/com/google/protobuf/nested_builders_test.proto" />
@ -111,7 +113,13 @@
<arg value="src/test/java/com/google/protobuf/nested_extension_lite.proto" />
<arg value="src/test/java/com/google/protobuf/non_nested_extension.proto" />
<arg value="src/test/java/com/google/protobuf/non_nested_extension_lite.proto" />
<arg value="src/test/java/com/google/protobuf/outer_class_name_test.proto" />
<arg value="src/test/java/com/google/protobuf/outer_class_name_test2.proto" />
<arg value="src/test/java/com/google/protobuf/outer_class_name_test3.proto" />
<arg value="src/test/java/com/google/protobuf/test_bad_identifiers.proto" />
<arg value="src/test/java/com/google/protobuf/test_check_utf8.proto" />
<arg value="src/test/java/com/google/protobuf/test_check_utf8_size.proto" />
<arg value="src/test/java/com/google/protobuf/test_custom_options.proto" />
<arg
value="../src/google/protobuf/unittest_optimize_for.proto" />
<arg
@ -179,6 +187,8 @@
<include>**/RopeByteString.java</include>
<include>**/Utf8.java</include>
<include>**/LazyField.java</include>
<include>**/LazyFieldLite.java</include>
<include>**/ProtocolStringList.java</include>
</includes>
<testIncludes>
<testInclude>**/LiteTest.java</testInclude>

@ -30,14 +30,13 @@
package com.google.protobuf;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.GeneratedMessage.ExtendableBuilder;
import com.google.protobuf.Descriptors.OneofDescriptor;
import com.google.protobuf.Internal.EnumLite;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@ -49,56 +48,30 @@ import java.util.Map;
*/
public abstract class AbstractMessage extends AbstractMessageLite
implements Message {
@SuppressWarnings("unchecked")
public boolean isInitialized() {
// Check that all required fields are present.
for (final FieldDescriptor field : getDescriptorForType().getFields()) {
if (field.isRequired()) {
if (!hasField(field)) {
return false;
}
}
}
// Check that embedded messages are initialized.
for (final Map.Entry<FieldDescriptor, Object> entry :
getAllFields().entrySet()) {
final FieldDescriptor field = entry.getKey();
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
if (field.isRepeated()) {
for (final Message element : (List<Message>) entry.getValue()) {
if (!element.isInitialized()) {
return false;
}
}
} else {
if (!((Message) entry.getValue()).isInitialized()) {
return false;
}
}
}
}
return true;
return MessageReflection.isInitialized(this);
}
public List<String> findInitializationErrors() {
return Builder.findMissingFields(this);
return MessageReflection.findMissingFields(this);
}
public String getInitializationErrorString() {
return delimitWithCommas(findInitializationErrors());
return MessageReflection.delimitWithCommas(findInitializationErrors());
}
private static String delimitWithCommas(List<String> parts) {
StringBuilder result = new StringBuilder();
for (String part : parts) {
if (result.length() > 0) {
result.append(", ");
}
result.append(part);
}
return result.toString();
/** TODO(jieluo): Clear it when all subclasses have implemented this method. */
@Override
public boolean hasOneof(OneofDescriptor oneof) {
throw new UnsupportedOperationException("hasOneof() is not implemented.");
}
/** TODO(jieluo): Clear it when all subclasses have implemented this method. */
@Override
public FieldDescriptor getOneofFieldDescriptor(OneofDescriptor oneof) {
throw new UnsupportedOperationException(
"getOneofFieldDescriptor() is not implemented.");
}
@Override
@ -107,28 +80,7 @@ public abstract class AbstractMessage extends AbstractMessageLite
}
public void writeTo(final CodedOutputStream output) throws IOException {
final boolean isMessageSet =
getDescriptorForType().getOptions().getMessageSetWireFormat();
for (final Map.Entry<FieldDescriptor, Object> entry :
getAllFields().entrySet()) {
final FieldDescriptor field = entry.getKey();
final Object value = entry.getValue();
if (isMessageSet && field.isExtension() &&
field.getType() == FieldDescriptor.Type.MESSAGE &&
!field.isRepeated()) {
output.writeMessageSetExtension(field.getNumber(), (Message) value);
} else {
FieldSet.writeField(field, value, output);
}
}
final UnknownFieldSet unknownFields = getUnknownFields();
if (isMessageSet) {
unknownFields.writeAsMessageSetTo(output);
} else {
unknownFields.writeTo(output);
}
MessageReflection.writeMessageTo(this, output, false);
}
private int memoizedSize = -1;
@ -139,33 +91,8 @@ public abstract class AbstractMessage extends AbstractMessageLite
return size;
}
size = 0;
final boolean isMessageSet =
getDescriptorForType().getOptions().getMessageSetWireFormat();
for (final Map.Entry<FieldDescriptor, Object> entry :
getAllFields().entrySet()) {
final FieldDescriptor field = entry.getKey();
final Object value = entry.getValue();
if (isMessageSet && field.isExtension() &&
field.getType() == FieldDescriptor.Type.MESSAGE &&
!field.isRepeated()) {
size += CodedOutputStream.computeMessageSetExtensionSize(
field.getNumber(), (Message) value);
} else {
size += FieldSet.computeFieldSize(field, value);
}
}
final UnknownFieldSet unknownFields = getUnknownFields();
if (isMessageSet) {
size += unknownFields.getSerializedSizeAsMessageSet();
} else {
size += unknownFields.getSerializedSize();
}
memoizedSize = size;
return size;
memoizedSize = MessageReflection.getSerializedSize(this);
return memoizedSize;
}
@Override
@ -180,22 +107,93 @@ public abstract class AbstractMessage extends AbstractMessageLite
if (getDescriptorForType() != otherMessage.getDescriptorForType()) {
return false;
}
return getAllFields().equals(otherMessage.getAllFields()) &&
return compareFields(getAllFields(), otherMessage.getAllFields()) &&
getUnknownFields().equals(otherMessage.getUnknownFields());
}
@Override
public int hashCode() {
int hash = 41;
hash = (19 * hash) + getDescriptorForType().hashCode();
hash = hashFields(hash, getAllFields());
hash = (29 * hash) + getUnknownFields().hashCode();
int hash = memoizedHashCode;
if (hash == 0) {
hash = 41;
hash = (19 * hash) + getDescriptorForType().hashCode();
hash = hashFields(hash, getAllFields());
hash = (29 * hash) + getUnknownFields().hashCode();
memoizedHashCode = hash;
}
return hash;
}
private static ByteString toByteString(Object value) {
if (value instanceof byte[]) {
return ByteString.copyFrom((byte[]) value);
} else {
return (ByteString) value;
}
}
/**
* Compares two bytes fields. The parameters must be either a byte array or a
* ByteString object. They can be of different type though.
*/
private static boolean compareBytes(Object a, Object b) {
if (a instanceof byte[] && b instanceof byte[]) {
return Arrays.equals((byte[])a, (byte[])b);
}
return toByteString(a).equals(toByteString(b));
}
/**
* Compares two set of fields.
* This method is used to implement {@link AbstractMessage#equals(Object)}
* and {@link AbstractMutableMessage#equals(Object)}. It takes special care
* of bytes fields because immutable messages and mutable messages use
* different Java type to reprensent a bytes field and this method should be
* able to compare immutable messages, mutable messages and also an immutable
* message to a mutable message.
*/
static boolean compareFields(Map<FieldDescriptor, Object> a,
Map<FieldDescriptor, Object> b) {
if (a.size() != b.size()) {
return false;
}
for (FieldDescriptor descriptor : a.keySet()) {
if (!b.containsKey(descriptor)) {
return false;
}
Object value1 = a.get(descriptor);
Object value2 = b.get(descriptor);
if (descriptor.getType() == FieldDescriptor.Type.BYTES) {
if (descriptor.isRepeated()) {
List list1 = (List) value1;
List list2 = (List) value2;
if (list1.size() != list2.size()) {
return false;
}
for (int i = 0; i < list1.size(); i++) {
if (!compareBytes(list1.get(i), list2.get(i))) {
return false;
}
}
} else {
// Compares a singular bytes field.
if (!compareBytes(value1, value2)) {
return false;
}
}
} else {
// Compare non-bytes fields.
if (!value1.equals(value2)) {
return false;
}
}
}
return true;
}
/** Get a hash code for given fields and values, using the given seed. */
@SuppressWarnings("unchecked")
protected int hashFields(int hash, Map<FieldDescriptor, Object> map) {
protected static int hashFields(int hash, Map<FieldDescriptor, Object> map) {
for (Map.Entry<FieldDescriptor, Object> entry : map.entrySet()) {
FieldDescriptor field = entry.getKey();
Object value = entry.getValue();
@ -204,30 +202,14 @@ public abstract class AbstractMessage extends AbstractMessageLite
hash = (53 * hash) + value.hashCode();
} else if (field.isRepeated()) {
List<? extends EnumLite> list = (List<? extends EnumLite>) value;
hash = (53 * hash) + hashEnumList(list);
hash = (53 * hash) + Internal.hashEnumList(list);
} else {
hash = (53 * hash) + hashEnum((EnumLite) value);
hash = (53 * hash) + Internal.hashEnum((EnumLite) value);
}
}
return hash;
}
/**
* Helper method for implementing {@link Message#hashCode()}.
* @see Boolean#hashCode()
*/
protected static int hashLong(long n) {
return (int) (n ^ (n >>> 32));
}
/**
* Helper method for implementing {@link Message#hashCode()}.
* @see Boolean#hashCode()
*/
protected static int hashBoolean(boolean b) {
return b ? 1231 : 1237;
}
/**
* Package private helper method for AbstractParser to create
* UninitializedMessageException with missing field information.
@ -237,26 +219,6 @@ public abstract class AbstractMessage extends AbstractMessageLite
return Builder.newUninitializedMessageException(this);
}
/**
* Helper method for implementing {@link Message#hashCode()}.
* <p>
* This is needed because {@link java.lang.Enum#hashCode()} is final, but we
* need to use the field number as the hash code to ensure compatibility
* between statically and dynamically generated enum objects.
*/
protected static int hashEnum(EnumLite e) {
return e.getNumber();
}
/** Helper method for implementing {@link Message#hashCode()}. */
protected static int hashEnumList(List<? extends EnumLite> list) {
int hash = 1;
for (EnumLite e : list) {
hash = 31 * hash + hashEnum(e);
}
return hash;
}
// =================================================================
/**
@ -272,6 +234,25 @@ public abstract class AbstractMessage extends AbstractMessageLite
@Override
public abstract BuilderType clone();
/** TODO(jieluo): Clear it when all subclasses have implemented this method. */
@Override
public boolean hasOneof(OneofDescriptor oneof) {
throw new UnsupportedOperationException("hasOneof() is not implemented.");
}
/** TODO(jieluo): Clear it when all subclasses have implemented this method. */
@Override
public FieldDescriptor getOneofFieldDescriptor(OneofDescriptor oneof) {
throw new UnsupportedOperationException(
"getOneofFieldDescriptor() is not implemented.");
}
/** TODO(jieluo): Clear it when all subclasses have implemented this method. */
@Override
public BuilderType clearOneof(OneofDescriptor oneof) {
throw new UnsupportedOperationException("clearOneof() is not implemented.");
}
public BuilderType clear() {
for (final Map.Entry<FieldDescriptor, Object> entry :
getAllFields().entrySet()) {
@ -281,11 +262,11 @@ public abstract class AbstractMessage extends AbstractMessageLite
}
public List<String> findInitializationErrors() {
return findMissingFields(this);
return MessageReflection.findMissingFields(this);
}
public String getInitializationErrorString() {
return delimitWithCommas(findInitializationErrors());
return MessageReflection.delimitWithCommas(findInitializationErrors());
}
public BuilderType mergeFrom(final Message other) {
@ -350,8 +331,13 @@ public abstract class AbstractMessage extends AbstractMessageLite
break;
}
if (!mergeFieldFrom(input, unknownFields, extensionRegistry,
getDescriptorForType(), this, null, tag)) {
MessageReflection.BuilderAdapter builderAdapter =
new MessageReflection.BuilderAdapter(this);
if (!MessageReflection.mergeFieldFrom(input, unknownFields,
extensionRegistry,
getDescriptorForType(),
builderAdapter,
tag)) {
// end group tag
break;
}
@ -360,394 +346,6 @@ public abstract class AbstractMessage extends AbstractMessageLite
return (BuilderType) this;
}
/** helper method to handle {@code builder} and {@code extensions}. */
private static void addRepeatedField(
Message.Builder builder,
FieldSet<FieldDescriptor> extensions,
FieldDescriptor field,
Object value) {
if (builder != null) {
builder.addRepeatedField(field, value);
} else {
extensions.addRepeatedField(field, value);
}
}
/** helper method to handle {@code builder} and {@code extensions}. */
private static void setField(
Message.Builder builder,
FieldSet<FieldDescriptor> extensions,
FieldDescriptor field,
Object value) {
if (builder != null) {
builder.setField(field, value);
} else {
extensions.setField(field, value);
}
}
/** helper method to handle {@code builder} and {@code extensions}. */
private static boolean hasOriginalMessage(
Message.Builder builder,
FieldSet<FieldDescriptor> extensions,
FieldDescriptor field) {
if (builder != null) {
return builder.hasField(field);
} else {
return extensions.hasField(field);
}
}
/** helper method to handle {@code builder} and {@code extensions}. */
private static Message getOriginalMessage(
Message.Builder builder,
FieldSet<FieldDescriptor> extensions,
FieldDescriptor field) {
if (builder != null) {
return (Message) builder.getField(field);
} else {
return (Message) extensions.getField(field);
}
}
/** helper method to handle {@code builder} and {@code extensions}. */
private static void mergeOriginalMessage(
Message.Builder builder,
FieldSet<FieldDescriptor> extensions,
FieldDescriptor field,
Message.Builder subBuilder) {
Message originalMessage = getOriginalMessage(builder, extensions, field);
if (originalMessage != null) {
subBuilder.mergeFrom(originalMessage);
}
}
/**
* Like {@link #mergeFrom(CodedInputStream, ExtensionRegistryLite)}, but
* parses a single field.
*
* When {@code builder} is not null, the method will parse and merge the
* field into {@code builder}. Otherwise, it will try to parse the field
* into {@code extensions}, when it's called by the parsing constructor in
* generated classes.
*
* Package-private because it is used by GeneratedMessage.ExtendableMessage.
* @param tag The tag, which should have already been read.
* @return {@code true} unless the tag is an end-group tag.
*/
static boolean mergeFieldFrom(
CodedInputStream input,
UnknownFieldSet.Builder unknownFields,
ExtensionRegistryLite extensionRegistry,
Descriptor type,
Message.Builder builder,
FieldSet<FieldDescriptor> extensions,
int tag) throws IOException {
if (type.getOptions().getMessageSetWireFormat() &&
tag == WireFormat.MESSAGE_SET_ITEM_TAG) {
mergeMessageSetExtensionFromCodedStream(
input, unknownFields, extensionRegistry, type, builder, extensions);
return true;
}
final int wireType = WireFormat.getTagWireType(tag);
final int fieldNumber = WireFormat.getTagFieldNumber(tag);
final FieldDescriptor field;
Message defaultInstance = null;
if (type.isExtensionNumber(fieldNumber)) {
// extensionRegistry may be either ExtensionRegistry or
// ExtensionRegistryLite. Since the type we are parsing is a full
// message, only a full ExtensionRegistry could possibly contain
// extensions of it. Otherwise we will treat the registry as if it
// were empty.
if (extensionRegistry instanceof ExtensionRegistry) {
final ExtensionRegistry.ExtensionInfo extension =
((ExtensionRegistry) extensionRegistry)
.findExtensionByNumber(type, fieldNumber);
if (extension == null) {
field = null;
} else {
field = extension.descriptor;
defaultInstance = extension.defaultInstance;
if (defaultInstance == null &&
field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
throw new IllegalStateException(
"Message-typed extension lacked default instance: " +
field.getFullName());
}
}
} else {
field = null;
}
} else if (builder != null) {
field = type.findFieldByNumber(fieldNumber);
} else {
field = null;
}
boolean unknown = false;
boolean packed = false;
if (field == null) {
unknown = true; // Unknown field.
} else if (wireType == FieldSet.getWireFormatForFieldType(
field.getLiteType(),
false /* isPacked */)) {
packed = false;
} else if (field.isPackable() &&
wireType == FieldSet.getWireFormatForFieldType(
field.getLiteType(),
true /* isPacked */)) {
packed = true;
} else {
unknown = true; // Unknown wire type.
}
if (unknown) { // Unknown field or wrong wire type. Skip.
return unknownFields.mergeFieldFrom(tag, input);
}
if (packed) {
final int length = input.readRawVarint32();
final int limit = input.pushLimit(length);
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;
}
addRepeatedField(builder, extensions, field, value);
}
} else {
while (input.getBytesUntilLimit() > 0) {
final Object value =
FieldSet.readPrimitiveField(input, field.getLiteType());
addRepeatedField(builder, extensions, field, value);
}
}
input.popLimit(limit);
} else {
final Object value;
switch (field.getType()) {
case GROUP: {
final Message.Builder subBuilder;
if (defaultInstance != null) {
subBuilder = defaultInstance.newBuilderForType();
} else {
subBuilder = builder.newBuilderForField(field);
}
if (!field.isRepeated()) {
mergeOriginalMessage(builder, extensions, field, subBuilder);
}
input.readGroup(field.getNumber(), subBuilder, extensionRegistry);
value = subBuilder.buildPartial();
break;
}
case MESSAGE: {
final Message.Builder subBuilder;
if (defaultInstance != null) {
subBuilder = defaultInstance.newBuilderForType();
} else {
subBuilder = builder.newBuilderForField(field);
}
if (!field.isRepeated()) {
mergeOriginalMessage(builder, extensions, field, subBuilder);
}
input.readMessage(subBuilder, extensionRegistry);
value = subBuilder.buildPartial();
break;
}
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;
}
break;
default:
value = FieldSet.readPrimitiveField(input, field.getLiteType());
break;
}
if (field.isRepeated()) {
addRepeatedField(builder, extensions, field, value);
} else {
setField(builder, extensions, field, value);
}
}
return true;
}
/**
* Called by {@code #mergeFieldFrom()} to parse a MessageSet extension.
* If {@code builder} is not null, this method will merge MessageSet into
* the builder. Otherwise, it will merge the MessageSet into {@code
* extensions}.
*/
private static void mergeMessageSetExtensionFromCodedStream(
CodedInputStream input,
UnknownFieldSet.Builder unknownFields,
ExtensionRegistryLite extensionRegistry,
Descriptor type,
Message.Builder builder,
FieldSet<FieldDescriptor> extensions) throws IOException {
// The wire format for MessageSet is:
// message MessageSet {
// repeated group Item = 1 {
// required int32 typeId = 2;
// required bytes message = 3;
// }
// }
// "typeId" is the extension's field number. The extension can only be
// a message type, where "message" contains the encoded bytes of that
// message.
//
// In practice, we will probably never see a MessageSet item in which
// the message appears before the type ID, or where either field does not
// appear exactly once. However, in theory such cases are valid, so we
// should be prepared to accept them.
int typeId = 0;
ByteString rawBytes = null; // If we encounter "message" before "typeId"
ExtensionRegistry.ExtensionInfo extension = null;
// Read bytes from input, if we get it's type first then parse it eagerly,
// otherwise we store the raw bytes in a local variable.
while (true) {
final int tag = input.readTag();
if (tag == 0) {
break;
}
if (tag == WireFormat.MESSAGE_SET_TYPE_ID_TAG) {
typeId = input.readUInt32();
if (typeId != 0) {
// extensionRegistry may be either ExtensionRegistry or
// ExtensionRegistryLite. Since the type we are parsing is a full
// message, only a full ExtensionRegistry could possibly contain
// extensions of it. Otherwise we will treat the registry as if it
// were empty.
if (extensionRegistry instanceof ExtensionRegistry) {
extension = ((ExtensionRegistry) extensionRegistry)
.findExtensionByNumber(type, typeId);
}
}
} else if (tag == WireFormat.MESSAGE_SET_MESSAGE_TAG) {
if (typeId != 0) {
if (extension != null && ExtensionRegistryLite.isEagerlyParseMessageSets()) {
// We already know the type, so we can parse directly from the
// input with no copying. Hooray!
eagerlyMergeMessageSetExtension(
input, extension, extensionRegistry, builder, extensions);
rawBytes = null;
continue;
}
}
// We haven't seen a type ID yet or we want parse message lazily.
rawBytes = input.readBytes();
} else { // Unknown tag. Skip it.
if (!input.skipField(tag)) {
break; // End of group
}
}
}
input.checkLastTagWas(WireFormat.MESSAGE_SET_ITEM_END_TAG);
// Process the raw bytes.
if (rawBytes != null && typeId != 0) { // Zero is not a valid type ID.
if (extension != null) { // We known the type
mergeMessageSetExtensionFromBytes(
rawBytes, extension, extensionRegistry, builder, extensions);
} else { // We don't know how to parse this. Ignore it.
if (rawBytes != null) {
unknownFields.mergeField(typeId, UnknownFieldSet.Field.newBuilder()
.addLengthDelimited(rawBytes).build());
}
}
}
}
private static void eagerlyMergeMessageSetExtension(
CodedInputStream input,
ExtensionRegistry.ExtensionInfo extension,
ExtensionRegistryLite extensionRegistry,
Message.Builder builder,
FieldSet<FieldDescriptor> extensions) throws IOException {
FieldDescriptor field = extension.descriptor;
Message value = null;
if (hasOriginalMessage(builder, extensions, field)) {
Message originalMessage =
getOriginalMessage(builder, extensions, field);
Message.Builder subBuilder = originalMessage.toBuilder();
input.readMessage(subBuilder, extensionRegistry);
value = subBuilder.buildPartial();
} else {
value = input.readMessage(extension.defaultInstance.getParserForType(),
extensionRegistry);
}
if (builder != null) {
builder.setField(field, value);
} else {
extensions.setField(field, value);
}
}
private static void mergeMessageSetExtensionFromBytes(
ByteString rawBytes,
ExtensionRegistry.ExtensionInfo extension,
ExtensionRegistryLite extensionRegistry,
Message.Builder builder,
FieldSet<FieldDescriptor> extensions) throws IOException {
FieldDescriptor field = extension.descriptor;
boolean hasOriginalValue = hasOriginalMessage(builder, extensions, field);
if (hasOriginalValue || ExtensionRegistryLite.isEagerlyParseMessageSets()) {
// If the field already exists, we just parse the field.
Message value = null;
if (hasOriginalValue) {
Message originalMessage =
getOriginalMessage(builder, extensions, field);
Message.Builder subBuilder= originalMessage.toBuilder();
subBuilder.mergeFrom(rawBytes, extensionRegistry);
value = subBuilder.buildPartial();
} else {
value = extension.defaultInstance.getParserForType()
.parsePartialFrom(rawBytes, extensionRegistry);
}
setField(builder, extensions, field, value);
} else {
// Use LazyField to load MessageSet lazily.
LazyField lazyField = new LazyField(
extension.defaultInstance, extensionRegistry, rawBytes);
if (builder != null) {
// TODO(xiangl): it looks like this method can only be invoked by
// ExtendableBuilder, but I'm not sure. So I double check the type of
// builder here. It may be useless and need more investigation.
if (builder instanceof ExtendableBuilder) {
builder.setField(field, lazyField);
} else {
builder.setField(field, lazyField.getValue());
}
} else {
extensions.setField(field, lazyField);
}
}
}
public BuilderType mergeUnknownFields(final UnknownFieldSet unknownFields) {
setUnknownFields(
UnknownFieldSet.newBuilder(getUnknownFields())
@ -761,79 +359,18 @@ public abstract class AbstractMessage extends AbstractMessageLite
"getFieldBuilder() called on an unsupported message type.");
}
public String toString() {
return TextFormat.printToString(this);
}
/**
* Construct an UninitializedMessageException reporting missing fields in
* the given message.
*/
protected static UninitializedMessageException
newUninitializedMessageException(Message message) {
return new UninitializedMessageException(findMissingFields(message));
}
/**
* Populates {@code this.missingFields} with the full "path" of each
* missing required field in the given message.
*/
private static List<String> findMissingFields(
final MessageOrBuilder message) {
final List<String> results = new ArrayList<String>();
findMissingFields(message, "", results);
return results;
}
/** Recursive helper implementing {@link #findMissingFields(Message)}. */
private static void findMissingFields(final MessageOrBuilder message,
final String prefix,
final List<String> results) {
for (final FieldDescriptor field :
message.getDescriptorForType().getFields()) {
if (field.isRequired() && !message.hasField(field)) {
results.add(prefix + field.getName());
}
}
for (final Map.Entry<FieldDescriptor, Object> entry :
message.getAllFields().entrySet()) {
final FieldDescriptor field = entry.getKey();
final Object value = entry.getValue();
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
if (field.isRepeated()) {
int i = 0;
for (final Object element : (List) value) {
findMissingFields((MessageOrBuilder) element,
subMessagePrefix(prefix, field, i++),
results);
}
} else {
if (message.hasField(field)) {
findMissingFields((MessageOrBuilder) value,
subMessagePrefix(prefix, field, -1),
results);
}
}
}
}
}
private static String subMessagePrefix(final String prefix,
final FieldDescriptor field,
final int index) {
final StringBuilder result = new StringBuilder(prefix);
if (field.isExtension()) {
result.append('(')
.append(field.getFullName())
.append(')');
} else {
result.append(field.getName());
}
if (index != -1) {
result.append('[')
.append(index)
.append(']');
}
result.append('.');
return result.toString();
return new UninitializedMessageException(
MessageReflection.findMissingFields(message));
}
// ===============================================================
@ -925,6 +462,5 @@ public abstract class AbstractMessage extends AbstractMessageLite
throws IOException {
return super.mergeDelimitedFrom(input, extensionRegistry);
}
}
}

@ -44,6 +44,8 @@ import java.util.Collection;
* @author kenton@google.com Kenton Varda
*/
public abstract class AbstractMessageLite implements MessageLite {
protected int memoizedHashCode = 0;
public ByteString toByteString() {
try {
final ByteString.CodedBuilder out =
@ -91,6 +93,7 @@ public abstract class AbstractMessageLite implements MessageLite {
codedOutput.flush();
}
/**
* Package private helper method for AbstractParser to create
* UninitializedMessageException.
@ -99,6 +102,13 @@ public abstract class AbstractMessageLite implements MessageLite {
return new UninitializedMessageException(this);
}
protected static void checkByteStringIsUtf8(ByteString byteString)
throws IllegalArgumentException {
if (!byteString.isValidUtf8()) {
throw new IllegalArgumentException("Byte string is not UTF-8.");
}
}
/**
* A partial implementation of the {@link Message.Builder} interface which
* implements as many methods of that interface as possible in terms of
@ -311,7 +321,8 @@ public abstract class AbstractMessageLite implements MessageLite {
* used by generated code. Users should ignore it.
*
* @throws NullPointerException if any of the elements of {@code values} is
* null.
* null. When that happens, some elements of {@code values} may have already
* been added to the result {@code list}.
*/
protected static <T> void addAll(final Iterable<T> values,
final Collection<? super T> list) {
@ -319,14 +330,15 @@ public abstract class AbstractMessageLite implements MessageLite {
// For StringOrByteStringLists, check the underlying elements to avoid
// forcing conversions of ByteStrings to Strings.
checkForNullValues(((LazyStringList) values).getUnderlyingElements());
} else {
list.addAll((Collection<T>) values);
} else if (values instanceof Collection) {
checkForNullValues(values);
}
if (values instanceof Collection) {
final Collection<T> collection = (Collection<T>) values;
list.addAll(collection);
list.addAll((Collection<T>) values);
} else {
for (final T value : values) {
if (value == null) {
throw new NullPointerException();
}
list.add(value);
}
}

@ -110,10 +110,6 @@ public abstract class AbstractParser<MessageType extends MessageLite>
return message;
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (IOException e) {
throw new RuntimeException(
"Reading from a ByteString threw an IOException (should " +
"never happen).", e);
}
}
@ -147,10 +143,6 @@ public abstract class AbstractParser<MessageType extends MessageLite>
return message;
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (IOException e) {
throw new RuntimeException(
"Reading from a byte array threw an IOException (should " +
"never happen).", e);
}
}

@ -177,6 +177,20 @@ public abstract class ByteString implements Iterable<Byte> {
substring(0, prefix.size()).equals(prefix);
}
/**
* Tests if this bytestring ends with the specified suffix.
* Similar to {@link String#endsWith(String)}
*
* @param suffix the suffix.
* @return <code>true</code> if the byte sequence represented by the
* argument is a suffix of the byte sequence represented by
* this string; <code>false</code> otherwise.
*/
public boolean endsWith(ByteString suffix) {
return size() >= suffix.size() &&
substring(size() - suffix.size()).equals(suffix);
}
// =================================================================
// byte[] -> ByteString
@ -512,6 +526,9 @@ public abstract class ByteString implements Iterable<Byte> {
*/
public byte[] toByteArray() {
int size = size();
if (size == 0) {
return Internal.EMPTY_BYTE_ARRAY;
}
byte[] result = new byte[size];
copyToInternal(result, 0, 0, size);
return result;
@ -525,6 +542,41 @@ public abstract class ByteString implements Iterable<Byte> {
* @throws IOException if an I/O error occurs.
*/
public abstract void writeTo(OutputStream out) throws IOException;
/**
* Writes a specified part of this byte string to an output stream.
*
* @param out the output stream to which to write the data.
* @param sourceOffset offset within these bytes
* @param numberToWrite number of bytes to write
* @throws IOException if an I/O error occurs.
* @throws IndexOutOfBoundsException if an offset or size is negative or too
* large
*/
void writeTo(OutputStream out, int sourceOffset, int numberToWrite)
throws IOException {
if (sourceOffset < 0) {
throw new IndexOutOfBoundsException("Source offset < 0: " + sourceOffset);
}
if (numberToWrite < 0) {
throw new IndexOutOfBoundsException("Length < 0: " + numberToWrite);
}
if (sourceOffset + numberToWrite > size()) {
throw new IndexOutOfBoundsException(
"Source end offset exceeded: " + (sourceOffset + numberToWrite));
}
if (numberToWrite > 0) {
writeToInternal(out, sourceOffset, numberToWrite);
}
}
/**
* Internal version of {@link #writeTo(OutputStream,int,int)} that assumes
* all error checking has already been done.
*/
abstract void writeToInternal(OutputStream out, int sourceOffset,
int numberToWrite) throws IOException;
/**
* Constructs a read-only {@code java.nio.ByteBuffer} whose content

@ -30,9 +30,12 @@
package com.google.protobuf;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
@ -88,6 +91,53 @@ public final class CodedInputStream {
return result;
}
/**
* Create a new CodedInputStream wrapping the given ByteBuffer. The data
* starting from the ByteBuffer's current position to its limit will be read.
* The returned CodedInputStream may or may not share the underlying data
* in the ByteBuffer, therefore the ByteBuffer cannot be changed while the
* CodedInputStream is in use.
* Note that the ByteBuffer's position won't be changed by this function.
* Concurrent calls with the same ByteBuffer object are safe if no other
* thread is trying to alter the ByteBuffer's status.
*/
public static CodedInputStream newInstance(ByteBuffer buf) {
if (buf.hasArray()) {
return newInstance(buf.array(), buf.arrayOffset() + buf.position(),
buf.remaining());
} else {
ByteBuffer temp = buf.duplicate();
byte[] buffer = new byte[temp.remaining()];
temp.get(buffer);
return newInstance(buffer);
}
}
/**
* Create a new CodedInputStream wrapping a LiteralByteString.
*/
static CodedInputStream newInstance(LiteralByteString byteString) {
CodedInputStream result = new CodedInputStream(byteString);
try {
// Some uses of CodedInputStream can be more efficient if they know
// exactly how many bytes are available. By pushing the end point of the
// buffer as a limit, we allow them to get this information via
// getBytesUntilLimit(). Pushing a limit that we know is at the end of
// the stream can never hurt, since we can never past that point anyway.
result.pushLimit(byteString.size());
} catch (InvalidProtocolBufferException ex) {
// The only reason pushLimit() might throw an exception here is if len
// is negative. Normally pushLimit()'s parameter comes directly off the
// wire, so it's important to catch exceptions in case of corrupt or
// malicious data. However, in this case, we expect that len is not a
// user-supplied value, so we can assume that it being negative indicates
// a programming error. Therefore, throwing an unchecked exception is
// appropriate.
throw new IllegalArgumentException(ex);
}
return result;
}
// -----------------------------------------------------------------
/**
@ -125,6 +175,10 @@ public final class CodedInputStream {
}
}
public int getLastTag() {
return lastTag;
}
/**
* Reads and discards a single field, given its tag value.
*
@ -134,10 +188,10 @@ public final class CodedInputStream {
public boolean skipField(final int tag) throws IOException {
switch (WireFormat.getTagWireType(tag)) {
case WireFormat.WIRETYPE_VARINT:
readInt32();
skipRawVarint();
return true;
case WireFormat.WIRETYPE_FIXED64:
readRawLittleEndian64();
skipRawBytes(8);
return true;
case WireFormat.WIRETYPE_LENGTH_DELIMITED:
skipRawBytes(readRawVarint32());
@ -151,8 +205,59 @@ public final class CodedInputStream {
case WireFormat.WIRETYPE_END_GROUP:
return false;
case WireFormat.WIRETYPE_FIXED32:
readRawLittleEndian32();
skipRawBytes(4);
return true;
default:
throw InvalidProtocolBufferException.invalidWireType();
}
}
/**
* Reads a single field and writes it to output in wire format,
* given its tag value.
*
* @return {@code false} if the tag is an endgroup tag, in which case
* nothing is skipped. Otherwise, returns {@code true}.
*/
public boolean skipField(final int tag, final CodedOutputStream output)
throws IOException {
switch (WireFormat.getTagWireType(tag)) {
case WireFormat.WIRETYPE_VARINT: {
long value = readInt64();
output.writeRawVarint32(tag);
output.writeUInt64NoTag(value);
return true;
}
case WireFormat.WIRETYPE_FIXED64: {
long value = readRawLittleEndian64();
output.writeRawVarint32(tag);
output.writeFixed64NoTag(value);
return true;
}
case WireFormat.WIRETYPE_LENGTH_DELIMITED: {
ByteString value = readBytes();
output.writeRawVarint32(tag);
output.writeBytesNoTag(value);
return true;
}
case WireFormat.WIRETYPE_START_GROUP: {
output.writeRawVarint32(tag);
skipMessage(output);
int endtag = WireFormat.makeTag(WireFormat.getTagFieldNumber(tag),
WireFormat.WIRETYPE_END_GROUP);
checkLastTagWas(endtag);
output.writeRawVarint32(endtag);
return true;
}
case WireFormat.WIRETYPE_END_GROUP: {
return false;
}
case WireFormat.WIRETYPE_FIXED32: {
int value = readRawLittleEndian32();
output.writeRawVarint32(tag);
output.writeFixed32NoTag(value);
return true;
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
@ -171,6 +276,51 @@ public final class CodedInputStream {
}
}
/**
* Reads an entire message and writes it to output in wire format.
* This will read either until EOF or until an endgroup tag,
* whichever comes first.
*/
public void skipMessage(CodedOutputStream output) throws IOException {
while (true) {
final int tag = readTag();
if (tag == 0 || !skipField(tag, output)) {
return;
}
}
}
/**
* Collects the bytes skipped and returns the data in a ByteBuffer.
*/
private class SkippedDataSink implements RefillCallback {
private int lastPos = bufferPos;
private ByteArrayOutputStream byteArrayStream;
@Override
public void onRefill() {
if (byteArrayStream == null) {
byteArrayStream = new ByteArrayOutputStream();
}
byteArrayStream.write(buffer, lastPos, bufferPos - lastPos);
lastPos = 0;
}
/**
* Gets skipped data in a ByteBuffer. This method should only be
* called once.
*/
ByteBuffer getSkippedData() {
if (byteArrayStream == null) {
return ByteBuffer.wrap(buffer, lastPos, bufferPos - lastPos);
} else {
byteArrayStream.write(buffer, lastPos, bufferPos);
return ByteBuffer.wrap(byteArrayStream.toByteArray());
}
}
}
// -----------------------------------------------------------------
/** Read a {@code double} field value from the stream. */
@ -210,10 +360,14 @@ public final class CodedInputStream {
/** Read a {@code bool} field value from the stream. */
public boolean readBool() throws IOException {
return readRawVarint32() != 0;
return readRawVarint64() != 0;
}
/** Read a {@code string} field value from the stream. */
/**
* Read a {@code string} field value from the stream.
* If the stream contains malformed UTF-8,
* replace the offending bytes with the standard UTF-8 replacement character.
*/
public String readString() throws IOException {
final int size = readRawVarint32();
if (size <= (bufferSize - bufferPos) && size > 0) {
@ -222,10 +376,40 @@ public final class CodedInputStream {
final String result = new String(buffer, bufferPos, size, "UTF-8");
bufferPos += size;
return result;
} else if (size == 0) {
return "";
} else {
// Slow path: Build a byte array first then copy it.
return new String(readRawBytesSlowPath(size), "UTF-8");
}
}
/**
* Read a {@code string} field value from the stream.
* If the stream contains malformed UTF-8,
* throw exception {@link InvalidProtocolBufferException}.
*/
public String readStringRequireUtf8() throws IOException {
final int size = readRawVarint32();
final byte[] bytes;
int pos = bufferPos;
if (size <= (bufferSize - pos) && size > 0) {
// Fast path: We already have the bytes in a contiguous buffer, so
// just copy directly from it.
bytes = buffer;
bufferPos = pos + size;
} else if (size == 0) {
return "";
} else {
// Slow path: Build a byte array first then copy it.
return new String(readRawBytes(size), "UTF-8");
bytes = readRawBytesSlowPath(size);
pos = 0;
}
// TODO(martinrb): We could save a pass by validating while decoding.
if (!Utf8.isValidUtf8(bytes, pos, pos + size)) {
throw InvalidProtocolBufferException.invalidUtf8();
}
return new String(bytes, pos, size, "UTF-8");
}
/** Read a {@code group} field value from the stream. */
@ -243,6 +427,7 @@ public final class CodedInputStream {
--recursionDepth;
}
/** Read a {@code group} field value from the stream. */
public <T extends MessageLite> T readGroup(
final int fieldNumber,
@ -295,6 +480,7 @@ public final class CodedInputStream {
popLimit(oldLimit);
}
/** Read an embedded message field value from the stream. */
public <T extends MessageLite> T readMessage(
final Parser<T> parser,
@ -316,17 +502,58 @@ public final class CodedInputStream {
/** Read a {@code bytes} field value from the stream. */
public ByteString readBytes() throws IOException {
final int size = readRawVarint32();
if (size == 0) {
return ByteString.EMPTY;
} else if (size <= (bufferSize - bufferPos) && size > 0) {
if (size <= (bufferSize - bufferPos) && size > 0) {
// Fast path: We already have the bytes in a contiguous buffer, so
// just copy directly from it.
final ByteString result = ByteString.copyFrom(buffer, bufferPos, size);
final ByteString result = bufferIsImmutable && enableAliasing
? new BoundedByteString(buffer, bufferPos, size)
: ByteString.copyFrom(buffer, bufferPos, size);
bufferPos += size;
return result;
} else if (size == 0) {
return ByteString.EMPTY;
} else {
// Slow path: Build a byte array first then copy it.
return ByteString.copyFrom(readRawBytes(size));
return new LiteralByteString(readRawBytesSlowPath(size));
}
}
/** Read a {@code bytes} field value from the stream. */
public byte[] readByteArray() throws IOException {
final int size = readRawVarint32();
if (size <= (bufferSize - bufferPos) && size > 0) {
// Fast path: We already have the bytes in a contiguous buffer, so
// just copy directly from it.
final byte[] result =
Arrays.copyOfRange(buffer, bufferPos, bufferPos + size);
bufferPos += size;
return result;
} else {
// Slow path: Build a byte array first then copy it.
return readRawBytesSlowPath(size);
}
}
/** Read a {@code bytes} field value from the stream. */
public ByteBuffer readByteBuffer() throws IOException {
final int size = readRawVarint32();
if (size <= (bufferSize - bufferPos) && size > 0) {
// Fast path: We already have the bytes in a contiguous buffer.
// When aliasing is enabled, we can return a ByteBuffer pointing directly
// into the underlying byte array without copy if the CodedInputStream is
// constructed from a byte array. If aliasing is disabled or the input is
// from an InputStream or ByteString, we have to make a copy of the bytes.
ByteBuffer result = input == null && !bufferIsImmutable && enableAliasing
? ByteBuffer.wrap(buffer, bufferPos, size).slice()
: ByteBuffer.wrap(Arrays.copyOfRange(
buffer, bufferPos, bufferPos + size));
bufferPos += size;
return result;
} else if (size == 0) {
return Internal.EMPTY_BYTE_BUFFER;
} else {
// Slow path: Build a byte array first then copy it.
return ByteBuffer.wrap(readRawBytesSlowPath(size));
}
}
@ -370,37 +597,67 @@ public final class CodedInputStream {
* upper bits.
*/
public int readRawVarint32() throws IOException {
byte tmp = readRawByte();
if (tmp >= 0) {
return tmp;
}
int result = tmp & 0x7f;
if ((tmp = readRawByte()) >= 0) {
result |= tmp << 7;
} else {
result |= (tmp & 0x7f) << 7;
if ((tmp = readRawByte()) >= 0) {
result |= tmp << 14;
// See implementation notes for readRawVarint64
fastpath: {
int pos = bufferPos;
if (bufferSize == pos) {
break fastpath;
}
final byte[] buffer = this.buffer;
int x;
if ((x = buffer[pos++]) >= 0) {
bufferPos = pos;
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 {
result |= (tmp & 0x7f) << 14;
if ((tmp = readRawByte()) >= 0) {
result |= tmp << 21;
} else {
result |= (tmp & 0x7f) << 21;
result |= (tmp = readRawByte()) << 28;
if (tmp < 0) {
// Discard upper 32 bits.
for (int i = 0; i < 5; i++) {
if (readRawByte() >= 0) {
return result;
}
}
throw InvalidProtocolBufferException.malformedVarint();
}
int y = buffer[pos++];
x ^= y << 28;
x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28);
if (y < 0 &&
buffer[pos++] < 0 &&
buffer[pos++] < 0 &&
buffer[pos++] < 0 &&
buffer[pos++] < 0 &&
buffer[pos++] < 0) {
break fastpath; // Will throw malformedVarint()
}
}
bufferPos = pos;
return x;
}
return result;
return (int) readRawVarint64SlowPath();
}
private void skipRawVarint() throws IOException {
if (bufferSize - bufferPos >= 10) {
final byte[] buffer = this.buffer;
int pos = bufferPos;
for (int i = 0; i < 10; i++) {
if (buffer[pos++] >= 0) {
bufferPos = pos;
return;
}
}
}
skipRawVarintSlowPath();
}
private void skipRawVarintSlowPath() throws IOException {
for (int i = 0; i < 10; i++) {
if (readRawByte() >= 0) {
return;
}
}
throw InvalidProtocolBufferException.malformedVarint();
}
/**
@ -456,49 +713,115 @@ public final class CodedInputStream {
/** Read a raw Varint from the stream. */
public long readRawVarint64() throws IOException {
int shift = 0;
// Implementation notes:
//
// Optimized for one-byte values, expected to be common.
// The particular code below was selected from various candidates
// empirically, by winning VarintBenchmark.
//
// Sign extension of (signed) Java bytes is usually a nuisance, but
// we exploit it here to more easily obtain the sign of bytes read.
// Instead of cleaning up the sign extension bits by masking eagerly,
// we delay until we find the final (positive) byte, when we clear all
// accumulated bits with one xor. We depend on javac to constant fold.
fastpath: {
int pos = bufferPos;
if (bufferSize == pos) {
break fastpath;
}
final byte[] buffer = this.buffer;
long x;
int y;
if ((y = buffer[pos++]) >= 0) {
bufferPos = pos;
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) {
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);
} else if ((x ^= ((long) buffer[pos++] << 42)) >= 0L) {
x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42);
} else if ((x ^= ((long) buffer[pos++] << 49)) < 0L) {
x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42)
^ (~0L << 49);
} else {
x ^= ((long) buffer[pos++] << 56);
x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42)
^ (~0L << 49) ^ (~0L << 56);
if (x < 0L) {
if (buffer[pos++] < 0L) {
break fastpath; // Will throw malformedVarint()
}
}
}
bufferPos = pos;
return x;
}
return readRawVarint64SlowPath();
}
/** Variant of readRawVarint64 for when uncomfortably close to the limit. */
/* Visible for testing */
long readRawVarint64SlowPath() throws IOException {
long result = 0;
while (shift < 64) {
for (int shift = 0; shift < 64; shift += 7) {
final byte b = readRawByte();
result |= (long)(b & 0x7F) << shift;
result |= (long) (b & 0x7F) << shift;
if ((b & 0x80) == 0) {
return result;
}
shift += 7;
}
throw InvalidProtocolBufferException.malformedVarint();
}
/** Read a 32-bit little-endian integer from the stream. */
public int readRawLittleEndian32() throws IOException {
final byte b1 = readRawByte();
final byte b2 = readRawByte();
final byte b3 = readRawByte();
final byte b4 = readRawByte();
return (((int)b1 & 0xff) ) |
(((int)b2 & 0xff) << 8) |
(((int)b3 & 0xff) << 16) |
(((int)b4 & 0xff) << 24);
int pos = bufferPos;
// hand-inlined ensureAvailable(4);
if (bufferSize - pos < 4) {
refillBuffer(4);
pos = bufferPos;
}
final byte[] buffer = this.buffer;
bufferPos = pos + 4;
return (((buffer[pos] & 0xff)) |
((buffer[pos + 1] & 0xff) << 8) |
((buffer[pos + 2] & 0xff) << 16) |
((buffer[pos + 3] & 0xff) << 24));
}
/** Read a 64-bit little-endian integer from the stream. */
public long readRawLittleEndian64() throws IOException {
final byte b1 = readRawByte();
final byte b2 = readRawByte();
final byte b3 = readRawByte();
final byte b4 = readRawByte();
final byte b5 = readRawByte();
final byte b6 = readRawByte();
final byte b7 = readRawByte();
final byte b8 = readRawByte();
return (((long)b1 & 0xff) ) |
(((long)b2 & 0xff) << 8) |
(((long)b3 & 0xff) << 16) |
(((long)b4 & 0xff) << 24) |
(((long)b5 & 0xff) << 32) |
(((long)b6 & 0xff) << 40) |
(((long)b7 & 0xff) << 48) |
(((long)b8 & 0xff) << 56);
int pos = bufferPos;
// hand-inlined ensureAvailable(8);
if (bufferSize - pos < 8) {
refillBuffer(8);
pos = bufferPos;
}
final byte[] buffer = this.buffer;
bufferPos = pos + 8;
return ((((long) buffer[pos] & 0xffL)) |
(((long) buffer[pos + 1] & 0xffL) << 8) |
(((long) buffer[pos + 2] & 0xffL) << 16) |
(((long) buffer[pos + 3] & 0xffL) << 24) |
(((long) buffer[pos + 4] & 0xffL) << 32) |
(((long) buffer[pos + 5] & 0xffL) << 40) |
(((long) buffer[pos + 6] & 0xffL) << 48) |
(((long) buffer[pos + 7] & 0xffL) << 56));
}
/**
@ -532,11 +855,13 @@ public final class CodedInputStream {
// -----------------------------------------------------------------
private final byte[] buffer;
private final boolean bufferIsImmutable;
private int bufferSize;
private int bufferSizeAfterLimit;
private int bufferPos;
private final InputStream input;
private int lastTag;
private boolean enableAliasing = false;
/**
* The total number of bytes read before the current buffer. The total
@ -567,6 +892,7 @@ public final class CodedInputStream {
bufferPos = off;
totalBytesRetired = -off;
input = null;
bufferIsImmutable = false;
}
private CodedInputStream(final InputStream input) {
@ -575,6 +901,20 @@ public final class CodedInputStream {
bufferPos = 0;
totalBytesRetired = 0;
this.input = input;
bufferIsImmutable = false;
}
private CodedInputStream(final LiteralByteString byteString) {
buffer = byteString.bytes;
bufferPos = byteString.getOffsetIntoBytes();
bufferSize = bufferPos + byteString.size();
totalBytesRetired = -bufferPos;
input = null;
bufferIsImmutable = true;
}
public void enableAliasing(boolean enabled) {
this.enableAliasing = enabled;
}
/**
@ -698,7 +1038,7 @@ public final class CodedInputStream {
* if the stream has reached a limit created using {@link #pushLimit(int)}.
*/
public boolean isAtEnd() throws IOException {
return bufferPos == bufferSize && !refillBuffer(false);
return bufferPos == bufferSize && !tryRefillBuffer(1);
}
/**
@ -709,53 +1049,93 @@ public final class CodedInputStream {
return totalBytesRetired + bufferPos;
}
private interface RefillCallback {
void onRefill();
}
private RefillCallback refillCallback = null;
/**
* Ensures that at least {@code n} bytes are available in the buffer, reading
* more bytes from the input if necessary to make it so. Caller must ensure
* that the requested space is less than BUFFER_SIZE.
*
* @throws InvalidProtocolBufferException The end of the stream or the current
* limit was reached.
*/
private void ensureAvailable(int n) throws IOException {
if (bufferSize - bufferPos < n) {
refillBuffer(n);
}
}
/**
* Called with {@code this.buffer} is empty to read more bytes from the
* input. If {@code mustSucceed} is true, refillBuffer() guarantees that
* either there will be at least one byte in the buffer when it returns
* or it will throw an exception. If {@code mustSucceed} is false,
* refillBuffer() returns false if no more bytes were available.
* Reads more bytes from the input, making at least {@code n} bytes available
* in the buffer. Caller must ensure that the requested space is not yet
* available, and that the requested space is less than BUFFER_SIZE.
*
* @throws InvalidProtocolBufferException The end of the stream or the current
* limit was reached.
*/
private boolean refillBuffer(final boolean mustSucceed) throws IOException {
if (bufferPos < bufferSize) {
private void refillBuffer(int n) throws IOException {
if (!tryRefillBuffer(n)) {
throw InvalidProtocolBufferException.truncatedMessage();
}
}
/**
* Tries to read more bytes from the input, making at least {@code n} bytes
* available in the buffer. Caller must ensure that the requested space is
* not yet available, and that the requested space is less than BUFFER_SIZE.
*
* @return {@code true} if the bytes could be made available; {@code false}
* if the end of the stream or the current limit was reached.
*/
private boolean tryRefillBuffer(int n) throws IOException {
if (bufferPos + n <= bufferSize) {
throw new IllegalStateException(
"refillBuffer() called when buffer wasn't empty.");
"refillBuffer() called when " + n +
" bytes were already available in buffer");
}
if (totalBytesRetired + bufferSize == currentLimit) {
if (totalBytesRetired + bufferPos + n > currentLimit) {
// Oops, we hit a limit.
if (mustSucceed) {
throw InvalidProtocolBufferException.truncatedMessage();
} else {
return false;
}
return false;
}
totalBytesRetired += bufferSize;
bufferPos = 0;
bufferSize = (input == null) ? -1 : input.read(buffer);
if (bufferSize == 0 || bufferSize < -1) {
throw new IllegalStateException(
"InputStream#read(byte[]) returned invalid result: " + bufferSize +
"\nThe InputStream implementation is buggy.");
if (refillCallback != null) {
refillCallback.onRefill();
}
if (bufferSize == -1) {
bufferSize = 0;
if (mustSucceed) {
throw InvalidProtocolBufferException.truncatedMessage();
} else {
return false;
if (input != null) {
int pos = bufferPos;
if (pos > 0) {
if (bufferSize > pos) {
System.arraycopy(buffer, pos, buffer, 0, bufferSize - pos);
}
totalBytesRetired += pos;
bufferSize -= pos;
bufferPos = 0;
}
} else {
recomputeBufferSizeAfterLimit();
final int totalBytesRead =
totalBytesRetired + bufferSize + bufferSizeAfterLimit;
if (totalBytesRead > sizeLimit || totalBytesRead < 0) {
throw InvalidProtocolBufferException.sizeLimitExceeded();
int bytesRead = input.read(buffer, bufferSize, buffer.length - bufferSize);
if (bytesRead == 0 || bytesRead < -1 || bytesRead > buffer.length) {
throw new IllegalStateException(
"InputStream#read(byte[]) returned invalid result: " + bytesRead +
"\nThe InputStream implementation is buggy.");
}
if (bytesRead > 0) {
bufferSize += bytesRead;
// Integer-overflow-conscious check against sizeLimit
if (totalBytesRetired + n - sizeLimit > 0) {
throw InvalidProtocolBufferException.sizeLimitExceeded();
}
recomputeBufferSizeAfterLimit();
return (bufferSize >= n) ? true : tryRefillBuffer(n);
}
return true;
}
return false;
}
/**
@ -766,7 +1146,7 @@ public final class CodedInputStream {
*/
public byte readRawByte() throws IOException {
if (bufferPos == bufferSize) {
refillBuffer(true);
refillBuffer(1);
}
return buffer[bufferPos++];
}
@ -778,8 +1158,26 @@ public final class CodedInputStream {
* limit was reached.
*/
public byte[] readRawBytes(final int size) throws IOException {
if (size < 0) {
throw InvalidProtocolBufferException.negativeSize();
final int pos = bufferPos;
if (size <= (bufferSize - pos) && size > 0) {
bufferPos = pos + size;
return Arrays.copyOfRange(buffer, pos, pos + size);
} else {
return readRawBytesSlowPath(size);
}
}
/**
* Exactly like readRawBytes, but caller must have already checked the fast
* path: (size <= (bufferSize - pos) && size > 0)
*/
private byte[] readRawBytesSlowPath(final int size) throws IOException {
if (size <= 0) {
if (size == 0) {
return Internal.EMPTY_BYTE_ARRAY;
} else {
throw InvalidProtocolBufferException.negativeSize();
}
}
if (totalBytesRetired + bufferPos + size > currentLimit) {
@ -789,13 +1187,7 @@ public final class CodedInputStream {
throw InvalidProtocolBufferException.truncatedMessage();
}
if (size <= bufferSize - bufferPos) {
// We have all the bytes we need already.
final byte[] bytes = new byte[size];
System.arraycopy(buffer, bufferPos, bytes, 0, size);
bufferPos += size;
return bytes;
} else if (size < BUFFER_SIZE) {
if (size < BUFFER_SIZE) {
// Reading more bytes than are in the buffer, but not an excessive number
// of bytes. We can safely allocate the resulting array ahead of time.
@ -805,18 +1197,10 @@ public final class CodedInputStream {
System.arraycopy(buffer, bufferPos, bytes, 0, pos);
bufferPos = bufferSize;
// We want to use refillBuffer() and then copy from the buffer into our
// We want to refill the buffer and then copy from the buffer into our
// byte array rather than reading directly into our byte array because
// the input may be unbuffered.
refillBuffer(true);
while (size - pos > bufferSize) {
System.arraycopy(buffer, 0, bytes, pos, bufferSize);
pos += bufferSize;
bufferPos = bufferSize;
refillBuffer(true);
}
ensureAvailable(size - pos);
System.arraycopy(buffer, 0, bytes, pos, size - pos);
bufferPos = size - pos;
@ -885,6 +1269,19 @@ public final class CodedInputStream {
* limit was reached.
*/
public void skipRawBytes(final int size) throws IOException {
if (size <= (bufferSize - bufferPos) && size >= 0) {
// We have all the bytes we need already.
bufferPos += size;
} else {
skipRawBytesSlowPath(size);
}
}
/**
* Exactly like skipRawBytes, but caller must have already checked the fast
* path: (size <= (bufferSize - pos) && size >= 0)
*/
private void skipRawBytesSlowPath(final int size) throws IOException {
if (size < 0) {
throw InvalidProtocolBufferException.negativeSize();
}
@ -896,25 +1293,19 @@ public final class CodedInputStream {
throw InvalidProtocolBufferException.truncatedMessage();
}
if (size <= bufferSize - bufferPos) {
// We have all the bytes we need already.
bufferPos += size;
} else {
// Skipping more bytes than are in the buffer. First skip what we have.
int pos = bufferSize - bufferPos;
bufferPos = bufferSize;
// Keep refilling the buffer until we get to the point we wanted to skip
// to. This has the side effect of ensuring the limits are updated
// correctly.
refillBuffer(true);
while (size - pos > bufferSize) {
pos += bufferSize;
bufferPos = bufferSize;
refillBuffer(true);
}
// Skipping more bytes than are in the buffer. First skip what we have.
int pos = bufferSize - bufferPos;
bufferPos = bufferSize;
bufferPos = size - pos;
// Keep refilling the buffer until we get to the point we wanted to skip to.
// This has the side effect of ensuring the limits are updated correctly.
refillBuffer(1);
while (size - pos > bufferSize) {
pos += bufferSize;
bufferPos = bufferSize;
refillBuffer(1);
}
bufferPos = size - pos;
}
}

@ -31,9 +31,9 @@
package com.google.protobuf;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
/**
* Encodes and writes protocol message fields.
@ -53,6 +53,7 @@ public final class CodedOutputStream {
private final byte[] buffer;
private final int limit;
private int position;
private int totalBytesWritten = 0;
private final OutputStream output;
@ -129,6 +130,38 @@ public final class CodedOutputStream {
return new CodedOutputStream(flatArray, offset, length);
}
/**
* Create a new {@code CodedOutputStream} that writes to the given ByteBuffer.
*/
public static CodedOutputStream newInstance(ByteBuffer byteBuffer) {
return newInstance(byteBuffer, DEFAULT_BUFFER_SIZE);
}
/**
* Create a new {@code CodedOutputStream} that writes to the given ByteBuffer.
*/
public static CodedOutputStream newInstance(ByteBuffer byteBuffer,
int bufferSize) {
return newInstance(new ByteBufferOutputStream(byteBuffer), bufferSize);
}
private static class ByteBufferOutputStream extends OutputStream {
private final ByteBuffer byteBuffer;
public ByteBufferOutputStream(ByteBuffer byteBuffer) {
this.byteBuffer = byteBuffer;
}
@Override
public void write(int b) throws IOException {
byteBuffer.put((byte) b);
}
@Override
public void write(byte[] data, int offset, int length) throws IOException {
byteBuffer.put(data, offset, length);
}
}
// -----------------------------------------------------------------
/** Write a {@code double} field, including tag, to the stream. */
@ -202,6 +235,7 @@ public final class CodedOutputStream {
writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP);
}
/**
* Write a group represented by an {@link UnknownFieldSet}.
*
@ -222,6 +256,7 @@ public final class CodedOutputStream {
writeMessageNoTag(value);
}
/** Write a {@code bytes} field, including tag, to the stream. */
public void writeBytes(final int fieldNumber, final ByteString value)
throws IOException {
@ -229,6 +264,39 @@ public final class CodedOutputStream {
writeBytesNoTag(value);
}
/** Write a {@code bytes} field, including tag, to the stream. */
public void writeByteArray(final int fieldNumber, final byte[] value)
throws IOException {
writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
writeByteArrayNoTag(value);
}
/** Write a {@code bytes} field, including tag, to the stream. */
public void writeByteArray(final int fieldNumber,
final byte[] value,
final int offset,
final int length)
throws IOException {
writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
writeByteArrayNoTag(value, offset, length);
}
/**
* Write a {@code bytes} field, including tag, to the stream.
* This method will write all content of the ByteBuffer regardless of the
* current position and limit (i.e., the number of bytes to be written is
* value.capacity(), not value.remaining()). Furthermore, this method doesn't
* alter the state of the passed-in ByteBuffer. Its position, limit, mark,
* etc. will remain unchanged. If you only want to write the remaining bytes
* of a ByteBuffer, you can call
* {@code writeByteBuffer(fieldNumber, byteBuffer.slice())}.
*/
public void writeByteBuffer(final int fieldNumber, final ByteBuffer value)
throws IOException {
writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
writeByteBufferNoTag(value);
}
/** Write a {@code uint32} field, including tag, to the stream. */
public void writeUInt32(final int fieldNumber, final int value)
throws IOException {
@ -362,6 +430,7 @@ public final class CodedOutputStream {
value.writeTo(this);
}
/**
* Write a group represented by an {@link UnknownFieldSet}.
*
@ -380,12 +449,41 @@ public final class CodedOutputStream {
value.writeTo(this);
}
/** Write a {@code bytes} field to the stream. */
public void writeBytesNoTag(final ByteString value) throws IOException {
writeRawVarint32(value.size());
writeRawBytes(value);
}
/** Write a {@code bytes} field to the stream. */
public void writeByteArrayNoTag(final byte[] value) throws IOException {
writeRawVarint32(value.length);
writeRawBytes(value);
}
/** Write a {@code bytes} field to the stream. */
public void writeByteArrayNoTag(final byte[] value,
final int offset,
final int length) throws IOException {
writeRawVarint32(length);
writeRawBytes(value, offset, length);
}
/**
* Write a {@code bytes} field to the stream. This method will write all
* content of the ByteBuffer regardless of the current position and limit
* (i.e., the number of bytes to be written is value.capacity(), not
* value.remaining()). Furthermore, this method doesn't alter the state of
* the passed-in ByteBuffer. Its position, limit, mark, etc. will remain
* unchanged. If you only want to write the remaining bytes of a ByteBuffer,
* you can call {@code writeByteBufferNoTag(byteBuffer.slice())}.
*/
public void writeByteBufferNoTag(final ByteBuffer value) throws IOException {
writeRawVarint32(value.capacity());
writeRawBytes(value);
}
/** Write a {@code uint32} field to the stream. */
public void writeUInt32NoTag(final int value) throws IOException {
writeRawVarint32(value);
@ -539,12 +637,30 @@ public final class CodedOutputStream {
return computeTagSize(fieldNumber) + computeBytesSizeNoTag(value);
}
/**
* Compute the number of bytes that would be needed to encode a
* {@code bytes} field, including tag.
*/
public static int computeByteArraySize(final int fieldNumber,
final byte[] value) {
return computeTagSize(fieldNumber) + computeByteArraySizeNoTag(value);
}
/**
* Compute the number of bytes that would be needed to encode a
* {@code bytes} field, including tag.
*/
public static int computeByteBufferSize(final int fieldNumber,
final ByteBuffer value) {
return computeTagSize(fieldNumber) + computeByteBufferSizeNoTag(value);
}
/**
* Compute the number of bytes that would be needed to encode an
* embedded message in lazy field, including tag.
*/
public static int computeLazyFieldSize(final int fieldNumber,
final LazyField value) {
final LazyFieldLite value) {
return computeTagSize(fieldNumber) + computeLazyFieldSizeNoTag(value);
}
@ -629,7 +745,7 @@ public final class CodedOutputStream {
* historical reasons, the wire format differs from normal fields.
*/
public static int computeLazyFieldMessageSetExtensionSize(
final int fieldNumber, final LazyField value) {
final int fieldNumber, final LazyFieldLite value) {
return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 +
computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) +
computeLazyFieldSize(WireFormat.MESSAGE_SET_MESSAGE, value);
@ -754,7 +870,7 @@ public final class CodedOutputStream {
* Compute the number of bytes that would be needed to encode an embedded
* message stored in lazy field.
*/
public static int computeLazyFieldSizeNoTag(final LazyField value) {
public static int computeLazyFieldSizeNoTag(final LazyFieldLite value) {
final int size = value.getSerializedSize();
return computeRawVarint32Size(size) + size;
}
@ -768,6 +884,22 @@ public final class CodedOutputStream {
value.size();
}
/**
* Compute the number of bytes that would be needed to encode a
* {@code bytes} field.
*/
public static int computeByteArraySizeNoTag(final byte[] value) {
return computeRawVarint32Size(value.length) + value.length;
}
/**
* Compute the number of bytes that would be needed to encode a
* {@code bytes} field.
*/
public static int computeByteBufferSizeNoTag(final ByteBuffer value) {
return computeRawVarint32Size(value.capacity()) + value.capacity();
}
/**
* Compute the number of bytes that would be needed to encode a
* {@code uint32} field.
@ -886,6 +1018,15 @@ public final class CodedOutputStream {
}
}
/**
* Get the total number of bytes successfully written to this stream. The
* returned value is not guaranteed to be accurate if exceptions have been
* found in the middle of writing.
*/
public int getTotalBytesWritten() {
return totalBytesWritten;
}
/** Write a single byte. */
public void writeRawByte(final byte value) throws IOException {
if (position == limit) {
@ -893,6 +1034,7 @@ public final class CodedOutputStream {
}
buffer[position++] = value;
++totalBytesWritten;
}
/** Write a single byte, represented by an integer value. */
@ -910,6 +1052,61 @@ public final class CodedOutputStream {
writeRawBytes(value, 0, value.length);
}
/**
* Write a ByteBuffer. This method will write all content of the ByteBuffer
* regardless of the current position and limit (i.e., the number of bytes
* to be written is value.capacity(), not value.remaining()). Furthermore,
* this method doesn't alter the state of the passed-in ByteBuffer. Its
* position, limit, mark, etc. will remain unchanged. If you only want to
* write the remaining bytes of a ByteBuffer, you can call
* {@code writeRawBytes(byteBuffer.slice())}.
*/
public void writeRawBytes(final ByteBuffer value) throws IOException {
if (value.hasArray()) {
writeRawBytes(value.array(), value.arrayOffset(), value.capacity());
} else {
ByteBuffer duplicated = value.duplicate();
duplicated.clear();
writeRawBytesInternal(duplicated);
}
}
/** Write a ByteBuffer that isn't backed by an array. */
private void writeRawBytesInternal(final ByteBuffer value)
throws IOException {
int length = value.remaining();
if (limit - position >= length) {
// We have room in the current buffer.
value.get(buffer, position, length);
position += length;
totalBytesWritten += length;
} else {
// Write extends past current buffer. Fill the rest of this buffer and
// flush.
final int bytesWritten = limit - position;
value.get(buffer, position, bytesWritten);
length -= bytesWritten;
position = limit;
totalBytesWritten += bytesWritten;
refreshBuffer();
// Now deal with the rest.
// Since we have an output stream, this is our buffer
// and buffer offset == 0
while (length > limit) {
// Copy data into the buffer before writing it to OutputStream.
// TODO(xiaofeng): Introduce ZeroCopyOutputStream to avoid this copy.
value.get(buffer, 0, limit);
output.write(buffer, 0, limit);
length -= limit;
totalBytesWritten += limit;
}
value.get(buffer, 0, length);
position = length;
totalBytesWritten += length;
}
}
/** Write part of an array of bytes. */
public void writeRawBytes(final byte[] value, int offset, int length)
throws IOException {
@ -917,6 +1114,7 @@ public final class CodedOutputStream {
// We have room in the current buffer.
System.arraycopy(value, offset, buffer, position, length);
position += length;
totalBytesWritten += length;
} else {
// Write extends past current buffer. Fill the rest of this buffer and
// flush.
@ -925,6 +1123,7 @@ public final class CodedOutputStream {
offset += bytesWritten;
length -= bytesWritten;
position = limit;
totalBytesWritten += bytesWritten;
refreshBuffer();
// Now deal with the rest.
@ -938,6 +1137,7 @@ public final class CodedOutputStream {
// Write is very big. Let's do it all at once.
output.write(value, offset, length);
}
totalBytesWritten += length;
}
}
@ -948,6 +1148,7 @@ public final class CodedOutputStream {
// We have room in the current buffer.
value.copyTo(buffer, offset, position, length);
position += length;
totalBytesWritten += length;
} else {
// Write extends past current buffer. Fill the rest of this buffer and
// flush.
@ -956,6 +1157,7 @@ public final class CodedOutputStream {
offset += bytesWritten;
length -= bytesWritten;
position = limit;
totalBytesWritten += bytesWritten;
refreshBuffer();
// Now deal with the rest.
@ -966,25 +1168,9 @@ public final class CodedOutputStream {
value.copyTo(buffer, offset, 0, length);
position = length;
} else {
// Write is very big, but we can't do it all at once without allocating
// an a copy of the byte array since ByteString does not give us access
// to the underlying bytes. Use the InputStream interface on the
// ByteString and our buffer to copy between the two.
InputStream inputStreamFrom = value.newInput();
if (offset != inputStreamFrom.skip(offset)) {
throw new IllegalStateException("Skip failed? Should never happen.");
}
// Use the buffer as the temporary buffer to avoid allocating memory.
while (length > 0) {
int bytesToRead = Math.min(length, limit);
int bytesRead = inputStreamFrom.read(buffer, 0, bytesToRead);
if (bytesRead != bytesToRead) {
throw new IllegalStateException("Read failed? Should never happen");
}
output.write(buffer, 0, bytesRead);
length -= bytesRead;
}
value.writeTo(output, offset, length);
}
totalBytesWritten += length;
}
}

@ -32,6 +32,7 @@ package com.google.protobuf;
import com.google.protobuf.DescriptorProtos.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@ -39,6 +40,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import java.io.UnsupportedEncodingException;
/**
@ -60,19 +62,27 @@ import java.io.UnsupportedEncodingException;
* @author kenton@google.com Kenton Varda
*/
public final class Descriptors {
private static final Logger logger =
Logger.getLogger(Descriptors.class.getName());
/**
* Describes a {@code .proto} file, including everything defined within.
* That includes, in particular, descriptors for all the messages and
* file descriptors for all other imported {@code .proto} files
* (dependencies).
*/
public static final class FileDescriptor {
public static final class FileDescriptor extends GenericDescriptor {
/** Convert the descriptor to its protocol message representation. */
public FileDescriptorProto toProto() { return proto; }
/** Get the file name. */
public String getName() { return proto.getName(); }
/** Returns this object. */
public FileDescriptor getFile() { return this; }
/** Returns the same as getName(). */
public String getFullName() { return proto.getName(); }
/**
* Get the proto package name. This is the package name given by the
* {@code package} statement in the {@code .proto} file, which differs
@ -213,8 +223,7 @@ public final class Descriptors {
*
* @param proto The protocol message form of the FileDescriptor.
* @param dependencies {@code FileDescriptor}s corresponding to all of
* the file's dependencies, in the exact order listed
* in {@code proto}.
* the file's dependencies.
* @throws DescriptorValidationException {@code proto} is not a valid
* descriptor. This can occur for a number of reasons, e.g.
* because a field has an undefined type or because two messages
@ -223,6 +232,28 @@ public final class Descriptors {
public static FileDescriptor buildFrom(final FileDescriptorProto proto,
final FileDescriptor[] dependencies)
throws DescriptorValidationException {
return buildFrom(proto, dependencies, false);
}
/**
* Construct a {@code FileDescriptor}.
*
* @param proto The protocol message form of the FileDescriptor.
* @param dependencies {@code FileDescriptor}s corresponding to all of
* the file's dependencies.
* @param allowUnknownDependencies If true, non-exist dependenncies will be
* ignored and undefined message types will be replaced with a
* placeholder type.
* @throws DescriptorValidationException {@code proto} is not a valid
* descriptor. This can occur for a number of reasons, e.g.
* because a field has an undefined type or because two messages
* were defined with the same name.
*/
private static FileDescriptor buildFrom(
final FileDescriptorProto proto, final FileDescriptor[] dependencies,
final boolean allowUnknownDependencies)
throws DescriptorValidationException {
// Building descriptors involves two steps: translating and linking.
// In the translation step (implemented by FileDescriptor's
// constructor), we build an object tree mirroring the
@ -232,23 +263,10 @@ public final class Descriptors {
// FieldDescriptor for an embedded message contains a pointer directly
// to the Descriptor for that message's type. We also detect undefined
// types in the linking step.
final DescriptorPool pool = new DescriptorPool(dependencies);
final FileDescriptor result =
new FileDescriptor(proto, dependencies, pool);
if (dependencies.length != proto.getDependencyCount()) {
throw new DescriptorValidationException(result,
"Dependencies passed to FileDescriptor.buildFrom() don't match " +
"those listed in the FileDescriptorProto.");
}
for (int i = 0; i < proto.getDependencyCount(); i++) {
if (!dependencies[i].getName().equals(proto.getDependency(i))) {
throw new DescriptorValidationException(result,
"Dependencies passed to FileDescriptor.buildFrom() don't match " +
"those listed in the FileDescriptorProto.");
}
}
final DescriptorPool pool = new DescriptorPool(
dependencies, allowUnknownDependencies);
final FileDescriptor result = new FileDescriptor(
proto, dependencies, pool, allowUnknownDependencies);
result.crossLink();
return result;
}
@ -296,7 +314,9 @@ public final class Descriptors {
final FileDescriptor result;
try {
result = buildFrom(proto, dependencies);
// When building descriptors for generated code, we allow unknown
// dependencies by default.
result = buildFrom(proto, dependencies, true);
} catch (DescriptorValidationException e) {
throw new IllegalArgumentException(
"Invalid embedded descriptor for \"" + proto.getName() + "\".", e);
@ -319,6 +339,56 @@ public final class Descriptors {
}
}
/**
* This method is to be called by generated code only. It uses Java
* reflection to load the dependencies' descriptors.
*/
public static void internalBuildGeneratedFileFrom(
final String[] descriptorDataParts,
final Class<?> descriptorOuterClass,
final String[] dependencies,
final String[] dependencyFileNames,
final InternalDescriptorAssigner descriptorAssigner) {
List<FileDescriptor> descriptors = new ArrayList<FileDescriptor>();
for (int i = 0; i < dependencies.length; i++) {
try {
Class<?> clazz =
descriptorOuterClass.getClassLoader().loadClass(dependencies[i]);
descriptors.add(
(FileDescriptor) clazz.getField("descriptor").get(null));
} catch (Exception e) {
// We allow unknown dependencies by default. If a dependency cannot
// be found we only generate a warning.
logger.warning("Descriptors for \"" + dependencyFileNames[i] +
"\" can not be found.");
}
}
FileDescriptor[] descriptorArray = new FileDescriptor[descriptors.size()];
descriptors.toArray(descriptorArray);
internalBuildGeneratedFileFrom(
descriptorDataParts, descriptorArray, descriptorAssigner);
}
/**
* This method is to be called by generated code only. It is used to
* update the FileDescriptorProto associated with the descriptor by
* parsing it again with the given ExtensionRegistry. This is needed to
* recognize custom options.
*/
public static void internalUpdateFileDescriptor(
final FileDescriptor descriptor,
final ExtensionRegistry registry) {
ByteString bytes = descriptor.proto.toByteString();
FileDescriptorProto proto;
try {
proto = FileDescriptorProto.parseFrom(bytes, registry);
} catch (InvalidProtocolBufferException e) {
throw new IllegalArgumentException(
"Failed to parse protocol buffer descriptor for generated code.", e);
}
descriptor.setProto(proto);
}
/**
* This class should be used by generated code only. When calling
* {@link FileDescriptor#internalBuildGeneratedFileFrom}, the caller
@ -346,22 +416,38 @@ public final class Descriptors {
private FileDescriptor(final FileDescriptorProto proto,
final FileDescriptor[] dependencies,
final DescriptorPool pool)
final DescriptorPool pool,
boolean allowUnknownDependencies)
throws DescriptorValidationException {
this.pool = pool;
this.proto = proto;
this.dependencies = dependencies.clone();
this.publicDependencies =
new FileDescriptor[proto.getPublicDependencyCount()];
HashMap<String, FileDescriptor> nameToFileMap =
new HashMap<String, FileDescriptor>();
for (FileDescriptor file : dependencies) {
nameToFileMap.put(file.getName(), file);
}
List<FileDescriptor> publicDependencies = new ArrayList<FileDescriptor>();
for (int i = 0; i < proto.getPublicDependencyCount(); i++) {
int index = proto.getPublicDependency(i);
if (index < 0 || index >= this.dependencies.length) {
if (index < 0 || index >= proto.getDependencyCount()) {
throw new DescriptorValidationException(this,
"Invalid public dependency index.");
}
this.publicDependencies[i] =
this.dependencies[proto.getPublicDependency(i)];
String name = proto.getDependency(index);
FileDescriptor file = nameToFileMap.get(name);
if (file == null) {
if (!allowUnknownDependencies) {
throw new DescriptorValidationException(this,
"Invalid public dependency: " + name);
}
// Ignore unknown dependencies.
} else {
publicDependencies.add(file);
}
}
this.publicDependencies = new FileDescriptor[publicDependencies.size()];
publicDependencies.toArray(this.publicDependencies);
pool.addPackage(getPackage(), this);
@ -387,6 +473,27 @@ public final class Descriptors {
proto.getExtension(i), this, null, i, true);
}
}
/**
* Create a placeholder FileDescriptor for a message Descriptor.
*/
FileDescriptor(String packageName, Descriptor message)
throws DescriptorValidationException {
this.pool = new DescriptorPool(new FileDescriptor[0], true);
this.proto = FileDescriptorProto.newBuilder()
.setName(message.getFullName() + ".placeholder.proto")
.setPackage(packageName).addMessageType(message.toProto()).build();
this.dependencies = new FileDescriptor[0];
this.publicDependencies = new FileDescriptor[0];
messageTypes = new Descriptor[] {message};
enumTypes = new EnumDescriptor[0];
services = new ServiceDescriptor[0];
extensions = new FieldDescriptor[0];
pool.addPackage(packageName, this);
pool.addSymbol(message);
}
/** Look up and cross-link all field types, etc. */
private void crossLink() throws DescriptorValidationException {
@ -437,7 +544,7 @@ public final class Descriptors {
// =================================================================
/** Describes a message type. */
public static final class Descriptor implements GenericDescriptor {
public static final class Descriptor extends GenericDescriptor {
/**
* Get the index of this descriptor within its parent. In other words,
* given a {@link FileDescriptor} {@code file}, the following is true:
@ -486,6 +593,11 @@ public final class Descriptors {
return Collections.unmodifiableList(Arrays.asList(fields));
}
/** Get a list of this message type's oneofs. */
public List<OneofDescriptor> getOneofs() {
return Collections.unmodifiableList(Arrays.asList(oneofs));
}
/** Get a list of this message type's extensions. */
public List<FieldDescriptor> getExtensions() {
return Collections.unmodifiableList(Arrays.asList(extensions));
@ -512,6 +624,14 @@ public final class Descriptors {
return false;
}
/**
* Indicates whether the message can be extended. That is, whether it has
* any "extensions x to y" ranges declared on it.
*/
public boolean isExtendable() {
return proto.getExtensionRangeList().size() != 0;
}
/**
* Finds a field by name.
* @param name The unqualified name of the field (e.g. "foo").
@ -576,6 +696,33 @@ public final class Descriptors {
private final EnumDescriptor[] enumTypes;
private final FieldDescriptor[] fields;
private final FieldDescriptor[] extensions;
private final OneofDescriptor[] oneofs;
// Used to create a placeholder when the type cannot be found.
Descriptor(final String fullname) throws DescriptorValidationException {
String name = fullname;
String packageName = "";
int pos = fullname.lastIndexOf('.');
if (pos != -1) {
name = fullname.substring(pos + 1);
packageName = fullname.substring(0, pos);
}
this.index = 0;
this.proto = DescriptorProto.newBuilder().setName(name).addExtensionRange(
DescriptorProto.ExtensionRange.newBuilder().setStart(1)
.setEnd(536870912).build()).build();
this.fullName = fullname;
this.containingType = null;
this.nestedTypes = new Descriptor[0];
this.enumTypes = new EnumDescriptor[0];
this.fields = new FieldDescriptor[0];
this.extensions = new FieldDescriptor[0];
this.oneofs = new OneofDescriptor[0];
// Create a placeholder FileDescriptor to hold this message.
this.file = new FileDescriptor(packageName, this);
}
private Descriptor(final DescriptorProto proto,
final FileDescriptor file,
@ -588,6 +735,12 @@ public final class Descriptors {
this.file = file;
containingType = parent;
oneofs = new OneofDescriptor[proto.getOneofDeclCount()];
for (int i = 0; i < proto.getOneofDeclCount(); i++) {
oneofs[i] = new OneofDescriptor(
proto.getOneofDecl(i), file, this, i);
}
nestedTypes = new Descriptor[proto.getNestedTypeCount()];
for (int i = 0; i < proto.getNestedTypeCount(); i++) {
nestedTypes[i] = new Descriptor(
@ -612,6 +765,17 @@ public final class Descriptors {
proto.getExtension(i), file, this, i, true);
}
for (int i = 0; i < proto.getOneofDeclCount(); i++) {
oneofs[i].fields = new FieldDescriptor[oneofs[i].getFieldCount()];
oneofs[i].fieldCount = 0;
}
for (int i = 0; i < proto.getFieldCount(); i++) {
OneofDescriptor oneofDescriptor = fields[i].getContainingOneof();
if (oneofDescriptor != null) {
oneofDescriptor.fields[oneofDescriptor.fieldCount++] = fields[i];
}
}
file.pool.addSymbol(this);
}
@ -656,7 +820,8 @@ public final class Descriptors {
/** Describes a field of a message type. */
public static final class FieldDescriptor
implements GenericDescriptor, Comparable<FieldDescriptor>,
extends GenericDescriptor
implements Comparable<FieldDescriptor>,
FieldSet.FieldDescriptorLite<FieldDescriptor> {
/**
* Get the index of this descriptor within its parent.
@ -700,6 +865,12 @@ public final class Descriptors {
public WireFormat.FieldType getLiteType() {
return table[type.ordinal()];
}
/** For internal use only. */
public boolean needsUtf8Check() {
return (type == Type.STRING) && (getFile().getOptions().getJavaStringCheckUtf8());
}
// 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.
@ -761,6 +932,9 @@ public final class Descriptors {
*/
public Descriptor getContainingType() { return containingType; }
/** Get the field's containing oneof. */
public OneofDescriptor getContainingOneof() { return containingOneof; }
/**
* For extensions defined nested within message types, gets the outer
* type. Not valid for non-extension fields. For example, consider
@ -838,6 +1012,7 @@ public final class Descriptors {
private Type type;
private Descriptor containingType;
private Descriptor messageType;
private OneofDescriptor containingOneof;
private EnumDescriptor enumType;
private Object defaultValue;
@ -946,12 +1121,31 @@ public final class Descriptors {
} else {
extensionScope = null;
}
if (proto.hasOneofIndex()) {
throw new DescriptorValidationException(this,
"FieldDescriptorProto.oneof_index set for extension field.");
}
containingOneof = null;
} else {
if (proto.hasExtendee()) {
throw new DescriptorValidationException(this,
"FieldDescriptorProto.extendee set for non-extension field.");
}
containingType = parent;
if (proto.hasOneofIndex()) {
if (proto.getOneofIndex() < 0 ||
proto.getOneofIndex() >= parent.toProto().getOneofDeclCount()) {
throw new DescriptorValidationException(this,
"FieldDescriptorProto.oneof_index is out of range for type "
+ parent.getName());
}
containingOneof = parent.getOneofs().get(proto.getOneofIndex());
containingOneof.fieldCount++;
} else {
containingOneof = null;
}
extensionScope = null;
}
@ -1161,13 +1355,14 @@ public final class Descriptors {
// down-cast and call mergeFrom directly.
return ((Message.Builder) to).mergeFrom((Message) from);
}
}
// =================================================================
/** Describes an enum type. */
public static final class EnumDescriptor
implements GenericDescriptor, Internal.EnumLiteMap<EnumValueDescriptor> {
public static final class EnumDescriptor extends GenericDescriptor
implements Internal.EnumLiteMap<EnumValueDescriptor> {
/**
* Get the index of this descriptor within its parent.
* @see Descriptors.Descriptor#getIndex()
@ -1278,8 +1473,8 @@ public final class Descriptors {
* with the same number after the first become aliases of the first.
* However, they still have independent EnumValueDescriptors.
*/
public static final class EnumValueDescriptor
implements GenericDescriptor, Internal.EnumLite {
public static final class EnumValueDescriptor extends GenericDescriptor
implements Internal.EnumLite {
/**
* Get the index of this descriptor within its parent.
* @see Descriptors.Descriptor#getIndex()
@ -1294,6 +1489,9 @@ public final class Descriptors {
/** Get the value's number. */
public int getNumber() { return proto.getNumber(); }
@Override
public String toString() { return proto.getName(); }
/**
* Get the value's fully-qualified name.
@ -1343,7 +1541,7 @@ public final class Descriptors {
// =================================================================
/** Describes a service type. */
public static final class ServiceDescriptor implements GenericDescriptor {
public static final class ServiceDescriptor extends GenericDescriptor {
/**
* Get the index of this descriptor within its parent.
* * @see Descriptors.Descriptor#getIndex()
@ -1433,7 +1631,7 @@ public final class Descriptors {
/**
* Describes one method within a service type.
*/
public static final class MethodDescriptor implements GenericDescriptor {
public static final class MethodDescriptor extends GenericDescriptor {
/**
* Get the index of this descriptor within its parent.
* * @see Descriptors.Descriptor#getIndex()
@ -1537,14 +1735,18 @@ public final class Descriptors {
// =================================================================
/**
* All descriptors except {@code FileDescriptor} implement this to make
* {@code DescriptorPool}'s life easier.
* All descriptors implement this to make it easier to implement tools like
* {@code DescriptorPool}.<p>
*
* This class is public so that the methods it exposes can be called from
* outside of this package. However, it should only be subclassed from
* nested classes of Descriptors.
*/
private interface GenericDescriptor {
Message toProto();
String getName();
String getFullName();
FileDescriptor getFile();
public abstract static class GenericDescriptor {
public abstract Message toProto();
public abstract String getName();
public abstract String getFullName();
public abstract FileDescriptor getFile();
}
/**
@ -1620,8 +1822,10 @@ public final class Descriptors {
TYPES_ONLY, AGGREGATES_ONLY, ALL_SYMBOLS
}
DescriptorPool(final FileDescriptor[] dependencies) {
DescriptorPool(final FileDescriptor[] dependencies,
boolean allowUnknownDependencies) {
this.dependencies = new HashSet<FileDescriptor>();
this.allowUnknownDependencies = allowUnknownDependencies;
for (int i = 0; i < dependencies.length; i++) {
this.dependencies.add(dependencies[i]);
@ -1650,6 +1854,7 @@ public final class Descriptors {
}
private final Set<FileDescriptor> dependencies;
private boolean allowUnknownDependencies;
private final Map<String, GenericDescriptor> descriptorsByName =
new HashMap<String, GenericDescriptor>();
@ -1718,9 +1923,11 @@ public final class Descriptors {
// TODO(kenton): This could be optimized in a number of ways.
GenericDescriptor result;
String fullname;
if (name.startsWith(".")) {
// Fully-qualified name.
result = findSymbol(name.substring(1), filter);
fullname = name.substring(1);
result = findSymbol(fullname, filter);
} else {
// If "name" is a compound identifier, we want to search for the
// first component of it, then search within it for the rest.
@ -1752,6 +1959,7 @@ public final class Descriptors {
// Chop off the last component of the scope.
final int dotpos = scopeToTry.lastIndexOf(".");
if (dotpos == -1) {
fullname = name;
result = findSymbol(name, filter);
break;
} else {
@ -1771,6 +1979,7 @@ public final class Descriptors {
scopeToTry.append(name);
result = findSymbol(scopeToTry.toString(), filter);
}
fullname = scopeToTry.toString();
break;
}
@ -1781,8 +1990,24 @@ public final class Descriptors {
}
if (result == null) {
throw new DescriptorValidationException(relativeTo,
'\"' + name + "\" is not defined.");
if (allowUnknownDependencies && filter == SearchFilter.TYPES_ONLY) {
logger.warning("The descriptor for message type \"" + name +
"\" can not be found and a placeholder is created for it");
// We create a dummy message descriptor here regardless of the
// expected type. If the type should be message, this dummy
// descriptor will work well and if the type should be enum, a
// DescriptorValidationException will be thrown latter. In either
// case, the code works as expected: we allow unknown message types
// but not unknwon enum types.
result = new Descriptor(fullname);
// Add the placeholder file as a dependency so we can find the
// placeholder symbol when resolving other references.
this.dependencies.add(result.getFile());
return result;
} else {
throw new DescriptorValidationException(relativeTo,
'\"' + name + "\" is not defined.");
}
} else {
return result;
}
@ -1826,7 +2051,7 @@ public final class Descriptors {
* just as placeholders so that someone cannot define, say, a message type
* that has the same name as an existing package.
*/
private static final class PackageDescriptor implements GenericDescriptor {
private static final class PackageDescriptor extends GenericDescriptor {
public Message toProto() { return file.toProto(); }
public String getName() { return name; }
public String getFullName() { return fullName; }
@ -1911,7 +2136,7 @@ public final class Descriptors {
fieldsByNumber.put(key, old);
throw new DescriptorValidationException(field,
"Field number " + field.getNumber() +
"has already been used in \"" +
" has already been used in \"" +
field.getContainingType().getFullName() +
"\" by field \"" + old.getName() + "\".");
}
@ -1967,4 +2192,47 @@ public final class Descriptors {
}
}
}
/** Describes an oneof of a message type. */
public static final class OneofDescriptor {
/** Get the index of this descriptor within its parent. */
public int getIndex() { return index; }
public String getName() { return proto.getName(); }
public FileDescriptor getFile() { return file; }
public String getFullName() { return fullName; }
public Descriptor getContainingType() { return containingType; }
public int getFieldCount() { return fieldCount; }
public FieldDescriptor getField(int index) {
return fields[index];
}
private OneofDescriptor(final OneofDescriptorProto proto,
final FileDescriptor file,
final Descriptor parent,
final int index)
throws DescriptorValidationException {
this.proto = proto;
fullName = computeFullName(file, parent, proto.getName());
this.file = file;
this.index = index;
containingType = parent;
fieldCount = 0;
}
private final int index;
private OneofDescriptorProto proto;
private final String fullName;
private final FileDescriptor file;
private Descriptor containingType;
private int fieldCount;
private FieldDescriptor[] fields;
}
}

@ -32,6 +32,7 @@ package com.google.protobuf;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.OneofDescriptor;
import java.io.InputStream;
import java.io.IOException;
@ -47,16 +48,25 @@ import java.util.Map;
public final class DynamicMessage extends AbstractMessage {
private final Descriptor type;
private final FieldSet<FieldDescriptor> fields;
private final FieldDescriptor[] oneofCases;
private final UnknownFieldSet unknownFields;
private int memoizedSize = -1;
/**
* Construct a {@code DynamicMessage} using the given {@code FieldSet}.
* oneofCases stores the FieldDescriptor for each oneof to indicate
* which field is set. Caller should make sure the array is immutable.
*
* This contructor is package private and will be used in
* {@code DynamicMutableMessage} to convert a mutable message to an immutable
* message.
*/
private DynamicMessage(Descriptor type, FieldSet<FieldDescriptor> fields,
UnknownFieldSet unknownFields) {
DynamicMessage(Descriptor type, FieldSet<FieldDescriptor> fields,
FieldDescriptor[] oneofCases,
UnknownFieldSet unknownFields) {
this.type = type;
this.fields = fields;
this.oneofCases = oneofCases;
this.unknownFields = unknownFields;
}
@ -65,10 +75,14 @@ public final class DynamicMessage extends AbstractMessage {
* given type.
*/
public static DynamicMessage getDefaultInstance(Descriptor type) {
int oneofDeclCount = type.toProto().getOneofDeclCount();
FieldDescriptor[] oneofCases = new FieldDescriptor[oneofDeclCount];
return new DynamicMessage(type, FieldSet.<FieldDescriptor>emptySet(),
oneofCases,
UnknownFieldSet.getDefaultInstance());
}
/** Parse a message of the given type from the given input stream. */
public static DynamicMessage parseFrom(Descriptor type,
CodedInputStream input)
@ -152,6 +166,20 @@ public final class DynamicMessage extends AbstractMessage {
return fields.getAllFields();
}
public boolean hasOneof(OneofDescriptor oneof) {
verifyOneofContainingType(oneof);
FieldDescriptor field = oneofCases[oneof.getIndex()];
if (field == null) {
return false;
}
return true;
}
public FieldDescriptor getOneofFieldDescriptor(OneofDescriptor oneof) {
verifyOneofContainingType(oneof);
return oneofCases[oneof.getIndex()];
}
public boolean hasField(FieldDescriptor field) {
verifyContainingType(field);
return fields.hasField(field);
@ -186,8 +214,8 @@ public final class DynamicMessage extends AbstractMessage {
return unknownFields;
}
private static boolean isInitialized(Descriptor type,
FieldSet<FieldDescriptor> fields) {
static boolean isInitialized(Descriptor type,
FieldSet<FieldDescriptor> fields) {
// Check that all required fields are present.
for (final FieldDescriptor field : type.getFields()) {
if (field.isRequired()) {
@ -270,6 +298,14 @@ public final class DynamicMessage extends AbstractMessage {
}
}
/** Verifies that the oneof is an oneof of this message. */
private void verifyOneofContainingType(OneofDescriptor oneof) {
if (oneof.getContainingType() != type) {
throw new IllegalArgumentException(
"OneofDescriptor does not match message type.");
}
}
// =================================================================
/**
@ -278,6 +314,7 @@ public final class DynamicMessage extends AbstractMessage {
public static final class Builder extends AbstractMessage.Builder<Builder> {
private final Descriptor type;
private FieldSet<FieldDescriptor> fields;
private final FieldDescriptor[] oneofCases;
private UnknownFieldSet unknownFields;
/** Construct a {@code Builder} for the given type. */
@ -285,6 +322,7 @@ public final class DynamicMessage extends AbstractMessage {
this.type = type;
this.fields = FieldSet.newFieldSet();
this.unknownFields = UnknownFieldSet.getDefaultInstance();
this.oneofCases = new FieldDescriptor[type.toProto().getOneofDeclCount()];
}
// ---------------------------------------------------------------
@ -313,6 +351,17 @@ public final class DynamicMessage extends AbstractMessage {
ensureIsMutable();
fields.mergeFrom(otherDynamicMessage.fields);
mergeUnknownFields(otherDynamicMessage.unknownFields);
for (int i = 0; i < oneofCases.length; i++) {
if (oneofCases[i] == null) {
oneofCases[i] = otherDynamicMessage.oneofCases[i];
} else {
if ((otherDynamicMessage.oneofCases[i] != null)
&& (oneofCases[i] != otherDynamicMessage.oneofCases[i])) {
fields.clearField(oneofCases[i]);
oneofCases[i] = otherDynamicMessage.oneofCases[i];
}
}
}
return this;
} else {
return super.mergeFrom(other);
@ -322,7 +371,8 @@ public final class DynamicMessage extends AbstractMessage {
public DynamicMessage build() {
if (!isInitialized()) {
throw newUninitializedMessageException(
new DynamicMessage(type, fields, unknownFields));
new DynamicMessage(type, fields,
java.util.Arrays.copyOf(oneofCases, oneofCases.length), unknownFields));
}
return buildPartial();
}
@ -335,7 +385,8 @@ public final class DynamicMessage extends AbstractMessage {
private DynamicMessage buildParsed() throws InvalidProtocolBufferException {
if (!isInitialized()) {
throw newUninitializedMessageException(
new DynamicMessage(type, fields, unknownFields))
new DynamicMessage(type, fields,
java.util.Arrays.copyOf(oneofCases, oneofCases.length), unknownFields))
.asInvalidProtocolBufferException();
}
return buildPartial();
@ -344,7 +395,8 @@ public final class DynamicMessage extends AbstractMessage {
public DynamicMessage buildPartial() {
fields.makeImmutable();
DynamicMessage result =
new DynamicMessage(type, fields, unknownFields);
new DynamicMessage(type, fields,
java.util.Arrays.copyOf(oneofCases, oneofCases.length), unknownFields);
return result;
}
@ -353,6 +405,7 @@ public final class DynamicMessage extends AbstractMessage {
Builder result = new Builder(type);
result.fields.mergeFrom(fields);
result.mergeUnknownFields(unknownFields);
System.arraycopy(oneofCases, 0, result.oneofCases, 0 , oneofCases.length);
return result;
}
@ -383,6 +436,29 @@ public final class DynamicMessage extends AbstractMessage {
return new Builder(field.getMessageType());
}
public boolean hasOneof(OneofDescriptor oneof) {
verifyOneofContainingType(oneof);
FieldDescriptor field = oneofCases[oneof.getIndex()];
if (field == null) {
return false;
}
return true;
}
public FieldDescriptor getOneofFieldDescriptor(OneofDescriptor oneof) {
verifyOneofContainingType(oneof);
return oneofCases[oneof.getIndex()];
}
public Builder clearOneof(OneofDescriptor oneof) {
verifyOneofContainingType(oneof);
FieldDescriptor field = oneofCases[oneof.getIndex()];
if (field != null) {
clearField(field);
}
return this;
}
public boolean hasField(FieldDescriptor field) {
verifyContainingType(field);
return fields.hasField(field);
@ -392,7 +468,9 @@ public final class DynamicMessage extends AbstractMessage {
verifyContainingType(field);
Object result = fields.getField(field);
if (result == null) {
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
if (field.isRepeated()) {
result = Collections.emptyList();
} else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
result = getDefaultInstance(field.getMessageType());
} else {
result = field.getDefaultValue();
@ -404,6 +482,15 @@ public final class DynamicMessage extends AbstractMessage {
public Builder setField(FieldDescriptor field, Object value) {
verifyContainingType(field);
ensureIsMutable();
OneofDescriptor oneofDescriptor = field.getContainingOneof();
if (oneofDescriptor != null) {
int index = oneofDescriptor.getIndex();
FieldDescriptor oldField = oneofCases[index];
if ((oldField != null) && (oldField != field)) {
fields.clearField(oldField);
}
oneofCases[index] = field;
}
fields.setField(field, value);
return this;
}
@ -411,6 +498,13 @@ public final class DynamicMessage extends AbstractMessage {
public Builder clearField(FieldDescriptor field) {
verifyContainingType(field);
ensureIsMutable();
OneofDescriptor oneofDescriptor = field.getContainingOneof();
if (oneofDescriptor != null) {
int index = oneofDescriptor.getIndex();
if (oneofCases[index] == field) {
oneofCases[index] = null;
}
}
fields.clearField(field);
return this;
}
@ -466,6 +560,14 @@ public final class DynamicMessage extends AbstractMessage {
}
}
/** Verifies that the oneof is an oneof of this message. */
private void verifyOneofContainingType(OneofDescriptor oneof) {
if (oneof.getContainingType() != type) {
throw new IllegalArgumentException(
"OneofDescriptor does not match message type.");
}
}
private void ensureIsMutable() {
if (fields.isImmutable()) {
fields = fields.clone();

@ -0,0 +1,96 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// 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;
/**
* Interface that generated extensions implement.
*
* @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();
/** 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();
// All the methods below are extension implementation details.
/**
* The API type that the extension is used for.
*/
protected enum ExtensionType {
IMMUTABLE,
MUTABLE,
PROTO1,
}
protected ExtensionType getExtensionType() {
// TODO(liujisi): make this abstract after we fix proto1.
return ExtensionType.IMMUTABLE;
}
/**
* Type of a message extension.
*/
public enum MessageType {
PROTO1,
PROTO2,
}
/**
* If the extension is a message extension (i.e., getLiteType() == MESSAGE),
* returns the type of the message, otherwise undefined.
*/
public MessageType getMessageType() {
return MessageType.PROTO2;
}
protected abstract Object fromReflectionType(Object value);
protected abstract Object singularFromReflectionType(Object value);
protected abstract Object toReflectionType(Object value);
protected abstract Object singularToReflectionType(Object value);
}

@ -33,9 +33,12 @@ package com.google.protobuf;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* A table of known extensions, searchable by name or field number. When
@ -90,7 +93,7 @@ import java.util.Map;
*
* @author kenton@google.com Kenton Varda
*/
public final class ExtensionRegistry extends ExtensionRegistryLite {
public class ExtensionRegistry extends ExtensionRegistryLite {
/** Construct a new, empty instance. */
public static ExtensionRegistry newInstance() {
return new ExtensionRegistry();
@ -101,6 +104,7 @@ public final class ExtensionRegistry extends ExtensionRegistryLite {
return EMPTY;
}
/** Returns an unmodifiable view of the registry. */
@Override
public ExtensionRegistry getUnmodifiable() {
@ -130,42 +134,127 @@ public final class ExtensionRegistry extends ExtensionRegistryLite {
}
/**
* Find an extension by fully-qualified field name, in the proto namespace.
* I.e. {@code result.descriptor.fullName()} will match {@code fullName} if
* a match is found.
* Deprecated. Use {@link #findImmutableExtensionByName(String)} instead.
*/
public ExtensionInfo findExtensionByName(final String fullName) {
return findImmutableExtensionByName(fullName);
}
/**
* Find an extension for immutable APIs by fully-qualified field name,
* in the proto namespace. i.e. {@code result.descriptor.fullName()} will
* match {@code fullName} if a match is found.
*
* @return Information about the extension if found, or {@code null}
* otherwise.
*/
public ExtensionInfo findExtensionByName(final String fullName) {
return extensionsByName.get(fullName);
public ExtensionInfo findImmutableExtensionByName(final String fullName) {
return immutableExtensionsByName.get(fullName);
}
/**
* Find an extension for mutable APIs by fully-qualified field name,
* in the proto namespace. i.e. {@code result.descriptor.fullName()} will
* match {@code fullName} if a match is found.
*
* @return Information about the extension if found, or {@code null}
* otherwise.
*/
public ExtensionInfo findMutableExtensionByName(final String fullName) {
return mutableExtensionsByName.get(fullName);
}
/**
* Find an extension by containing type and field number.
* Deprecated. Use {@link #findImmutableExtensionByNumber(
* Descriptors.Descriptor, int)}
*/
public ExtensionInfo findExtensionByNumber(
final Descriptor containingType, final int fieldNumber) {
return findImmutableExtensionByNumber(containingType, fieldNumber);
}
/**
* Find an extension by containing type and field number for immutable APIs.
*
* @return Information about the extension if found, or {@code null}
* otherwise.
*/
public ExtensionInfo findExtensionByNumber(final Descriptor containingType,
final int fieldNumber) {
return extensionsByNumber.get(
public ExtensionInfo findImmutableExtensionByNumber(
final Descriptor containingType, final int fieldNumber) {
return immutableExtensionsByNumber.get(
new DescriptorIntPair(containingType, fieldNumber));
}
/**
* Find an extension by containing type and field number for mutable APIs.
*
* @return Information about the extension if found, or {@code null}
* otherwise.
*/
public ExtensionInfo findMutableExtensionByNumber(
final Descriptor containingType, final int fieldNumber) {
return mutableExtensionsByNumber.get(
new DescriptorIntPair(containingType, fieldNumber));
}
/**
* Find all extensions for mutable APIs by fully-qualified name of
* extended class. Note that this method is more computationally expensive
* than getting a single extension by name or number.
*
* @return Information about the extensions found, or {@code null} if there
* are none.
*/
public Set<ExtensionInfo> getAllMutableExtensionsByExtendedType(final String fullName) {
HashSet<ExtensionInfo> extensions = new HashSet<ExtensionInfo>();
for (DescriptorIntPair pair : mutableExtensionsByNumber.keySet()) {
if (pair.descriptor.getFullName().equals(fullName)) {
extensions.add(mutableExtensionsByNumber.get(pair));
}
}
return extensions;
}
/**
* Find all extensions for immutable APIs by fully-qualified name of
* extended class. Note that this method is more computationally expensive
* than getting a single extension by name or number.
*
* @return Information about the extensions found, or {@code null} if there
* are none.
*/
public Set<ExtensionInfo> getAllImmutableExtensionsByExtendedType(final String fullName) {
HashSet<ExtensionInfo> extensions = new HashSet<ExtensionInfo>();
for (DescriptorIntPair pair : immutableExtensionsByNumber.keySet()) {
if (pair.descriptor.getFullName().equals(fullName)) {
extensions.add(immutableExtensionsByNumber.get(pair));
}
}
return extensions;
}
/** Add an extension from a generated file to the registry. */
public void add(final GeneratedMessage.GeneratedExtension<?, ?> extension) {
public void add(final Extension<?, ?> extension) {
if (extension.getExtensionType() != Extension.ExtensionType.IMMUTABLE &&
extension.getExtensionType() != Extension.ExtensionType.MUTABLE) {
// do not support other extension types. ignore
return;
}
add(newExtensionInfo(extension), extension.getExtensionType());
}
static ExtensionInfo newExtensionInfo(final Extension<?, ?> extension) {
if (extension.getDescriptor().getJavaType() ==
FieldDescriptor.JavaType.MESSAGE) {
if (extension.getMessageDefaultInstance() == null) {
throw new IllegalStateException(
"Registered message-type extension had null default instance: " +
extension.getDescriptor().getFullName());
extension.getDescriptor().getFullName());
}
add(new ExtensionInfo(extension.getDescriptor(),
extension.getMessageDefaultInstance()));
return new ExtensionInfo(extension.getDescriptor(),
(Message) extension.getMessageDefaultInstance());
} else {
add(new ExtensionInfo(extension.getDescriptor(), null));
return new ExtensionInfo(extension.getDescriptor(), null);
}
}
@ -176,7 +265,9 @@ public final class ExtensionRegistry extends ExtensionRegistryLite {
"ExtensionRegistry.add() must be provided a default instance when " +
"adding an embedded message extension.");
}
add(new ExtensionInfo(type, null));
ExtensionInfo info = new ExtensionInfo(type, null);
add(info, Extension.ExtensionType.IMMUTABLE);
add(info, Extension.ExtensionType.MUTABLE);
}
/** Add a message-type extension to the registry by descriptor. */
@ -186,40 +277,75 @@ public final class ExtensionRegistry extends ExtensionRegistryLite {
"ExtensionRegistry.add() provided a default instance for a " +
"non-message extension.");
}
add(new ExtensionInfo(type, defaultInstance));
add(new ExtensionInfo(type, defaultInstance),
Extension.ExtensionType.IMMUTABLE);
}
// =================================================================
// Private stuff.
private ExtensionRegistry() {
this.extensionsByName = new HashMap<String, ExtensionInfo>();
this.extensionsByNumber = new HashMap<DescriptorIntPair, ExtensionInfo>();
this.immutableExtensionsByName = new HashMap<String, ExtensionInfo>();
this.mutableExtensionsByName = new HashMap<String, ExtensionInfo>();
this.immutableExtensionsByNumber =
new HashMap<DescriptorIntPair, ExtensionInfo>();
this.mutableExtensionsByNumber =
new HashMap<DescriptorIntPair, ExtensionInfo>();
}
private ExtensionRegistry(ExtensionRegistry other) {
super(other);
this.extensionsByName = Collections.unmodifiableMap(other.extensionsByName);
this.extensionsByNumber =
Collections.unmodifiableMap(other.extensionsByNumber);
this.immutableExtensionsByName =
Collections.unmodifiableMap(other.immutableExtensionsByName);
this.mutableExtensionsByName =
Collections.unmodifiableMap(other.mutableExtensionsByName);
this.immutableExtensionsByNumber =
Collections.unmodifiableMap(other.immutableExtensionsByNumber);
this.mutableExtensionsByNumber =
Collections.unmodifiableMap(other.mutableExtensionsByNumber);
}
private final Map<String, ExtensionInfo> extensionsByName;
private final Map<DescriptorIntPair, ExtensionInfo> extensionsByNumber;
private final Map<String, ExtensionInfo> immutableExtensionsByName;
private final Map<String, ExtensionInfo> mutableExtensionsByName;
private final Map<DescriptorIntPair, ExtensionInfo> immutableExtensionsByNumber;
private final Map<DescriptorIntPair, ExtensionInfo> mutableExtensionsByNumber;
private ExtensionRegistry(boolean empty) {
ExtensionRegistry(boolean empty) {
super(ExtensionRegistryLite.getEmptyRegistry());
this.extensionsByName = Collections.<String, ExtensionInfo>emptyMap();
this.extensionsByNumber =
this.immutableExtensionsByName =
Collections.<String, ExtensionInfo>emptyMap();
this.mutableExtensionsByName =
Collections.<String, ExtensionInfo>emptyMap();
this.immutableExtensionsByNumber =
Collections.<DescriptorIntPair, ExtensionInfo>emptyMap();
this.mutableExtensionsByNumber =
Collections.<DescriptorIntPair, ExtensionInfo>emptyMap();
}
private static final ExtensionRegistry EMPTY = new ExtensionRegistry(true);
private void add(final ExtensionInfo extension) {
private void add(
final ExtensionInfo extension,
final Extension.ExtensionType extensionType) {
if (!extension.descriptor.isExtension()) {
throw new IllegalArgumentException(
"ExtensionRegistry.add() was given a FieldDescriptor for a regular " +
"(non-extension) field.");
"ExtensionRegistry.add() was given a FieldDescriptor for a regular " +
"(non-extension) field.");
}
Map<String, ExtensionInfo> extensionsByName;
Map<DescriptorIntPair, ExtensionInfo> extensionsByNumber;
switch (extensionType) {
case IMMUTABLE:
extensionsByName = immutableExtensionsByName;
extensionsByNumber = immutableExtensionsByNumber;
break;
case MUTABLE:
extensionsByName = mutableExtensionsByName;
extensionsByNumber = mutableExtensionsByNumber;
break;
default:
// Ignore the unknown supported type.
return;
}
extensionsByName.put(extension.descriptor.getFullName(), extension);

@ -146,6 +146,7 @@ final class FieldSet<FieldDescriptorType extends
return clone;
}
// =================================================================
/** See {@link Message.Builder#clear()}. */
@ -376,10 +377,13 @@ final class FieldSet<FieldDescriptorType extends
case DOUBLE: isValid = value instanceof Double ; break;
case BOOLEAN: isValid = value instanceof Boolean ; break;
case STRING: isValid = value instanceof String ; break;
case BYTE_STRING: isValid = value instanceof ByteString; break;
case BYTE_STRING:
isValid = value instanceof ByteString || value instanceof byte[];
break;
case ENUM:
// TODO(kenton): Caller must do type checking here, I guess.
isValid = value instanceof Internal.EnumLite;
isValid =
(value instanceof Integer || value instanceof Internal.EnumLite);
break;
case MESSAGE:
// TODO(kenton): Caller must do type checking here, I guess.
@ -483,6 +487,17 @@ final class FieldSet<FieldDescriptorType extends
}
}
private Object cloneIfMutable(Object value) {
if (value instanceof byte[]) {
byte[] bytes = (byte[]) value;
byte[] copy = new byte[bytes.length];
System.arraycopy(bytes, 0, copy, 0, bytes.length);
return copy;
} else {
return value;
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
private void mergeFromField(
final Map.Entry<FieldDescriptorType, Object> entry) {
@ -495,28 +510,26 @@ final class FieldSet<FieldDescriptorType extends
if (descriptor.isRepeated()) {
Object value = getField(descriptor);
if (value == null) {
// Our list is empty, but we still need to make a defensive copy of
// the other list since we don't know if the other FieldSet is still
// mutable.
fields.put(descriptor, new ArrayList((List) otherValue));
} else {
// Concatenate the lists.
((List) value).addAll((List) otherValue);
value = new ArrayList();
}
for (Object element : (List) otherValue) {
((List) value).add(cloneIfMutable(element));
}
fields.put(descriptor, value);
} else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
Object value = getField(descriptor);
if (value == null) {
fields.put(descriptor, otherValue);
fields.put(descriptor, cloneIfMutable(otherValue));
} else {
// Merge the messages.
fields.put(
descriptor,
descriptor.internalMergeFrom(
value = descriptor.internalMergeFrom(
((MessageLite) value).toBuilder(), (MessageLite) otherValue)
.build());
.build();
fields.put(descriptor, value);
}
} else {
fields.put(descriptor, otherValue);
fields.put(descriptor, cloneIfMutable(otherValue));
}
}
@ -524,11 +537,13 @@ final class FieldSet<FieldDescriptorType extends
// other class. Probably WireFormat.
/**
* Read a field of any primitive type from a CodedInputStream. Enums,
* groups, and embedded messages are not handled by this method.
* Read a field of any primitive type for immutable messages from a
* CodedInputStream. Enums, groups, and embedded messages are not handled by
* this method.
*
* @param input The stream from which to read.
* @param type Declared type of the field.
* @param checkUtf8 When true, check that the input is valid utf8.
* @return An object representing the field's value, of the exact
* type which would be returned by
* {@link Message#getField(Descriptors.FieldDescriptor)} for
@ -536,7 +551,8 @@ final class FieldSet<FieldDescriptorType extends
*/
public static Object readPrimitiveField(
CodedInputStream input,
final WireFormat.FieldType type) throws IOException {
final WireFormat.FieldType type,
boolean checkUtf8) throws IOException {
switch (type) {
case DOUBLE : return input.readDouble ();
case FLOAT : return input.readFloat ();
@ -546,7 +562,11 @@ final class FieldSet<FieldDescriptorType extends
case FIXED64 : return input.readFixed64 ();
case FIXED32 : return input.readFixed32 ();
case BOOL : return input.readBool ();
case STRING : return input.readString ();
case STRING : if (checkUtf8) {
return input.readStringRequireUtf8();
} else {
return input.readString();
}
case BYTES : return input.readBytes ();
case UINT32 : return input.readUInt32 ();
case SFIXED32: return input.readSFixed32();
@ -571,6 +591,7 @@ final class FieldSet<FieldDescriptorType extends
"There is no way to get here, but the compiler thinks otherwise.");
}
/** See {@link Message#writeTo(CodedOutputStream)}. */
public void writeTo(final CodedOutputStream output)
throws IOException {
@ -605,8 +626,12 @@ final class FieldSet<FieldDescriptorType extends
final FieldDescriptorType descriptor = entry.getKey();
if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE &&
!descriptor.isRepeated() && !descriptor.isPacked()) {
Object value = entry.getValue();
if (value instanceof LazyField) {
value = ((LazyField) value).getValue();
}
output.writeMessageSetExtension(entry.getKey().getNumber(),
(MessageLite) entry.getValue());
(MessageLite) value);
} else {
writeField(descriptor, entry.getValue(), output);
}
@ -630,7 +655,7 @@ final class FieldSet<FieldDescriptorType extends
// Special case for groups, which need a start and end tag; other fields
// can just use writeTag() and writeFieldNoTag().
if (type == WireFormat.FieldType.GROUP) {
output.writeGroup(number, (MessageLite) value);
output.writeGroup(number, (MessageLite) value);
} else {
output.writeTag(number, getWireFormatForFieldType(type, false));
writeElementNoTag(output, type, value);
@ -663,7 +688,13 @@ final class FieldSet<FieldDescriptorType extends
case STRING : output.writeStringNoTag ((String ) value); break;
case GROUP : output.writeGroupNoTag ((MessageLite) value); break;
case MESSAGE : output.writeMessageNoTag ((MessageLite) value); break;
case BYTES : output.writeBytesNoTag ((ByteString ) value); break;
case BYTES:
if (value instanceof ByteString) {
output.writeBytesNoTag((ByteString) value);
} else {
output.writeByteArrayNoTag((byte[]) value);
}
break;
case UINT32 : output.writeUInt32NoTag ((Integer ) value); break;
case SFIXED32: output.writeSFixed32NoTag((Integer ) value); break;
case SFIXED64: output.writeSFixed64NoTag((Long ) value); break;
@ -671,7 +702,11 @@ final class FieldSet<FieldDescriptorType extends
case SINT64 : output.writeSInt64NoTag ((Long ) value); break;
case ENUM:
output.writeEnumNoTag(((Internal.EnumLite) value).getNumber());
if (value instanceof Internal.EnumLite) {
output.writeEnumNoTag(((Internal.EnumLite) value).getNumber());
} else {
output.writeEnumNoTag(((Integer) value).intValue());
}
break;
}
}
@ -778,7 +813,9 @@ final class FieldSet<FieldDescriptorType extends
final int number, final Object value) {
int tagSize = CodedOutputStream.computeTagSize(number);
if (type == WireFormat.FieldType.GROUP) {
tagSize *= 2;
// Only count the end group tag for proto2 messages as for proto1 the end
// group tag will be counted as a part of getSerializedSize().
tagSize *= 2;
}
return tagSize + computeElementSizeNoTag(type, value);
}
@ -808,7 +845,12 @@ final class FieldSet<FieldDescriptorType extends
case BOOL : return CodedOutputStream.computeBoolSizeNoTag ((Boolean )value);
case STRING : return CodedOutputStream.computeStringSizeNoTag ((String )value);
case GROUP : return CodedOutputStream.computeGroupSizeNoTag ((MessageLite)value);
case BYTES : return CodedOutputStream.computeBytesSizeNoTag ((ByteString )value);
case BYTES :
if (value instanceof ByteString) {
return CodedOutputStream.computeBytesSizeNoTag((ByteString) value);
} else {
return CodedOutputStream.computeByteArraySizeNoTag((byte[]) value);
}
case UINT32 : return CodedOutputStream.computeUInt32SizeNoTag ((Integer )value);
case SFIXED32: return CodedOutputStream.computeSFixed32SizeNoTag((Integer )value);
case SFIXED64: return CodedOutputStream.computeSFixed64SizeNoTag((Long )value);
@ -823,8 +865,12 @@ final class FieldSet<FieldDescriptorType extends
}
case ENUM:
return CodedOutputStream.computeEnumSizeNoTag(
((Internal.EnumLite) value).getNumber());
if (value instanceof Internal.EnumLite) {
return CodedOutputStream.computeEnumSizeNoTag(
((Internal.EnumLite) value).getNumber());
} else {
return CodedOutputStream.computeEnumSizeNoTag((Integer) value);
}
}
throw new RuntimeException(

@ -33,6 +33,8 @@ package com.google.protobuf;
import com.google.protobuf.Descriptors.Descriptor;
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 java.io.IOException;
import java.io.ObjectStreamException;
@ -71,7 +73,7 @@ public abstract class GeneratedMessage extends AbstractMessage
protected GeneratedMessage(Builder<?> builder) {
}
public Parser<? extends Message> getParserForType() {
public Parser<? extends GeneratedMessage> getParserForType() {
throw new UnsupportedOperationException(
"This is supposed to be overridden by subclasses.");
}
@ -153,6 +155,16 @@ public abstract class GeneratedMessage extends AbstractMessage
return Collections.unmodifiableMap(getAllFieldsMutable());
}
//@Override (Java 1.6 override semantics, but we must support 1.5)
public boolean hasOneof(final OneofDescriptor oneof) {
return internalGetFieldAccessorTable().getOneof(oneof).has(this);
}
//@Override (Java 1.6 override semantics, but we must support 1.5)
public FieldDescriptor getOneofFieldDescriptor(final OneofDescriptor oneof) {
return internalGetFieldAccessorTable().getOneof(oneof).get(this);
}
//@Override (Java 1.6 override semantics, but we must support 1.5)
public boolean hasField(final FieldDescriptor field) {
return internalGetFieldAccessorTable().getField(field).has(this);
@ -193,6 +205,7 @@ public abstract class GeneratedMessage extends AbstractMessage
return unknownFields.mergeFieldFrom(tag, input);
}
/**
* Used by parsing constructors in generated classes.
*/
@ -344,6 +357,16 @@ public abstract class GeneratedMessage extends AbstractMessage
return internalGetFieldAccessorTable().getField(field).getBuilder(this);
}
//@Override (Java 1.6 override semantics, but we must support 1.5)
public boolean hasOneof(final OneofDescriptor oneof) {
return internalGetFieldAccessorTable().getOneof(oneof).has(this);
}
//@Override (Java 1.6 override semantics, but we must support 1.5)
public FieldDescriptor getOneofFieldDescriptor(final OneofDescriptor oneof) {
return internalGetFieldAccessorTable().getOneof(oneof).get(this);
}
//@Override (Java 1.6 override semantics, but we must support 1.5)
public boolean hasField(final FieldDescriptor field) {
return internalGetFieldAccessorTable().getField(field).has(this);
@ -373,6 +396,12 @@ public abstract class GeneratedMessage extends AbstractMessage
return (BuilderType) this;
}
//@Override (Java 1.6 override semantics, but we must support 1.5)
public BuilderType clearOneof(final OneofDescriptor oneof) {
internalGetFieldAccessorTable().getOneof(oneof).clear(this);
return (BuilderType) this;
}
//@Override (Java 1.6 override semantics, but we must support 1.5)
public int getRepeatedFieldCount(final FieldDescriptor field) {
return internalGetFieldAccessorTable().getField(field)
@ -507,21 +536,24 @@ public abstract class GeneratedMessage extends AbstractMessage
public interface ExtendableMessageOrBuilder<
MessageType extends ExtendableMessage> extends MessageOrBuilder {
// Re-define for return type covariance.
Message getDefaultInstanceForType();
/** Check if a singular extension is present. */
<Type> boolean hasExtension(
GeneratedExtension<MessageType, Type> extension);
Extension<MessageType, Type> extension);
/** Get the number of elements in a repeated extension. */
<Type> int getExtensionCount(
GeneratedExtension<MessageType, List<Type>> extension);
Extension<MessageType, List<Type>> extension);
/** Get the value of an extension. */
<Type> Type getExtension(GeneratedExtension<MessageType, Type> extension);
<Type> Type getExtension(
Extension<MessageType, Type> extension);
/** Get one element of a repeated extension. */
<Type> Type getExtension(
GeneratedExtension<MessageType, List<Type>> extension,
Extension<MessageType, List<Type>> extension,
int index);
}
@ -578,7 +610,7 @@ public abstract class GeneratedMessage extends AbstractMessage
}
private void verifyExtensionContainingType(
final GeneratedExtension<MessageType, ?> extension) {
final Extension<MessageType, ?> extension) {
if (extension.getDescriptor().getContainingType() !=
getDescriptorForType()) {
// This can only happen if someone uses unchecked operations.
@ -593,7 +625,7 @@ 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 GeneratedExtension<MessageType, Type> extension) {
final Extension<MessageType, Type> extension) {
verifyExtensionContainingType(extension);
return extensions.hasField(extension.getDescriptor());
}
@ -601,7 +633,7 @@ 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 GeneratedExtension<MessageType, List<Type>> extension) {
final Extension<MessageType, List<Type>> extension) {
verifyExtensionContainingType(extension);
final FieldDescriptor descriptor = extension.getDescriptor();
return extensions.getRepeatedFieldCount(descriptor);
@ -611,7 +643,7 @@ 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 GeneratedExtension<MessageType, Type> extension) {
final Extension<MessageType, Type> extension) {
verifyExtensionContainingType(extension);
FieldDescriptor descriptor = extension.getDescriptor();
final Object value = extensions.getField(descriptor);
@ -634,7 +666,7 @@ 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 GeneratedExtension<MessageType, List<Type>> extension,
final Extension<MessageType, List<Type>> extension,
final int index) {
verifyExtensionContainingType(extension);
FieldDescriptor descriptor = extension.getDescriptor();
@ -658,11 +690,12 @@ public abstract class GeneratedMessage extends AbstractMessage
UnknownFieldSet.Builder unknownFields,
ExtensionRegistryLite extensionRegistry,
int tag) throws IOException {
return AbstractMessage.Builder.mergeFieldFrom(
input, unknownFields, extensionRegistry, getDescriptorForType(),
null, extensions, tag);
return MessageReflection.mergeFieldFrom(
input, unknownFields, extensionRegistry, getDescriptorForType(),
new MessageReflection.ExtensionAdapter(extensions), tag);
}
/**
* Used by parsing constructors in generated classes.
*/
@ -868,6 +901,11 @@ public abstract class GeneratedMessage extends AbstractMessage
super(parent);
}
// For immutable message conversion.
void internalSetExtensionSet(FieldSet<FieldDescriptor> extensions) {
this.extensions = extensions;
}
@Override
public BuilderType clear() {
extensions = FieldSet.emptySet();
@ -890,7 +928,7 @@ public abstract class GeneratedMessage extends AbstractMessage
}
private void verifyExtensionContainingType(
final GeneratedExtension<MessageType, ?> extension) {
final Extension<MessageType, ?> extension) {
if (extension.getDescriptor().getContainingType() !=
getDescriptorForType()) {
// This can only happen if someone uses unchecked operations.
@ -905,7 +943,7 @@ 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 GeneratedExtension<MessageType, Type> extension) {
final Extension<MessageType, Type> extension) {
verifyExtensionContainingType(extension);
return extensions.hasField(extension.getDescriptor());
}
@ -913,7 +951,7 @@ 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 GeneratedExtension<MessageType, List<Type>> extension) {
final Extension<MessageType, List<Type>> extension) {
verifyExtensionContainingType(extension);
final FieldDescriptor descriptor = extension.getDescriptor();
return extensions.getRepeatedFieldCount(descriptor);
@ -922,7 +960,7 @@ 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 GeneratedExtension<MessageType, Type> extension) {
final Extension<MessageType, Type> extension) {
verifyExtensionContainingType(extension);
FieldDescriptor descriptor = extension.getDescriptor();
final Object value = extensions.getField(descriptor);
@ -944,7 +982,7 @@ 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 GeneratedExtension<MessageType, List<Type>> extension,
final Extension<MessageType, List<Type>> extension,
final int index) {
verifyExtensionContainingType(extension);
FieldDescriptor descriptor = extension.getDescriptor();
@ -954,7 +992,7 @@ public abstract class GeneratedMessage extends AbstractMessage
/** Set the value of an extension. */
public final <Type> BuilderType setExtension(
final GeneratedExtension<MessageType, Type> extension,
final Extension<MessageType, Type> extension,
final Type value) {
verifyExtensionContainingType(extension);
ensureExtensionsIsMutable();
@ -966,7 +1004,7 @@ public abstract class GeneratedMessage extends AbstractMessage
/** Set the value of one element of a repeated extension. */
public final <Type> BuilderType setExtension(
final GeneratedExtension<MessageType, List<Type>> extension,
final Extension<MessageType, List<Type>> extension,
final int index, final Type value) {
verifyExtensionContainingType(extension);
ensureExtensionsIsMutable();
@ -980,7 +1018,7 @@ public abstract class GeneratedMessage extends AbstractMessage
/** Append a value to a repeated extension. */
public final <Type> BuilderType addExtension(
final GeneratedExtension<MessageType, List<Type>> extension,
final Extension<MessageType, List<Type>> extension,
final Type value) {
verifyExtensionContainingType(extension);
ensureExtensionsIsMutable();
@ -993,7 +1031,7 @@ public abstract class GeneratedMessage extends AbstractMessage
/** Clear an extension. */
public final <Type> BuilderType clearExtension(
final GeneratedExtension<MessageType, ?> extension) {
final Extension<MessageType, ?> extension) {
verifyExtensionContainingType(extension);
ensureExtensionsIsMutable();
extensions.clearField(extension.getDescriptor());
@ -1030,9 +1068,9 @@ public abstract class GeneratedMessage extends AbstractMessage
final UnknownFieldSet.Builder unknownFields,
final ExtensionRegistryLite extensionRegistry,
final int tag) throws IOException {
return AbstractMessage.Builder.mergeFieldFrom(
input, unknownFields, extensionRegistry, getDescriptorForType(),
this, null, tag);
return MessageReflection.mergeFieldFrom(
input, unknownFields, extensionRegistry, getDescriptorForType(),
new MessageReflection.BuilderAdapter(this), tag);
}
// ---------------------------------------------------------------
@ -1172,7 +1210,7 @@ public abstract class GeneratedMessage extends AbstractMessage
* Gets the descriptor for an extension. The implementation depends on whether
* the extension is scoped in the top level of a file or scoped in a Message.
*/
private static interface ExtensionDescriptorRetriever {
static interface ExtensionDescriptorRetriever {
FieldDescriptor getDescriptor();
}
@ -1187,15 +1225,16 @@ public abstract class GeneratedMessage extends AbstractMessage
// the outer class's descriptor, from which the extension descriptor is
// obtained.
return new GeneratedExtension<ContainingType, Type>(
new ExtensionDescriptorRetriever() {
new CachedDescriptorRetriever() {
//@Override (Java 1.6 override semantics, but we must support 1.5)
public FieldDescriptor getDescriptor() {
public FieldDescriptor loadDescriptor() {
return scope.getDescriptorForType().getExtensions()
.get(descriptorIndex);
}
},
singularType,
defaultInstance);
defaultInstance,
Extension.ExtensionType.IMMUTABLE);
}
/** For use by generated code only. */
@ -1209,7 +1248,87 @@ public abstract class GeneratedMessage extends AbstractMessage
return new GeneratedExtension<ContainingType, Type>(
null, // ExtensionDescriptorRetriever is initialized in internalInit();
singularType,
defaultInstance);
defaultInstance,
Extension.ExtensionType.IMMUTABLE);
}
private abstract static class CachedDescriptorRetriever
implements ExtensionDescriptorRetriever {
private volatile FieldDescriptor descriptor;
protected abstract FieldDescriptor loadDescriptor();
public FieldDescriptor getDescriptor() {
if (descriptor == null) {
synchronized (this) {
if (descriptor == null) {
descriptor = loadDescriptor();
}
}
}
return descriptor;
}
}
/**
* Used in proto1 generated code only.
*
* After enabling bridge, we can define proto2 extensions (the extended type
* is a proto2 mutable message) in a proto1 .proto file. For these extensions
* we should generate proto2 GeneratedExtensions.
*/
public static <ContainingType extends Message, Type>
GeneratedExtension<ContainingType, Type>
newMessageScopedGeneratedExtension(
final Message scope, final String name,
final Class singularType, final Message defaultInstance) {
// For extensions scoped within a Message, we use the Message to resolve
// the outer class's descriptor, from which the extension descriptor is
// obtained.
return new GeneratedExtension<ContainingType, Type>(
new CachedDescriptorRetriever() {
protected FieldDescriptor loadDescriptor() {
return scope.getDescriptorForType().findFieldByName(name);
}
},
singularType,
defaultInstance,
Extension.ExtensionType.MUTABLE);
}
/**
* Used in proto1 generated code only.
*
* After enabling bridge, we can define proto2 extensions (the extended type
* is a proto2 mutable message) in a proto1 .proto file. For these extensions
* we should generate proto2 GeneratedExtensions.
*/
public static <ContainingType extends Message, Type>
GeneratedExtension<ContainingType, Type>
newFileScopedGeneratedExtension(
final Class singularType, final Message defaultInstance,
final String descriptorOuterClass, final String extensionName) {
// For extensions scoped within a file, we load the descriptor outer
// class and rely on it to get the FileDescriptor which then can be
// used to obtain the extension's FieldDescriptor.
return new GeneratedExtension<ContainingType, Type>(
new CachedDescriptorRetriever() {
protected FieldDescriptor loadDescriptor() {
try {
Class clazz =
singularType.getClassLoader().loadClass(descriptorOuterClass);
FileDescriptor file =
(FileDescriptor) clazz.getField("descriptor").get(null);
return file.findExtensionByName(extensionName);
} catch (Exception e) {
throw new RuntimeException(
"Cannot load descriptors: " + descriptorOuterClass +
" is not a valid descriptor class name", e);
}
}
},
singularType,
defaultInstance,
Extension.ExtensionType.MUTABLE);
}
/**
@ -1237,8 +1356,9 @@ public abstract class GeneratedMessage extends AbstractMessage
* these static singletons as parameters to the extension accessors defined
* in {@link ExtendableMessage} and {@link ExtendableBuilder}.
*/
public static final class GeneratedExtension<
ContainingType extends Message, Type> {
public static class GeneratedExtension<
ContainingType extends Message, Type> extends
Extension<ContainingType, Type> {
// TODO(kenton): Find ways to avoid using Java reflection within this
// class. Also try to avoid suppressing unchecked warnings.
@ -1254,9 +1374,10 @@ public abstract class GeneratedMessage extends AbstractMessage
// In the case of non-nested extensions, we initialize the
// ExtensionDescriptorRetriever to null and rely on the outer class's static
// initializer to call internalInit() after the descriptor has been parsed.
private GeneratedExtension(ExtensionDescriptorRetriever descriptorRetriever,
Class singularType,
Message messageDefaultInstance) {
GeneratedExtension(ExtensionDescriptorRetriever descriptorRetriever,
Class singularType,
Message messageDefaultInstance,
ExtensionType extensionType) {
if (Message.class.isAssignableFrom(singularType) &&
!singularType.isInstance(messageDefaultInstance)) {
throw new IllegalArgumentException(
@ -1275,6 +1396,7 @@ public abstract class GeneratedMessage extends AbstractMessage
this.enumValueOf = null;
this.enumGetValueDescriptor = null;
}
this.extensionType = extensionType;
}
/** For use by generated code only. */
@ -1295,6 +1417,7 @@ public abstract class GeneratedMessage extends AbstractMessage
private final Message messageDefaultInstance;
private final Method enumValueOf;
private final Method enumGetValueDescriptor;
private final ExtensionType extensionType;
public FieldDescriptor getDescriptor() {
if (descriptorRetriever == null) {
@ -1312,14 +1435,19 @@ public abstract class GeneratedMessage extends AbstractMessage
return messageDefaultInstance;
}
protected ExtensionType getExtensionType() {
return extensionType;
}
/**
* Convert from the type used by the reflection accessors to the type used
* by native accessors. E.g., for enums, the reflection accessors use
* EnumValueDescriptors but the native accessors use the generated enum
* type.
*/
// @Override
@SuppressWarnings("unchecked")
private Object fromReflectionType(final Object value) {
protected Object fromReflectionType(final Object value) {
FieldDescriptor descriptor = getDescriptor();
if (descriptor.isRepeated()) {
if (descriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE ||
@ -1342,21 +1470,16 @@ public abstract class GeneratedMessage extends AbstractMessage
* Like {@link #fromReflectionType(Object)}, but if the type is a repeated
* type, this converts a single element.
*/
private Object singularFromReflectionType(final Object value) {
// @Override
protected Object singularFromReflectionType(final Object value) {
FieldDescriptor descriptor = getDescriptor();
switch (descriptor.getJavaType()) {
case MESSAGE:
if (singularType.isInstance(value)) {
return value;
} else {
// It seems the copy of the embedded message stored inside the
// extended message is not of the exact type the user was
// expecting. This can happen if a user defines a
// GeneratedExtension manually and gives it a different type.
// This should not happen in normal use. But, to be nice, we'll
// copy the message to whatever type the caller was expecting.
return messageDefaultInstance.newBuilderForType()
.mergeFrom((Message) value).build();
.mergeFrom((Message) value).build();
}
case ENUM:
return invokeOrDie(enumValueOf, null, (EnumValueDescriptor) value);
@ -1371,8 +1494,9 @@ public abstract class GeneratedMessage extends AbstractMessage
* EnumValueDescriptors but the native accessors use the generated enum
* type.
*/
// @Override
@SuppressWarnings("unchecked")
private Object toReflectionType(final Object value) {
protected Object toReflectionType(final Object value) {
FieldDescriptor descriptor = getDescriptor();
if (descriptor.isRepeated()) {
if (descriptor.getJavaType() == FieldDescriptor.JavaType.ENUM) {
@ -1394,7 +1518,8 @@ public abstract class GeneratedMessage extends AbstractMessage
* Like {@link #toReflectionType(Object)}, but if the type is a repeated
* type, this converts a single element.
*/
private Object singularToReflectionType(final Object value) {
// @Override
protected Object singularToReflectionType(final Object value) {
FieldDescriptor descriptor = getDescriptor();
switch (descriptor.getJavaType()) {
case ENUM:
@ -1403,6 +1528,34 @@ public abstract class GeneratedMessage extends AbstractMessage
return value;
}
}
// @Override
public int getNumber() {
return getDescriptor().getNumber();
}
// @Override
public WireFormat.FieldType getLiteType() {
return getDescriptor().getLiteType();
}
// @Override
public boolean isRepeated() {
return getDescriptor().isRepeated();
}
// @Override
@SuppressWarnings("unchecked")
public Type getDefaultValue() {
if (isRepeated()) {
return (Type) Collections.emptyList();
}
if (getDescriptor().getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
return (Type) messageDefaultInstance;
}
return (Type) singularFromReflectionType(
getDescriptor().getDefaultValue());
}
}
// =================================================================
@ -1477,6 +1630,7 @@ public abstract class GeneratedMessage extends AbstractMessage
this.descriptor = descriptor;
this.camelCaseNames = camelCaseNames;
fields = new FieldAccessor[descriptor.getFields().size()];
oneofs = new OneofAccessor[descriptor.getOneofs().size()];
initialized = false;
}
@ -1493,8 +1647,14 @@ public abstract class GeneratedMessage extends AbstractMessage
if (initialized) { return this; }
synchronized (this) {
if (initialized) { return this; }
for (int i = 0; i < fields.length; i++) {
int fieldsSize = fields.length;
for (int i = 0; i < fieldsSize; i++) {
FieldDescriptor field = descriptor.getFields().get(i);
String containingOneofCamelCaseName = null;
if (field.getContainingOneof() != null) {
containingOneofCamelCaseName =
camelCaseNames[fieldsSize + field.getContainingOneof().getIndex()];
}
if (field.isRepeated()) {
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
fields[i] = new RepeatedMessageFieldAccessor(
@ -1509,16 +1669,26 @@ public abstract class GeneratedMessage extends AbstractMessage
} else {
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
fields[i] = new SingularMessageFieldAccessor(
field, camelCaseNames[i], messageClass, builderClass);
field, camelCaseNames[i], messageClass, builderClass,
containingOneofCamelCaseName);
} else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM) {
fields[i] = new SingularEnumFieldAccessor(
field, camelCaseNames[i], messageClass, builderClass);
field, camelCaseNames[i], messageClass, builderClass,
containingOneofCamelCaseName);
} else {
fields[i] = new SingularFieldAccessor(
field, camelCaseNames[i], messageClass, builderClass);
field, camelCaseNames[i], messageClass, builderClass,
containingOneofCamelCaseName);
}
}
}
int oneofsSize = oneofs.length;
for (int i = 0; i < oneofsSize; i++) {
oneofs[i] = new OneofAccessor(
descriptor, camelCaseNames[i + fieldsSize],
messageClass, builderClass);
}
initialized = true;
camelCaseNames = null;
return this;
@ -1528,6 +1698,7 @@ public abstract class GeneratedMessage extends AbstractMessage
private final Descriptor descriptor;
private final FieldAccessor[] fields;
private String[] camelCaseNames;
private final OneofAccessor[] oneofs;
private volatile boolean initialized;
/** Get the FieldAccessor for a particular field. */
@ -1544,6 +1715,15 @@ public abstract class GeneratedMessage extends AbstractMessage
return fields[field.getIndex()];
}
/** Get the OneofAccessor for a particular oneof. */
private OneofAccessor getOneof(final OneofDescriptor oneof) {
if (oneof.getContainingType() != descriptor) {
throw new IllegalArgumentException(
"OneofDescriptor does not match message type.");
}
return oneofs[oneof.getIndex()];
}
/**
* Abstract interface that provides access to a single field. This is
* implemented differently depending on the field type and cardinality.
@ -1566,22 +1746,89 @@ public abstract class GeneratedMessage extends AbstractMessage
Message.Builder getBuilder(GeneratedMessage.Builder builder);
}
/** OneofAccessor provides access to a single oneof. */
private static class OneofAccessor {
OneofAccessor(
final Descriptor descriptor, final String camelCaseName,
final Class<? extends GeneratedMessage> messageClass,
final Class<? extends Builder> builderClass) {
this.descriptor = descriptor;
caseMethod =
getMethodOrDie(messageClass, "get" + camelCaseName + "Case");
caseMethodBuilder =
getMethodOrDie(builderClass, "get" + camelCaseName + "Case");
clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName);
}
private final Descriptor descriptor;
private final Method caseMethod;
private final Method caseMethodBuilder;
private final Method clearMethod;
public boolean has(final GeneratedMessage message) {
if (((Internal.EnumLite) invokeOrDie(caseMethod, message)).getNumber() == 0) {
return false;
}
return true;
}
public boolean has(GeneratedMessage.Builder builder) {
if (((Internal.EnumLite) invokeOrDie(caseMethodBuilder, builder)).getNumber() == 0) {
return false;
}
return true;
}
public FieldDescriptor get(final GeneratedMessage message) {
int fieldNumber = ((Internal.EnumLite) invokeOrDie(caseMethod, message)).getNumber();
if (fieldNumber > 0) {
return descriptor.findFieldByNumber(fieldNumber);
}
return null;
}
public FieldDescriptor get(GeneratedMessage.Builder builder) {
int fieldNumber = ((Internal.EnumLite) invokeOrDie(caseMethodBuilder, builder)).getNumber();
if (fieldNumber > 0) {
return descriptor.findFieldByNumber(fieldNumber);
}
return null;
}
public void clear(final Builder builder) {
invokeOrDie(clearMethod, builder);
}
}
private static boolean supportFieldPresence(FileDescriptor file) {
return true;
}
// ---------------------------------------------------------------
private static class SingularFieldAccessor implements FieldAccessor {
SingularFieldAccessor(
final FieldDescriptor descriptor, final String camelCaseName,
final Class<? extends GeneratedMessage> messageClass,
final Class<? extends Builder> builderClass) {
final Class<? extends Builder> builderClass,
final String containingOneofCamelCaseName) {
field = descriptor;
isOneofField = descriptor.getContainingOneof() != null;
hasHasMethod = supportFieldPresence(descriptor.getFile())
|| (!isOneofField && descriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE);
getMethod = getMethodOrDie(messageClass, "get" + camelCaseName);
getMethodBuilder = getMethodOrDie(builderClass, "get" + camelCaseName);
type = getMethod.getReturnType();
setMethod = getMethodOrDie(builderClass, "set" + camelCaseName, type);
hasMethod =
getMethodOrDie(messageClass, "has" + camelCaseName);
hasHasMethod ? getMethodOrDie(messageClass, "has" + camelCaseName) : null;
hasMethodBuilder =
getMethodOrDie(builderClass, "has" + camelCaseName);
hasHasMethod ? getMethodOrDie(builderClass, "has" + camelCaseName) : null;
clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName);
caseMethod = isOneofField ? getMethodOrDie(
messageClass, "get" + containingOneofCamelCaseName + "Case") : null;
caseMethodBuilder = isOneofField ? getMethodOrDie(
builderClass, "get" + containingOneofCamelCaseName + "Case") : null;
}
// Note: We use Java reflection to call public methods rather than
@ -1594,6 +1841,19 @@ public abstract class GeneratedMessage extends AbstractMessage
protected final Method hasMethod;
protected final Method hasMethodBuilder;
protected final Method clearMethod;
protected final Method caseMethod;
protected final Method caseMethodBuilder;
protected final FieldDescriptor field;
protected final boolean isOneofField;
protected final boolean hasHasMethod;
private int getOneofFieldNumber(final GeneratedMessage message) {
return ((Internal.EnumLite) invokeOrDie(caseMethod, message)).getNumber();
}
private int getOneofFieldNumber(final GeneratedMessage.Builder builder) {
return ((Internal.EnumLite) invokeOrDie(caseMethodBuilder, builder)).getNumber();
}
public Object get(final GeneratedMessage message) {
return invokeOrDie(getMethod, message);
@ -1623,9 +1883,21 @@ public abstract class GeneratedMessage extends AbstractMessage
"addRepeatedField() called on a singular field.");
}
public boolean has(final GeneratedMessage message) {
if (!hasHasMethod) {
if (isOneofField) {
return getOneofFieldNumber(message) == field.getNumber();
}
return !get(message).equals(field.getDefaultValue());
}
return (Boolean) invokeOrDie(hasMethod, message);
}
public boolean has(GeneratedMessage.Builder builder) {
if (!hasHasMethod) {
if (isOneofField) {
return getOneofFieldNumber(builder) == field.getNumber();
}
return !get(builder).equals(field.getDefaultValue());
}
return (Boolean) invokeOrDie(hasMethodBuilder, builder);
}
public int getRepeatedCount(final GeneratedMessage message) {
@ -1751,8 +2023,9 @@ public abstract class GeneratedMessage extends AbstractMessage
SingularEnumFieldAccessor(
final FieldDescriptor descriptor, final String camelCaseName,
final Class<? extends GeneratedMessage> messageClass,
final Class<? extends Builder> builderClass) {
super(descriptor, camelCaseName, messageClass, builderClass);
final Class<? extends Builder> builderClass,
final String containingOneofCamelCaseName) {
super(descriptor, camelCaseName, messageClass, builderClass, containingOneofCamelCaseName);
valueOfMethod = getMethodOrDie(type, "valueOf",
EnumValueDescriptor.class);
@ -1847,8 +2120,9 @@ public abstract class GeneratedMessage extends AbstractMessage
SingularMessageFieldAccessor(
final FieldDescriptor descriptor, final String camelCaseName,
final Class<? extends GeneratedMessage> messageClass,
final Class<? extends Builder> builderClass) {
super(descriptor, camelCaseName, messageClass, builderClass);
final Class<? extends Builder> builderClass,
final String containingOneofCamelCaseName) {
super(descriptor, camelCaseName, messageClass, builderClass, containingOneofCamelCaseName);
newBuilderMethod = getMethodOrDie(type, "newBuilder");
getBuilderMethodBuilder =

@ -35,6 +35,7 @@ import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@ -66,9 +67,10 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
*/
protected boolean parseUnknownField(
CodedInputStream input,
CodedOutputStream unknownFieldsCodedOutput,
ExtensionRegistryLite extensionRegistry,
int tag) throws IOException {
return input.skipField(tag);
return input.skipField(tag, unknownFieldsCodedOutput);
}
/**
@ -86,6 +88,7 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
//@Override (Java 1.6 override semantics, but we must support 1.5)
public BuilderType clear() {
unknownFields = ByteString.EMPTY;
return (BuilderType) this;
}
@ -110,12 +113,25 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
*/
protected boolean parseUnknownField(
CodedInputStream input,
CodedOutputStream unknownFieldsCodedOutput,
ExtensionRegistryLite extensionRegistry,
int tag) throws IOException {
return input.skipField(tag);
return input.skipField(tag, unknownFieldsCodedOutput);
}
public final ByteString getUnknownFields() {
return unknownFields;
}
public final BuilderType setUnknownFields(final ByteString unknownFields) {
this.unknownFields = unknownFields;
return (BuilderType) this;
}
private ByteString unknownFields = ByteString.EMPTY;
}
// =================================================================
// Extensions-related stuff
@ -197,7 +213,7 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
if (value == null) {
return extension.defaultValue;
} else {
return (Type) value;
return (Type) extension.fromFieldSetType(value);
}
}
@ -208,7 +224,8 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
final GeneratedExtension<MessageType, List<Type>> extension,
final int index) {
verifyExtensionContainingType(extension);
return (Type) extensions.getRepeatedField(extension.descriptor, index);
return (Type) extension.singularFromFieldSetType(
extensions.getRepeatedField(extension.descriptor, index));
}
/** Called by subclasses to check if all extensions are initialized. */
@ -223,16 +240,19 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
@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.
*/
@ -314,6 +334,11 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
private FieldSet<ExtensionDescriptor> extensions = FieldSet.emptySet();
private boolean extensionsIsMutable;
// For immutable message conversion.
void internalSetExtensionSet(FieldSet<ExtensionDescriptor> extensions) {
this.extensions = extensions;
}
@Override
public BuilderType clear() {
extensions.clear();
@ -375,7 +400,7 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
if (value == null) {
return extension.defaultValue;
} else {
return (Type) value;
return (Type) extension.fromFieldSetType(value);
}
}
@ -386,7 +411,8 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
final GeneratedExtension<MessageType, List<Type>> extension,
final int index) {
verifyExtensionContainingType(extension);
return (Type) extensions.getRepeatedField(extension.descriptor, index);
return (Type) extension.singularFromFieldSetType(
extensions.getRepeatedField(extension.descriptor, index));
}
// This is implemented here only to work around an apparent bug in the
@ -404,7 +430,8 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
final Type value) {
verifyExtensionContainingType(extension);
ensureExtensionsIsMutable();
extensions.setField(extension.descriptor, value);
extensions.setField(extension.descriptor,
extension.toFieldSetType(value));
return (BuilderType) this;
}
@ -414,7 +441,8 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
final int index, final Type value) {
verifyExtensionContainingType(extension);
ensureExtensionsIsMutable();
extensions.setRepeatedField(extension.descriptor, index, value);
extensions.setRepeatedField(extension.descriptor, index,
extension.singularToFieldSetType(value));
return (BuilderType) this;
}
@ -424,7 +452,8 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
final Type value) {
verifyExtensionContainingType(extension);
ensureExtensionsIsMutable();
extensions.addRepeatedField(extension.descriptor, value);
extensions.addRepeatedField(extension.descriptor,
extension.singularToFieldSetType(value));
return (BuilderType) this;
}
@ -449,6 +478,7 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
@Override
protected boolean parseUnknownField(
CodedInputStream input,
CodedOutputStream unknownFieldsCodedOutput,
ExtensionRegistryLite extensionRegistry,
int tag) throws IOException {
ensureExtensionsIsMutable();
@ -456,6 +486,7 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
extensions,
getDefaultInstanceForType(),
input,
unknownFieldsCodedOutput,
extensionRegistry,
tag);
}
@ -477,6 +508,7 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
FieldSet<ExtensionDescriptor> extensions,
MessageType defaultInstance,
CodedInputStream input,
CodedOutputStream unknownFieldsCodedOutput,
ExtensionRegistryLite extensionRegistry,
int tag) throws IOException {
int wireType = WireFormat.getTagWireType(tag);
@ -505,7 +537,7 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
}
if (unknown) { // Unknown field or wrong wire type. Skip.
return input.skipField(tag);
return input.skipField(tag, unknownFieldsCodedOutput);
}
if (packed) {
@ -521,13 +553,15 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
// enum, drop it (don't even add it to unknownFields).
return true;
}
extensions.addRepeatedField(extension.descriptor, value);
extensions.addRepeatedField(extension.descriptor,
extension.singularToFieldSetType(value));
}
} else {
while (input.getBytesUntilLimit() > 0) {
Object value =
FieldSet.readPrimitiveField(input,
extension.descriptor.getLiteType());
FieldSet.readPrimitiveField(input,
extension.descriptor.getLiteType(),
/*checkUtf8=*/ false);
extensions.addRepeatedField(extension.descriptor, value);
}
}
@ -545,7 +579,8 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
}
}
if (subBuilder == null) {
subBuilder = extension.messageDefaultInstance.newBuilderForType();
subBuilder = extension.getMessageDefaultInstance()
.newBuilderForType();
}
if (extension.descriptor.getLiteType() ==
WireFormat.FieldType.GROUP) {
@ -562,21 +597,26 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
value = extension.descriptor.getEnumType()
.findValueByNumber(rawValue);
// If the number isn't recognized as a valid value for this enum,
// drop it.
// write it to unknown fields object.
if (value == null) {
unknownFieldsCodedOutput.writeRawVarint32(tag);
unknownFieldsCodedOutput.writeUInt32NoTag(rawValue);
return true;
}
break;
default:
value = FieldSet.readPrimitiveField(input,
extension.descriptor.getLiteType());
extension.descriptor.getLiteType(),
/*checkUtf8=*/ false);
break;
}
if (extension.descriptor.isRepeated()) {
extensions.addRepeatedField(extension.descriptor, value);
extensions.addRepeatedField(extension.descriptor,
extension.singularToFieldSetType(value));
} else {
extensions.setField(extension.descriptor, value);
extensions.setField(extension.descriptor,
extension.singularToFieldSetType(value));
}
}
@ -594,14 +634,16 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
final MessageLite messageDefaultInstance,
final Internal.EnumLiteMap<?> enumTypeMap,
final int number,
final WireFormat.FieldType type) {
final WireFormat.FieldType type,
final Class singularType) {
return new GeneratedExtension<ContainingType, Type>(
containingTypeDefaultInstance,
defaultValue,
messageDefaultInstance,
new ExtensionDescriptor(enumTypeMap, number, type,
false /* isRepeated */,
false /* isPacked */));
false /* isPacked */),
singularType);
}
/** For use by generated code only. */
@ -613,7 +655,8 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
final Internal.EnumLiteMap<?> enumTypeMap,
final int number,
final WireFormat.FieldType type,
final boolean isPacked) {
final boolean isPacked,
final Class singularType) {
@SuppressWarnings("unchecked") // Subclasses ensure Type is a List
Type emptyList = (Type) Collections.emptyList();
return new GeneratedExtension<ContainingType, Type>(
@ -621,13 +664,14 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
emptyList,
messageDefaultInstance,
new ExtensionDescriptor(
enumTypeMap, number, type, true /* isRepeated */, isPacked));
enumTypeMap, number, type, true /* isRepeated */, isPacked),
singularType);
}
private static final class ExtensionDescriptor
static final class ExtensionDescriptor
implements FieldSet.FieldDescriptorLite<
ExtensionDescriptor> {
private ExtensionDescriptor(
ExtensionDescriptor(
final Internal.EnumLiteMap<?> enumTypeMap,
final int number,
final WireFormat.FieldType type,
@ -640,11 +684,11 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
this.isPacked = isPacked;
}
private final Internal.EnumLiteMap<?> enumTypeMap;
private final int number;
private final WireFormat.FieldType type;
private final boolean isRepeated;
private final boolean isPacked;
final Internal.EnumLiteMap<?> enumTypeMap;
final int number;
final WireFormat.FieldType type;
final boolean isRepeated;
final boolean isPacked;
public int getNumber() {
return number;
@ -676,25 +720,70 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
return ((Builder) to).mergeFrom((GeneratedMessageLite) from);
}
public int compareTo(ExtensionDescriptor other) {
return number - other.number;
}
}
// =================================================================
/** Calls Class.getMethod and throws a RuntimeException if it fails. */
@SuppressWarnings("unchecked")
static Method getMethodOrDie(Class clazz, String name, Class... params) {
try {
return clazz.getMethod(name, params);
} catch (NoSuchMethodException e) {
throw new RuntimeException(
"Generated message class \"" + clazz.getName() +
"\" missing method \"" + name + "\".", e);
}
}
/** Calls invoke and throws a RuntimeException if it fails. */
static Object invokeOrDie(Method method, Object object, Object... params) {
try {
return method.invoke(object, params);
} catch (IllegalAccessException e) {
throw new RuntimeException(
"Couldn't use Java reflection to implement protocol message " +
"reflection.", e);
} catch (InvocationTargetException e) {
final Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
} else {
throw new RuntimeException(
"Unexpected exception thrown by generated accessor method.", cause);
}
}
}
/**
* Lite equivalent to {@link GeneratedMessage.GeneratedExtension}.
*
* Users should ignore the contents of this class and only use objects of
* this type as parameters to extension accessors and ExtensionRegistry.add().
*/
public static final class GeneratedExtension<
public static class GeneratedExtension<
ContainingType extends MessageLite, Type> {
private GeneratedExtension(
/**
* Create a new isntance with the given parameters.
*
* The last parameter {@code singularType} is only needed for enum types.
* We store integer values for enum types in a {@link ExtendableMessage}
* and use Java reflection to convert an integer value back into a concrete
* enum object.
*/
GeneratedExtension(
final ContainingType containingTypeDefaultInstance,
final Type defaultValue,
final MessageLite messageDefaultInstance,
final ExtensionDescriptor descriptor) {
final ExtensionDescriptor descriptor,
Class singularType) {
// Defensive checks to verify the correct initialization order of
// GeneratedExtensions and their related GeneratedMessages.
if (containingTypeDefaultInstance == null) {
@ -710,12 +799,24 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
this.defaultValue = defaultValue;
this.messageDefaultInstance = messageDefaultInstance;
this.descriptor = descriptor;
// Use Java reflection to invoke the static method {@code valueOf} of
// enum types in order to convert integers to concrete enum objects.
this.singularType = singularType;
if (Internal.EnumLite.class.isAssignableFrom(singularType)) {
this.enumValueOf = getMethodOrDie(
singularType, "valueOf", int.class);
} else {
this.enumValueOf = null;
}
}
private final ContainingType containingTypeDefaultInstance;
private final Type defaultValue;
private final MessageLite messageDefaultInstance;
private final ExtensionDescriptor descriptor;
final ContainingType containingTypeDefaultInstance;
final Type defaultValue;
final MessageLite messageDefaultInstance;
final ExtensionDescriptor descriptor;
final Class singularType;
final Method enumValueOf;
/**
* Default instance of the type being extended, used to identify that type.
@ -729,13 +830,64 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
return descriptor.getNumber();
}
/**
* If the extension is an embedded message, this is the default instance of
* that type.
* If the extension is an embedded message or group, returns the default
* instance of the message.
*/
public MessageLite getMessageDefaultInstance() {
return messageDefaultInstance;
}
@SuppressWarnings("unchecked")
Object fromFieldSetType(final Object value) {
if (descriptor.isRepeated()) {
if (descriptor.getLiteJavaType() == WireFormat.JavaType.ENUM) {
final List result = new ArrayList();
for (final Object element : (List) value) {
result.add(singularFromFieldSetType(element));
}
return result;
} else {
return value;
}
} else {
return singularFromFieldSetType(value);
}
}
Object singularFromFieldSetType(final Object value) {
if (descriptor.getLiteJavaType() == WireFormat.JavaType.ENUM) {
return invokeOrDie(enumValueOf, null, (Integer) value);
} else {
return value;
}
}
@SuppressWarnings("unchecked")
Object toFieldSetType(final Object value) {
if (descriptor.isRepeated()) {
if (descriptor.getLiteJavaType() == WireFormat.JavaType.ENUM) {
final List result = new ArrayList();
for (final Object element : (List) value) {
result.add(singularToFieldSetType(element));
}
return result;
} else {
return value;
}
} else {
return singularToFieldSetType(value);
}
}
Object singularToFieldSetType(final Object value) {
if (descriptor.getLiteJavaType() == WireFormat.JavaType.ENUM) {
return ((Internal.EnumLite) value).getNumber();
} else {
return value;
}
}
}
/**

@ -30,7 +30,11 @@
package com.google.protobuf;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.List;
/**
* The classes contained within are used internally by the Protocol Buffer
@ -98,6 +102,51 @@ public class Internal {
"Java VM does not support a standard character set.", e);
}
}
/**
* Helper called by generated code to construct default values for bytes
* fields.
* <p>
* This is like {@link #bytesDefaultValue}, but returns a byte array.
*/
public static byte[] byteArrayDefaultValue(String bytes) {
try {
return bytes.getBytes("ISO-8859-1");
} catch (UnsupportedEncodingException e) {
// This should never happen since all JVMs are required to implement
// ISO-8859-1.
throw new IllegalStateException(
"Java VM does not support a standard character set.", e);
}
}
/**
* Helper called by generated code to construct default values for bytes
* fields.
* <p>
* This is like {@link #bytesDefaultValue}, but returns a ByteBuffer.
*/
public static ByteBuffer byteBufferDefaultValue(String bytes) {
return ByteBuffer.wrap(byteArrayDefaultValue(bytes));
}
/**
* Create a new ByteBuffer and copy all the content of {@code source}
* ByteBuffer to the new ByteBuffer. The new ByteBuffer's limit and
* capacity will be source.capacity(), and its position will be 0.
* Note that the state of {@code source} ByteBuffer won't be changed.
*/
public static ByteBuffer copyByteBuffer(ByteBuffer source) {
// Make a duplicate of the source ByteBuffer and read data from the
// duplicate. This is to avoid affecting the source ByteBuffer's state.
ByteBuffer temp = source.duplicate();
// We want to copy all the data in the source ByteBuffer, not just the
// remaining bytes.
temp.clear();
ByteBuffer result = ByteBuffer.allocate(temp.capacity());
result.put(temp);
result.clear();
return result;
}
/**
* Helper called by generated code to determine if a byte array is a valid
@ -130,6 +179,35 @@ public class Internal {
public static boolean isValidUtf8(ByteString byteString) {
return byteString.isValidUtf8();
}
/**
* Like {@link #isValidUtf8(ByteString)} but for byte arrays.
*/
public static boolean isValidUtf8(byte[] byteArray) {
return Utf8.isValidUtf8(byteArray);
}
/**
* Helper method to get the UTF-8 bytes of a string.
*/
public static byte[] toByteArray(String value) {
try {
return value.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("UTF-8 not supported?", e);
}
}
/**
* Helper method to convert a byte array to a string using UTF-8 encoding.
*/
public static String toStringUtf8(byte[] bytes) {
try {
return new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("UTF-8 not supported?", e);
}
}
/**
* Interface for an enum value or value descriptor, to be used in FieldSet.
@ -150,4 +228,164 @@ public class Internal {
public interface EnumLiteMap<T extends EnumLite> {
T findValueByNumber(int number);
}
/**
* Helper method for implementing {@link MessageLite#hashCode()} for longs.
* @see Long#hashCode()
*/
public static int hashLong(long n) {
return (int) (n ^ (n >>> 32));
}
/**
* Helper method for implementing {@link MessageLite#hashCode()} for
* booleans.
* @see Boolean#hashCode()
*/
public static int hashBoolean(boolean b) {
return b ? 1231 : 1237;
}
/**
* Helper method for implementing {@link MessageLite#hashCode()} for enums.
* <p>
* This is needed because {@link java.lang.Enum#hashCode()} is final, but we
* need to use the field number as the hash code to ensure compatibility
* between statically and dynamically generated enum objects.
*/
public static int hashEnum(EnumLite e) {
return e.getNumber();
}
/**
* Helper method for implementing {@link MessageLite#hashCode()} for
* enum lists.
*/
public static int hashEnumList(List<? extends EnumLite> list) {
int hash = 1;
for (EnumLite e : list) {
hash = 31 * hash + hashEnum(e);
}
return hash;
}
/**
* Helper method for implementing {@link MessageLite#equals()} for bytes field.
*/
public static boolean equals(List<byte[]> a, List<byte[]> b) {
if (a.size() != b.size()) return false;
for (int i = 0; i < a.size(); ++i) {
if (!Arrays.equals(a.get(i), b.get(i))) {
return false;
}
}
return true;
}
/**
* Helper method for implementing {@link MessageLite#hashCode()} for bytes field.
*/
public static int hashCode(List<byte[]> list) {
int hash = 1;
for (byte[] bytes : list) {
hash = 31 * hash + hashCode(bytes);
}
return hash;
}
/**
* Helper method for implementing {@link MessageLite#hashCode()} for bytes field.
*/
public static int hashCode(byte[] bytes) {
// The hash code for a byte array should be the same as the hash code for a
// ByteString with the same content. This is to ensure that the generated
// hashCode() method will return the same value as the pure reflection
// based hashCode() method.
return LiteralByteString.hashCode(bytes);
}
/**
* Helper method for implementing {@link MessageLite#equals()} for bytes
* field.
*/
public static boolean equalsByteBuffer(ByteBuffer a, ByteBuffer b) {
if (a.capacity() != b.capacity()) {
return false;
}
// ByteBuffer.equals() will only compare the remaining bytes, but we want to
// compare all the content.
return a.duplicate().clear().equals(b.duplicate().clear());
}
/**
* Helper method for implementing {@link MessageLite#equals()} for bytes
* field.
*/
public static boolean equalsByteBuffer(
List<ByteBuffer> a, List<ByteBuffer> b) {
if (a.size() != b.size()) {
return false;
}
for (int i = 0; i < a.size(); ++i) {
if (!equalsByteBuffer(a.get(i), b.get(i))) {
return false;
}
}
return true;
}
/**
* Helper method for implementing {@link MessageLite#hashCode()} for bytes
* field.
*/
public static int hashCodeByteBuffer(List<ByteBuffer> list) {
int hash = 1;
for (ByteBuffer bytes : list) {
hash = 31 * hash + hashCodeByteBuffer(bytes);
}
return hash;
}
private static final int DEFAULT_BUFFER_SIZE = 4096;
/**
* Helper method for implementing {@link MessageLite#hashCode()} for bytes
* field.
*/
public static int hashCodeByteBuffer(ByteBuffer bytes) {
if (bytes.hasArray()) {
// Fast path.
int h = LiteralByteString.hashCode(bytes.capacity(), bytes.array(),
bytes.arrayOffset(), bytes.capacity());
return h == 0 ? 1 : h;
} else {
// Read the data into a temporary byte array before calculating the
// hash value.
final int bufferSize = bytes.capacity() > DEFAULT_BUFFER_SIZE
? DEFAULT_BUFFER_SIZE : bytes.capacity();
final byte[] buffer = new byte[bufferSize];
final ByteBuffer duplicated = bytes.duplicate();
duplicated.clear();
int h = bytes.capacity();
while (duplicated.remaining() > 0) {
final int length = duplicated.remaining() <= bufferSize ?
duplicated.remaining() : bufferSize;
duplicated.get(buffer, 0, length);
h = LiteralByteString.hashCode(h, buffer, 0, length);
}
return h == 0 ? 1 : h;
}
}
/**
* An empty byte array constant used in generated code.
*/
public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
/**
* An empty byte array constant used in generated code.
*/
public static final ByteBuffer EMPTY_BYTE_BUFFER =
ByteBuffer.wrap(EMPTY_BYTE_ARRAY);
}

@ -111,4 +111,12 @@ public class InvalidProtocolBufferException extends IOException {
"Protocol message was too large. May be malicious. " +
"Use CodedInputStream.setSizeLimit() to increase the size limit.");
}
static InvalidProtocolBufferException parseFailure() {
return new InvalidProtocolBufferException("Failed to parse the message.");
}
static InvalidProtocolBufferException invalidUtf8() {
return new InvalidProtocolBufferException("Protocol message had invalid UTF-8.");
}
}

@ -30,7 +30,6 @@
package com.google.protobuf;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map.Entry;
@ -38,110 +37,49 @@ import java.util.Map.Entry;
* LazyField encapsulates the logic of lazily parsing message fields. It stores
* the message in a ByteString initially and then parse it on-demand.
*
* LazyField is thread-compatible e.g. concurrent read are safe, however,
* synchronizations are needed under read/write situations.
*
* Now LazyField is only used to lazily load MessageSet.
* TODO(xiangl): Use LazyField to lazily load all messages.
* Most of key methods are implemented in {@link LazyFieldLite} but this class
* can contain default instance of the message to provide {@code hashCode()},
* {@code euqals()} and {@code toString()}.
*
* @author xiangl@google.com (Xiang Li)
*/
class LazyField {
final private MessageLite defaultInstance;
final private ExtensionRegistryLite extensionRegistry;
public class LazyField extends LazyFieldLite {
// Mutable because it is initialized lazily.
private ByteString bytes;
private volatile MessageLite value;
private volatile boolean isDirty = false;
/**
* Carry a message's default instance which is used by {@code hashCode()}, {@code euqals()} and
* {@code toString()}.
*/
private final MessageLite defaultInstance;
public LazyField(MessageLite defaultInstance,
ExtensionRegistryLite extensionRegistry, ByteString bytes) {
this.defaultInstance = defaultInstance;
this.extensionRegistry = extensionRegistry;
this.bytes = bytes;
}
super(extensionRegistry, bytes);
public MessageLite getValue() {
ensureInitialized();
return value;
}
/**
* LazyField is not thread-safe for write access. Synchronizations are needed
* under read/write situations.
*/
public MessageLite setValue(MessageLite value) {
MessageLite originalValue = this.value;
this.value = value;
bytes = null;
isDirty = true;
return originalValue;
this.defaultInstance = defaultInstance;
}
/**
* Due to the optional field can be duplicated at the end of serialized
* bytes, which will make the serialized size changed after LazyField
* parsed. Be careful when using this method.
*/
public int getSerializedSize() {
if (isDirty) {
return value.getSerializedSize();
}
return bytes.size();
@Override
public boolean containsDefaultInstance() {
return super.containsDefaultInstance() || value == defaultInstance;
}
public ByteString toByteString() {
if (!isDirty) {
return bytes;
}
synchronized (this) {
if (!isDirty) {
return bytes;
}
bytes = value.toByteString();
isDirty = false;
return bytes;
}
public MessageLite getValue() {
return getValue(defaultInstance);
}
@Override
public int hashCode() {
ensureInitialized();
return value.hashCode();
return getValue().hashCode();
}
@Override
public boolean equals(Object obj) {
ensureInitialized();
return value.equals(obj);
return getValue().equals(obj);
}
@Override
public String toString() {
ensureInitialized();
return value.toString();
}
private void ensureInitialized() {
if (value != null) {
return;
}
synchronized (this) {
if (value != null) {
return;
}
try {
if (bytes != null) {
value = defaultInstance.getParserForType()
.parseFrom(bytes, extensionRegistry);
}
} catch (IOException e) {
// TODO(xiangl): Refactory the API to support the exception thrown from
// lazily load messages.
}
}
return getValue().toString();
}
// ====================================================
@ -157,10 +95,12 @@ class LazyField {
this.entry = entry;
}
// @Override
public K getKey() {
return entry.getKey();
}
// @Override
public Object getValue() {
LazyField field = entry.getValue();
if (field == null) {
@ -173,6 +113,7 @@ class LazyField {
return entry.getValue();
}
// @Override
public Object setValue(Object value) {
if (!(value instanceof MessageLite)) {
throw new IllegalArgumentException(
@ -190,11 +131,13 @@ class LazyField {
this.iterator = iterator;
}
// @Override
public boolean hasNext() {
return iterator.hasNext();
}
@SuppressWarnings("unchecked")
// @Override
public Entry<K, Object> next() {
Entry<K, ?> entry = iterator.next();
if (entry.getValue() instanceof LazyField) {
@ -203,6 +146,7 @@ class LazyField {
return (Entry<K, Object>) entry;
}
// @Override
public void remove() {
iterator.remove();
}

@ -30,6 +30,7 @@
package com.google.protobuf;
import java.util.Arrays;
import java.util.List;
import java.util.AbstractList;
import java.util.ArrayList;
@ -39,9 +40,9 @@ import java.util.RandomAccess;
/**
* An implementation of {@link LazyStringList} that wraps an ArrayList. Each
* element is either a ByteString or a String. It caches the last one requested
* which is most likely the one needed next. This minimizes memory usage while
* satisfying the most common use cases.
* element is one of String, ByteString, or byte[]. It caches the last one
* requested which is most likely the one needed next. This minimizes memory
* usage while satisfying the most common use cases.
* <p>
* <strong>Note that this implementation is not synchronized.</strong>
* If multiple threads access an <tt>ArrayList</tt> instance concurrently,
@ -64,8 +65,8 @@ import java.util.RandomAccess;
public class LazyStringArrayList extends AbstractList<String>
implements LazyStringList, RandomAccess {
public final static LazyStringList EMPTY = new UnmodifiableLazyStringList(
new LazyStringArrayList());
public static final LazyStringList EMPTY =
new LazyStringArrayList().getUnmodifiableView();
private final List<Object> list;
@ -87,13 +88,20 @@ public class LazyStringArrayList extends AbstractList<String>
Object o = list.get(index);
if (o instanceof String) {
return (String) o;
} else {
} else if (o instanceof ByteString) {
ByteString bs = (ByteString) o;
String s = bs.toStringUtf8();
if (bs.isValidUtf8()) {
list.set(index, s);
}
return s;
} else {
byte[] ba = (byte[]) o;
String s = Internal.toStringUtf8(ba);
if (Internal.isValidUtf8(ba)) {
list.set(index, s);
}
return s;
}
}
@ -134,6 +142,20 @@ public class LazyStringArrayList extends AbstractList<String>
return ret;
}
// @Override
public boolean addAllByteString(Collection<? extends ByteString> values) {
boolean ret = list.addAll(values);
modCount++;
return ret;
}
// @Override
public boolean addAllByteArray(Collection<byte[]> c) {
boolean ret = list.addAll(c);
modCount++;
return ret;
}
@Override
public String remove(int index) {
Object o = list.remove(index);
@ -141,6 +163,7 @@ public class LazyStringArrayList extends AbstractList<String>
return asString(o);
}
@Override
public void clear() {
list.clear();
modCount++;
@ -151,28 +174,194 @@ public class LazyStringArrayList extends AbstractList<String>
list.add(element);
modCount++;
}
// @Override
public void add(byte[] element) {
list.add(element);
modCount++;
}
// @Override
public ByteString getByteString(int index) {
Object o = list.get(index);
if (o instanceof String) {
ByteString b = ByteString.copyFromUtf8((String) o);
ByteString b = asByteString(o);
if (b != o) {
list.set(index, b);
}
return b;
}
// @Override
public byte[] getByteArray(int index) {
Object o = list.get(index);
byte[] b = asByteArray(o);
if (b != o) {
list.set(index, b);
return b;
} else {
return (ByteString) o;
}
return b;
}
// @Override
public void set(int index, ByteString s) {
list.set(index, s);
}
// @Override
public void set(int index, byte[] s) {
list.set(index, s);
}
private String asString(Object o) {
private static String asString(Object o) {
if (o instanceof String) {
return (String) o;
} else {
} else if (o instanceof ByteString) {
return ((ByteString) o).toStringUtf8();
} else {
return Internal.toStringUtf8((byte[]) o);
}
}
private static ByteString asByteString(Object o) {
if (o instanceof ByteString) {
return (ByteString) o;
} else if (o instanceof String) {
return ByteString.copyFromUtf8((String) o);
} else {
return ByteString.copyFrom((byte[]) o);
}
}
private static byte[] asByteArray(Object o) {
if (o instanceof byte[]) {
return (byte[]) o;
} else if (o instanceof String) {
return Internal.toByteArray((String) o);
} else {
return ((ByteString) o).toByteArray();
}
}
// @Override
public List<?> getUnderlyingElements() {
return Collections.unmodifiableList(list);
}
// @Override
public void mergeFrom(LazyStringList other) {
for (Object o : other.getUnderlyingElements()) {
if (o instanceof byte[]) {
byte[] b = (byte[]) o;
// Byte array's content is mutable so they should be copied rather than
// shared when merging from one message to another.
list.add(Arrays.copyOf(b, b.length));
} else {
list.add(o);
}
}
}
private static class ByteArrayListView extends AbstractList<byte[]>
implements RandomAccess {
private final List<Object> list;
ByteArrayListView(List<Object> list) {
this.list = list;
}
@Override
public byte[] get(int index) {
Object o = list.get(index);
byte[] b = asByteArray(o);
if (b != o) {
list.set(index, b);
}
return b;
}
@Override
public int size() {
return list.size();
}
@Override
public byte[] set(int index, byte[] s) {
Object o = list.set(index, s);
modCount++;
return asByteArray(o);
}
@Override
public void add(int index, byte[] s) {
list.add(index, s);
modCount++;
}
@Override
public byte[] remove(int index) {
Object o = list.remove(index);
modCount++;
return asByteArray(o);
}
}
// @Override
public List<byte[]> asByteArrayList() {
return new ByteArrayListView(list);
}
private static class ByteStringListView extends AbstractList<ByteString>
implements RandomAccess {
private final List<Object> list;
ByteStringListView(List<Object> list) {
this.list = list;
}
@Override
public ByteString get(int index) {
Object o = list.get(index);
ByteString b = asByteString(o);
if (b != o) {
list.set(index, b);
}
return b;
}
@Override
public int size() {
return list.size();
}
@Override
public ByteString set(int index, ByteString s) {
Object o = list.set(index, s);
modCount++;
return asByteString(o);
}
@Override
public void add(int index, ByteString s) {
list.add(index, s);
modCount++;
}
@Override
public ByteString remove(int index) {
Object o = list.remove(index);
modCount++;
return asByteString(o);
}
}
// @Override
public List<ByteString> asByteStringList() {
return new ByteStringListView(list);
}
// @Override
public LazyStringList getUnmodifiableView() {
return new UnmodifiableLazyStringList(this);
}
}

@ -30,25 +30,22 @@
package com.google.protobuf;
import java.util.Collection;
import java.util.List;
/**
* An interface extending {@code List<String>} that also provides access to the
* items of the list as UTF8-encoded ByteString objects. This is used by the
* protocol buffer implementation to support lazily converting bytes parsed
* over the wire to String objects until needed and also increases the
* items of the list as UTF8-encoded ByteString or byte[] objects. This is
* used by the protocol buffer implementation to support lazily converting bytes
* parsed over the wire to String objects until needed and also increases the
* efficiency of serialization if the String was never requested as the
* ByteString is already cached.
* <p>
* This only adds additional methods that are required for the use in the
* protocol buffer code in order to be able successfully round trip byte arrays
* through parsing and serialization without conversion to strings. It's not
* attempting to support the functionality of say {@code List<ByteString>}, hence
* why only these two very specific methods are added.
* ByteString or byte[] is already cached. The ByteString methods are used in
* immutable API only and byte[] methods used in mutable API only for they use
* different representations for string/bytes fields.
*
* @author jonp@google.com (Jon Perlow)
*/
public interface LazyStringList extends List<String> {
public interface LazyStringList extends ProtocolStringList {
/**
* Returns the element at the specified position in this list as a ByteString.
@ -59,6 +56,16 @@ public interface LazyStringList extends List<String> {
* ({@code index < 0 || index >= size()})
*/
ByteString getByteString(int index);
/**
* Returns the element at the specified position in this list as byte[].
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException if the index is out of range
* ({@code index < 0 || index >= size()})
*/
byte[] getByteArray(int index);
/**
* Appends the specified element to the end of this list (optional
@ -71,11 +78,86 @@ public interface LazyStringList extends List<String> {
void add(ByteString element);
/**
* Returns an unmodifiable List of the underlying elements, each of
* which is either a {@code String} or its equivalent UTF-8 encoded
* {@code ByteString}. It is an error for the caller to modify the returned
* Appends the specified element to the end of this list (optional
* operation).
*
* @param element element to be appended to this list
* @throws UnsupportedOperationException if the <tt>add</tt> operation
* is not supported by this list
*/
void add(byte[] element);
/**
* Replaces the element at the specified position in this list with the
* specified element (optional operation).
*
* @param index index of the element to replace
* @param element the element to be stored at the specified position
* @throws UnsupportedOperationException if the <tt>set</tt> operation
* is not supported by this list
* IndexOutOfBoundsException if the index is out of range
* ({@code index < 0 || index >= size()})
*/
void set(int index, ByteString element);
/**
* Replaces the element at the specified position in this list with the
* specified element (optional operation).
*
* @param index index of the element to replace
* @param element the element to be stored at the specified position
* @throws UnsupportedOperationException if the <tt>set</tt> operation
* is not supported by this list
* IndexOutOfBoundsException if the index is out of range
* ({@code index < 0 || index >= size()})
*/
void set(int index, byte[] element);
/**
* Appends all elements in the specified ByteString collection to the end of
* this list.
*
* @param c collection whose elements are to be added to this list
* @return true if this list changed as a result of the call
* @throws UnsupportedOperationException if the <tt>addAllByteString</tt>
* operation is not supported by this list
*/
boolean addAllByteString(Collection<? extends ByteString> c);
/**
* Appends all elements in the specified byte[] collection to the end of
* this list.
*
* @param c collection whose elements are to be added to this list
* @return true if this list changed as a result of the call
* @throws UnsupportedOperationException if the <tt>addAllByteArray</tt>
* operation is not supported by this list
*/
boolean addAllByteArray(Collection<byte[]> c);
/**
* Returns an unmodifiable List of the underlying elements, each of which is
* either a {@code String} or its equivalent UTF-8 encoded {@code ByteString}
* or byte[]. It is an error for the caller to modify the returned
* List, and attempting to do so will result in an
* {@link UnsupportedOperationException}.
*/
List<?> getUnderlyingElements();
/**
* Merges all elements from another LazyStringList into this one. This method
* differs from {@link #addAll(Collection)} on that underlying byte arrays are
* copied instead of reference shared. Immutable API doesn't need to use this
* method as byte[] is not used there at all.
*/
void mergeFrom(LazyStringList other);
/**
* Returns a mutable view of this list. Changes to the view will be made into
* the original list. This method is used in mutable API only.
*/
List<byte[]> asByteArrayList();
/** Returns an unmodifiable view of the list. */
LazyStringList getUnmodifiableView();
}

@ -142,6 +142,13 @@ class LiteralByteString extends ByteString {
outputStream.write(toByteArray());
}
@Override
void writeToInternal(OutputStream outputStream, int sourceOffset,
int numberToWrite) throws IOException {
outputStream.write(bytes, getOffsetIntoBytes() + sourceOffset,
numberToWrite);
}
@Override
public String toString(String charsetName)
throws UnsupportedEncodingException {
@ -261,13 +268,20 @@ class LiteralByteString extends ByteString {
@Override
protected int partialHash(int h, int offset, int length) {
byte[] thisBytes = bytes;
for (int i = getOffsetIntoBytes() + offset, limit = i + length; i < limit;
i++) {
h = h * 31 + thisBytes[i];
return hashCode(h, bytes, getOffsetIntoBytes() + offset, length);
}
static int hashCode(int h, byte[] bytes, int offset, int length) {
for (int i = offset; i < offset + length; i++) {
h = h * 31 + bytes[i];
}
return h;
}
static int hashCode(byte[] bytes) {
int h = hashCode(bytes.length, bytes, 0, bytes.length);
return h == 0 ? 1 : h;
}
// =================================================================
// Input stream
@ -282,8 +296,7 @@ class LiteralByteString extends ByteString {
public CodedInputStream newCodedInput() {
// We trust CodedInputStream not to modify the bytes, or to give anyone
// else access to them.
return CodedInputStream
.newInstance(bytes, getOffsetIntoBytes(), size()); // No copy
return CodedInputStream.newInstance(this);
}
// =================================================================

@ -53,6 +53,7 @@ public interface Message extends MessageLite, MessageOrBuilder {
// (From MessageLite, re-declared here only for return type covariance.)
Parser<? extends Message> getParserForType();
// -----------------------------------------------------------------
// Comparison and hashing
@ -179,6 +180,12 @@ public interface Message extends MessageLite, MessageOrBuilder {
*/
Builder clearField(Descriptors.FieldDescriptor field);
/**
* Clears the oneof. This is exactly equivalent to calling the generated
* "clear" accessor method corresponding to the oneof.
*/
Builder clearOneof(Descriptors.OneofDescriptor oneof);
/**
* Sets an element of a repeated field to the given value. The value must
* be of the correct type for this field, i.e. the same type that

@ -128,6 +128,7 @@ public interface MessageLite extends MessageLiteOrBuilder {
*/
void writeDelimitedTo(OutputStream output) throws IOException;
// =================================================================
// Builders

@ -76,7 +76,7 @@ public interface MessageOrBuilder extends MessageLiteOrBuilder {
* Returns a collection of all the fields in this message which are set
* and their corresponding values. A singular ("required" or "optional")
* field is set iff hasField() returns true for that field. A "repeated"
* field is set iff getRepeatedFieldSize() is greater than zero. The
* field is set iff getRepeatedFieldCount() is greater than zero. The
* values are exactly what would be returned by calling
* {@link #getField(Descriptors.FieldDescriptor)} for each field. The map
* is guaranteed to be a sorted map, so iterating over it will return fields
@ -88,6 +88,20 @@ public interface MessageOrBuilder extends MessageLiteOrBuilder {
*/
Map<Descriptors.FieldDescriptor, Object> getAllFields();
/**
* Returns true if the given oneof is set.
* @throws IllegalArgumentException if
* {@code oneof.getContainingType() != getDescriptorForType()}.
*/
boolean hasOneof(Descriptors.OneofDescriptor oneof);
/**
* Obtains the FieldDescriptor if the given oneof is set. Returns null
* if no field is set.
*/
Descriptors.FieldDescriptor getOneofFieldDescriptor(
Descriptors.OneofDescriptor oneof);
/**
* Returns true if the given field is set. This is exactly equivalent to
* calling the generated "has" accessor method corresponding to the field.

@ -0,0 +1,931 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// 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 java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* Reflection utility methods shared by both mutable and immutable messages.
*
* @author liujisi@google.com (Pherl Liu)
*/
class MessageReflection {
static void writeMessageTo(Message message, CodedOutputStream output,
boolean alwaysWriteRequiredFields)
throws IOException {
final boolean isMessageSet =
message.getDescriptorForType().getOptions().getMessageSetWireFormat();
Map<FieldDescriptor, Object> fields = message.getAllFields();
if (alwaysWriteRequiredFields) {
fields = new TreeMap<FieldDescriptor, Object>(fields);
for (final FieldDescriptor field :
message.getDescriptorForType().getFields()) {
if (field.isRequired() && !fields.containsKey(field)) {
fields.put(field, message.getField(field));
}
}
}
for (final Map.Entry<Descriptors.FieldDescriptor, Object> entry :
fields.entrySet()) {
final Descriptors.FieldDescriptor field = entry.getKey();
final Object value = entry.getValue();
if (isMessageSet && field.isExtension() &&
field.getType() == Descriptors.FieldDescriptor.Type.MESSAGE &&
!field.isRepeated()) {
output.writeMessageSetExtension(field.getNumber(), (Message) value);
} else {
FieldSet.writeField(field, value, output);
}
}
final UnknownFieldSet unknownFields = message.getUnknownFields();
if (isMessageSet) {
unknownFields.writeAsMessageSetTo(output);
} else {
unknownFields.writeTo(output);
}
}
static int getSerializedSize(Message message) {
int size = 0;
final boolean isMessageSet =
message.getDescriptorForType().getOptions().getMessageSetWireFormat();
for (final Map.Entry<Descriptors.FieldDescriptor, Object> entry :
message.getAllFields().entrySet()) {
final Descriptors.FieldDescriptor field = entry.getKey();
final Object value = entry.getValue();
if (isMessageSet && field.isExtension() &&
field.getType() == Descriptors.FieldDescriptor.Type.MESSAGE &&
!field.isRepeated()) {
size += CodedOutputStream.computeMessageSetExtensionSize(
field.getNumber(), (Message) value);
} else {
size += FieldSet.computeFieldSize(field, value);
}
}
final UnknownFieldSet unknownFields = message.getUnknownFields();
if (isMessageSet) {
size += unknownFields.getSerializedSizeAsMessageSet();
} else {
size += unknownFields.getSerializedSize();
}
return size;
}
static String delimitWithCommas(List<String> parts) {
StringBuilder result = new StringBuilder();
for (String part : parts) {
if (result.length() > 0) {
result.append(", ");
}
result.append(part);
}
return result.toString();
}
@SuppressWarnings("unchecked")
static boolean isInitialized(MessageOrBuilder message) {
// Check that all required fields are present.
for (final Descriptors.FieldDescriptor field : message
.getDescriptorForType()
.getFields()) {
if (field.isRequired()) {
if (!message.hasField(field)) {
return false;
}
}
}
// Check that embedded messages are initialized.
for (final Map.Entry<Descriptors.FieldDescriptor, Object> entry :
message.getAllFields().entrySet()) {
final Descriptors.FieldDescriptor field = entry.getKey();
if (field.getJavaType() == Descriptors.FieldDescriptor.JavaType.MESSAGE) {
if (field.isRepeated()) {
for (final Message element
: (List<Message>) entry.getValue()) {
if (!element.isInitialized()) {
return false;
}
}
} else {
if (!((Message) entry.getValue()).isInitialized()) {
return false;
}
}
}
}
return true;
}
private static String subMessagePrefix(final String prefix,
final Descriptors.FieldDescriptor field,
final int index) {
final StringBuilder result = new StringBuilder(prefix);
if (field.isExtension()) {
result.append('(')
.append(field.getFullName())
.append(')');
} else {
result.append(field.getName());
}
if (index != -1) {
result.append('[')
.append(index)
.append(']');
}
result.append('.');
return result.toString();
}
private static void findMissingFields(final MessageOrBuilder message,
final String prefix,
final List<String> results) {
for (final Descriptors.FieldDescriptor field :
message.getDescriptorForType().getFields()) {
if (field.isRequired() && !message.hasField(field)) {
results.add(prefix + field.getName());
}
}
for (final Map.Entry<Descriptors.FieldDescriptor, Object> entry :
message.getAllFields().entrySet()) {
final Descriptors.FieldDescriptor field = entry.getKey();
final Object value = entry.getValue();
if (field.getJavaType() == Descriptors.FieldDescriptor.JavaType.MESSAGE) {
if (field.isRepeated()) {
int i = 0;
for (final Object element : (List) value) {
findMissingFields((MessageOrBuilder) element,
subMessagePrefix(prefix, field, i++),
results);
}
} else {
if (message.hasField(field)) {
findMissingFields((MessageOrBuilder) value,
subMessagePrefix(prefix, field, -1),
results);
}
}
}
}
}
/**
* Populates {@code this.missingFields} with the full "path" of each missing
* required field in the given message.
*/
static List<String> findMissingFields(
final MessageOrBuilder message) {
final List<String> results = new ArrayList<String>();
findMissingFields(message, "", results);
return results;
}
static interface MergeTarget {
enum ContainerType {
MESSAGE, EXTENSION_SET
}
/**
* Returns the descriptor for the target.
*/
public Descriptors.Descriptor getDescriptorForType();
public ContainerType getContainerType();
public ExtensionRegistry.ExtensionInfo findExtensionByName(
ExtensionRegistry registry, String name);
public ExtensionRegistry.ExtensionInfo findExtensionByNumber(
ExtensionRegistry registry, Descriptors.Descriptor containingType,
int fieldNumber);
/**
* Obtains the value of the given field, or the default value if it is not
* set. For primitive fields, the boxed primitive value is returned. For
* enum fields, the EnumValueDescriptor for the value is returned. For
* embedded message fields, the sub-message is returned. For repeated
* fields, a java.util.List is returned.
*/
public Object getField(Descriptors.FieldDescriptor field);
/**
* Returns true if the given field is set. This is exactly equivalent to
* calling the generated "has" accessor method corresponding to the field.
*
* @throws IllegalArgumentException The field is a repeated field, or {@code
* field.getContainingType() != getDescriptorForType()}.
*/
boolean hasField(Descriptors.FieldDescriptor field);
/**
* 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.
*/
MergeTarget setField(Descriptors.FieldDescriptor field, Object value);
/**
* Clears the field. This is exactly equivalent to calling the generated
* "clear" accessor method corresponding to the field.
*/
MergeTarget clearField(Descriptors.FieldDescriptor field);
/**
* Sets an element of a repeated field to the given value. The value must
* be of the correct type for this field, i.e. the same type that {@link
* Message#getRepeatedField(Descriptors.FieldDescriptor, int)} would return.
*
* @throws IllegalArgumentException The field is not a repeated field, or
* {@code field.getContainingType() !=
* getDescriptorForType()}.
*/
MergeTarget setRepeatedField(Descriptors.FieldDescriptor field,
int index, Object value);
/**
* Like {@code setRepeatedField}, but appends the value as a new element.
*
* @throws IllegalArgumentException The field is not a repeated field, or
* {@code field.getContainingType() !=
* getDescriptorForType()}.
*/
MergeTarget addRepeatedField(Descriptors.FieldDescriptor field,
Object value);
/**
* Returns true if the given oneof is set.
*
* @throws IllegalArgumentException if
* {@code oneof.getContainingType() != getDescriptorForType()}.
*/
boolean hasOneof(Descriptors.OneofDescriptor oneof);
/**
* Clears the oneof. This is exactly equivalent to calling the generated
* "clear" accessor method corresponding to the oneof.
*/
MergeTarget clearOneof(Descriptors.OneofDescriptor oneof);
/**
* Obtains the FieldDescriptor if the given oneof is set. Returns null
* if no field is set.
*/
Descriptors.FieldDescriptor getOneofFieldDescriptor(Descriptors.OneofDescriptor oneof);
/**
* Parse the input stream into a sub field group defined based on either
* FieldDescriptor or the default instance.
*/
Object parseGroup(CodedInputStream input, ExtensionRegistryLite registry,
Descriptors.FieldDescriptor descriptor, Message defaultInstance)
throws IOException;
/**
* Parse the input stream into a sub field message defined based on either
* FieldDescriptor or the default instance.
*/
Object parseMessage(CodedInputStream input, ExtensionRegistryLite registry,
Descriptors.FieldDescriptor descriptor, Message defaultInstance)
throws IOException;
/**
* Parse from a ByteString into a sub field message defined based on either
* FieldDescriptor or the default instance. There isn't a varint indicating
* the length of the message at the beginning of the input ByteString.
*/
Object parseMessageFromBytes(
ByteString bytes, ExtensionRegistryLite registry,
Descriptors.FieldDescriptor descriptor, Message defaultInstance)
throws IOException;
/**
* Read a primitive field from input. Note that builders and mutable
* messages may use different Java types to represent a primtive field.
*/
Object readPrimitiveField(
CodedInputStream input, WireFormat.FieldType type,
boolean checkUtf8) throws IOException;
/**
* Returns a new merge target for a sub-field. When defaultInstance is
* provided, it indicates the descriptor is for an extension type, and
* implementations should create a new instance from the defaultInstance
* prototype directly.
*/
MergeTarget newMergeTargetForField(
Descriptors.FieldDescriptor descriptor,
Message defaultInstance);
/**
* Finishes the merge and returns the underlying object.
*/
Object finish();
}
static class BuilderAdapter implements MergeTarget {
private final Message.Builder builder;
public Descriptors.Descriptor getDescriptorForType() {
return builder.getDescriptorForType();
}
public BuilderAdapter(Message.Builder builder) {
this.builder = builder;
}
public Object getField(Descriptors.FieldDescriptor field) {
return builder.getField(field);
}
@Override
public boolean hasField(Descriptors.FieldDescriptor field) {
return builder.hasField(field);
}
public MergeTarget setField(Descriptors.FieldDescriptor field,
Object value) {
builder.setField(field, value);
return this;
}
public MergeTarget clearField(Descriptors.FieldDescriptor field) {
builder.clearField(field);
return this;
}
public MergeTarget setRepeatedField(
Descriptors.FieldDescriptor field, int index, Object value) {
builder.setRepeatedField(field, index, value);
return this;
}
public MergeTarget addRepeatedField(
Descriptors.FieldDescriptor field, Object value) {
builder.addRepeatedField(field, value);
return this;
}
@Override
public boolean hasOneof(Descriptors.OneofDescriptor oneof) {
return builder.hasOneof(oneof);
}
@Override
public MergeTarget clearOneof(Descriptors.OneofDescriptor oneof) {
builder.clearOneof(oneof);
return this;
}
@Override
public Descriptors.FieldDescriptor getOneofFieldDescriptor(Descriptors.OneofDescriptor oneof) {
return builder.getOneofFieldDescriptor(oneof);
}
public ContainerType getContainerType() {
return ContainerType.MESSAGE;
}
public ExtensionRegistry.ExtensionInfo findExtensionByName(
ExtensionRegistry registry, String name) {
return registry.findImmutableExtensionByName(name);
}
public ExtensionRegistry.ExtensionInfo findExtensionByNumber(
ExtensionRegistry registry, Descriptors.Descriptor containingType,
int fieldNumber) {
return registry.findImmutableExtensionByNumber(containingType,
fieldNumber);
}
public Object parseGroup(CodedInputStream input,
ExtensionRegistryLite extensionRegistry,
Descriptors.FieldDescriptor field, Message defaultInstance)
throws IOException {
Message.Builder subBuilder;
// When default instance is not null. The field is an extension field.
if (defaultInstance != null) {
subBuilder = defaultInstance.newBuilderForType();
} else {
subBuilder = builder.newBuilderForField(field);
}
if (!field.isRepeated()) {
Message originalMessage = (Message) getField(field);
if (originalMessage != null) {
subBuilder.mergeFrom(originalMessage);
}
}
input.readGroup(field.getNumber(), subBuilder, extensionRegistry);
return subBuilder.buildPartial();
}
public Object parseMessage(CodedInputStream input,
ExtensionRegistryLite extensionRegistry,
Descriptors.FieldDescriptor field, Message defaultInstance)
throws IOException {
Message.Builder subBuilder;
// When default instance is not null. The field is an extension field.
if (defaultInstance != null) {
subBuilder = defaultInstance.newBuilderForType();
} else {
subBuilder = builder.newBuilderForField(field);
}
if (!field.isRepeated()) {
Message originalMessage = (Message) getField(field);
if (originalMessage != null) {
subBuilder.mergeFrom(originalMessage);
}
}
input.readMessage(subBuilder, extensionRegistry);
return subBuilder.buildPartial();
}
public Object parseMessageFromBytes(ByteString bytes,
ExtensionRegistryLite extensionRegistry,
Descriptors.FieldDescriptor field, Message defaultInstance)
throws IOException {
Message.Builder subBuilder;
// When default instance is not null. The field is an extension field.
if (defaultInstance != null) {
subBuilder = defaultInstance.newBuilderForType();
} else {
subBuilder = builder.newBuilderForField(field);
}
if (!field.isRepeated()) {
Message originalMessage = (Message) getField(field);
if (originalMessage != null) {
subBuilder.mergeFrom(originalMessage);
}
}
subBuilder.mergeFrom(bytes, extensionRegistry);
return subBuilder.buildPartial();
}
public MergeTarget newMergeTargetForField(Descriptors.FieldDescriptor field,
Message defaultInstance) {
if (defaultInstance != null) {
return new BuilderAdapter(
defaultInstance.newBuilderForType());
} else {
return new BuilderAdapter(builder.newBuilderForField(field));
}
}
public Object readPrimitiveField(
CodedInputStream input, WireFormat.FieldType type,
boolean checkUtf8) throws IOException {
return FieldSet.readPrimitiveField(input, type, checkUtf8);
}
public Object finish() {
return builder.buildPartial();
}
}
static class ExtensionAdapter implements MergeTarget {
private final FieldSet<Descriptors.FieldDescriptor> extensions;
ExtensionAdapter(FieldSet<Descriptors.FieldDescriptor> extensions) {
this.extensions = extensions;
}
public Descriptors.Descriptor getDescriptorForType() {
throw new UnsupportedOperationException(
"getDescriptorForType() called on FieldSet object");
}
public Object getField(Descriptors.FieldDescriptor field) {
return extensions.getField(field);
}
public boolean hasField(Descriptors.FieldDescriptor field) {
return extensions.hasField(field);
}
public MergeTarget setField(Descriptors.FieldDescriptor field,
Object value) {
extensions.setField(field, value);
return this;
}
public MergeTarget clearField(Descriptors.FieldDescriptor field) {
extensions.clearField(field);
return this;
}
public MergeTarget setRepeatedField(
Descriptors.FieldDescriptor field, int index, Object value) {
extensions.setRepeatedField(field, index, value);
return this;
}
public MergeTarget addRepeatedField(
Descriptors.FieldDescriptor field, Object value) {
extensions.addRepeatedField(field, value);
return this;
}
@Override
public boolean hasOneof(Descriptors.OneofDescriptor oneof) {
return false;
}
@Override
public MergeTarget clearOneof(Descriptors.OneofDescriptor oneof) {
// Nothing to clear.
return this;
}
@Override
public Descriptors.FieldDescriptor getOneofFieldDescriptor(Descriptors.OneofDescriptor oneof) {
return null;
}
public ContainerType getContainerType() {
return ContainerType.EXTENSION_SET;
}
public ExtensionRegistry.ExtensionInfo findExtensionByName(
ExtensionRegistry registry, String name) {
return registry.findImmutableExtensionByName(name);
}
public ExtensionRegistry.ExtensionInfo findExtensionByNumber(
ExtensionRegistry registry, Descriptors.Descriptor containingType,
int fieldNumber) {
return registry.findImmutableExtensionByNumber(containingType,
fieldNumber);
}
public Object parseGroup(CodedInputStream input,
ExtensionRegistryLite registry, Descriptors.FieldDescriptor field,
Message defaultInstance) throws IOException {
Message.Builder subBuilder =
defaultInstance.newBuilderForType();
if (!field.isRepeated()) {
Message originalMessage = (Message) getField(field);
if (originalMessage != null) {
subBuilder.mergeFrom(originalMessage);
}
}
input.readGroup(field.getNumber(), subBuilder, registry);
return subBuilder.buildPartial();
}
public Object parseMessage(CodedInputStream input,
ExtensionRegistryLite registry, Descriptors.FieldDescriptor field,
Message defaultInstance) throws IOException {
Message.Builder subBuilder =
defaultInstance.newBuilderForType();
if (!field.isRepeated()) {
Message originalMessage = (Message) getField(field);
if (originalMessage != null) {
subBuilder.mergeFrom(originalMessage);
}
}
input.readMessage(subBuilder, registry);
return subBuilder.buildPartial();
}
public Object parseMessageFromBytes(ByteString bytes,
ExtensionRegistryLite registry, Descriptors.FieldDescriptor field,
Message defaultInstance) throws IOException {
Message.Builder subBuilder = defaultInstance.newBuilderForType();
if (!field.isRepeated()) {
Message originalMessage = (Message) getField(field);
if (originalMessage != null) {
subBuilder.mergeFrom(originalMessage);
}
}
subBuilder.mergeFrom(bytes, registry);
return subBuilder.buildPartial();
}
public MergeTarget newMergeTargetForField(
Descriptors.FieldDescriptor descriptor, Message defaultInstance) {
throw new UnsupportedOperationException(
"newMergeTargetForField() called on FieldSet object");
}
public Object readPrimitiveField(
CodedInputStream input, WireFormat.FieldType type,
boolean checkUtf8) throws IOException {
return FieldSet.readPrimitiveField(input, type, checkUtf8);
}
public Object finish() {
throw new UnsupportedOperationException(
"finish() called on FieldSet object");
}
}
/**
* Parses a single field into MergeTarget. The target can be Message.Builder,
* FieldSet or MutableMessage.
*
* Package-private because it is used by GeneratedMessage.ExtendableMessage.
*
* @param tag The tag, which should have already been read.
* @return {@code true} unless the tag is an end-group tag.
*/
static boolean mergeFieldFrom(
CodedInputStream input,
UnknownFieldSet.Builder unknownFields,
ExtensionRegistryLite extensionRegistry,
Descriptors.Descriptor type,
MergeTarget target,
int tag) throws IOException {
if (type.getOptions().getMessageSetWireFormat() &&
tag == WireFormat.MESSAGE_SET_ITEM_TAG) {
mergeMessageSetExtensionFromCodedStream(
input, unknownFields, extensionRegistry, type, target);
return true;
}
final int wireType = WireFormat.getTagWireType(tag);
final int fieldNumber = WireFormat.getTagFieldNumber(tag);
final Descriptors.FieldDescriptor field;
Message defaultInstance = null;
if (type.isExtensionNumber(fieldNumber)) {
// extensionRegistry may be either ExtensionRegistry or
// ExtensionRegistryLite. Since the type we are parsing is a full
// message, only a full ExtensionRegistry could possibly contain
// extensions of it. Otherwise we will treat the registry as if it
// were empty.
if (extensionRegistry instanceof ExtensionRegistry) {
final ExtensionRegistry.ExtensionInfo extension =
target.findExtensionByNumber((ExtensionRegistry) extensionRegistry,
type, fieldNumber);
if (extension == null) {
field = null;
} else {
field = extension.descriptor;
defaultInstance = extension.defaultInstance;
if (defaultInstance == null &&
field.getJavaType()
== Descriptors.FieldDescriptor.JavaType.MESSAGE) {
throw new IllegalStateException(
"Message-typed extension lacked default instance: " +
field.getFullName());
}
}
} else {
field = null;
}
} else if (target.getContainerType() == MergeTarget.ContainerType.MESSAGE) {
field = type.findFieldByNumber(fieldNumber);
} else {
field = null;
}
boolean unknown = false;
boolean packed = false;
if (field == null) {
unknown = true; // Unknown field.
} else if (wireType == FieldSet.getWireFormatForFieldType(
field.getLiteType(),
false /* isPacked */)) {
packed = false;
} else if (field.isPackable() &&
wireType == FieldSet.getWireFormatForFieldType(
field.getLiteType(),
true /* isPacked */)) {
packed = true;
} else {
unknown = true; // Unknown wire type.
}
if (unknown) { // Unknown field or wrong wire type. Skip.
return unknownFields.mergeFieldFrom(tag, input);
}
if (packed) {
final int length = input.readRawVarint32();
final int limit = input.pushLimit(length);
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;
}
target.addRepeatedField(field, value);
}
} else {
while (input.getBytesUntilLimit() > 0) {
final Object value =
target.readPrimitiveField(input, field.getLiteType(), field.needsUtf8Check());
target.addRepeatedField(field, value);
}
}
input.popLimit(limit);
} else {
final Object value;
switch (field.getType()) {
case GROUP: {
value = target
.parseGroup(input, extensionRegistry, field, defaultInstance);
break;
}
case MESSAGE: {
value = target
.parseMessage(input, extensionRegistry, field, defaultInstance);
break;
}
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;
}
break;
default:
value = target.readPrimitiveField(input, field.getLiteType(), field.needsUtf8Check());
break;
}
if (field.isRepeated()) {
target.addRepeatedField(field, value);
} else {
target.setField(field, value);
}
}
return true;
}
/**
* Called by {@code #mergeFieldFrom()} to parse a MessageSet extension into
* MergeTarget.
*/
private static void mergeMessageSetExtensionFromCodedStream(
CodedInputStream input,
UnknownFieldSet.Builder unknownFields,
ExtensionRegistryLite extensionRegistry,
Descriptors.Descriptor type,
MergeTarget target) throws IOException {
// The wire format for MessageSet is:
// message MessageSet {
// repeated group Item = 1 {
// required int32 typeId = 2;
// required bytes message = 3;
// }
// }
// "typeId" is the extension's field number. The extension can only be
// a message type, where "message" contains the encoded bytes of that
// message.
//
// In practice, we will probably never see a MessageSet item in which
// the message appears before the type ID, or where either field does not
// appear exactly once. However, in theory such cases are valid, so we
// should be prepared to accept them.
int typeId = 0;
ByteString rawBytes = null; // If we encounter "message" before "typeId"
ExtensionRegistry.ExtensionInfo extension = null;
// Read bytes from input, if we get it's type first then parse it eagerly,
// otherwise we store the raw bytes in a local variable.
while (true) {
final int tag = input.readTag();
if (tag == 0) {
break;
}
if (tag == WireFormat.MESSAGE_SET_TYPE_ID_TAG) {
typeId = input.readUInt32();
if (typeId != 0) {
// extensionRegistry may be either ExtensionRegistry or
// ExtensionRegistryLite. Since the type we are parsing is a full
// message, only a full ExtensionRegistry could possibly contain
// extensions of it. Otherwise we will treat the registry as if it
// were empty.
if (extensionRegistry instanceof ExtensionRegistry) {
extension = target.findExtensionByNumber(
(ExtensionRegistry) extensionRegistry, type, typeId);
}
}
} else if (tag == WireFormat.MESSAGE_SET_MESSAGE_TAG) {
if (typeId != 0) {
if (extension != null &&
ExtensionRegistryLite.isEagerlyParseMessageSets()) {
// We already know the type, so we can parse directly from the
// input with no copying. Hooray!
eagerlyMergeMessageSetExtension(
input, extension, extensionRegistry, target);
rawBytes = null;
continue;
}
}
// We haven't seen a type ID yet or we want parse message lazily.
rawBytes = input.readBytes();
} else { // Unknown tag. Skip it.
if (!input.skipField(tag)) {
break; // End of group
}
}
}
input.checkLastTagWas(WireFormat.MESSAGE_SET_ITEM_END_TAG);
// Process the raw bytes.
if (rawBytes != null && typeId != 0) { // Zero is not a valid type ID.
if (extension != null) { // We known the type
mergeMessageSetExtensionFromBytes(
rawBytes, extension, extensionRegistry, target);
} else { // We don't know how to parse this. Ignore it.
if (rawBytes != null) {
unknownFields.mergeField(typeId, UnknownFieldSet.Field.newBuilder()
.addLengthDelimited(rawBytes).build());
}
}
}
}
private static void mergeMessageSetExtensionFromBytes(
ByteString rawBytes,
ExtensionRegistry.ExtensionInfo extension,
ExtensionRegistryLite extensionRegistry,
MergeTarget target) throws IOException {
Descriptors.FieldDescriptor field = extension.descriptor;
boolean hasOriginalValue = target.hasField(field);
if (hasOriginalValue || ExtensionRegistryLite.isEagerlyParseMessageSets()) {
// If the field already exists, we just parse the field.
Object value = target.parseMessageFromBytes(
rawBytes, extensionRegistry,field, extension.defaultInstance);
target.setField(field, value);
} else {
// Use LazyField to load MessageSet lazily.
LazyField lazyField = new LazyField(
extension.defaultInstance, extensionRegistry, rawBytes);
target.setField(field, lazyField);
}
}
private static void eagerlyMergeMessageSetExtension(
CodedInputStream input,
ExtensionRegistry.ExtensionInfo extension,
ExtensionRegistryLite extensionRegistry,
MergeTarget target) throws IOException {
Descriptors.FieldDescriptor field = extension.descriptor;
Object value = target.parseMessage(input, extensionRegistry, field,
extension.defaultInstance);
target.setField(field, value);
}
}

@ -35,6 +35,8 @@ import java.io.InputStream;
/**
* Abstract interface for parsing Protocol Messages.
*
* The implementation should be stateless and thread-safe.
*
* @author liujisi@google.com (Pherl Liu)
*/
public interface Parser<MessageType> {

@ -0,0 +1,48 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// 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.List;
/**
* An interface extending {@code List<String>} used for repeated string fields
* to provide optional access to the data as a list of ByteStrings. The
* underlying implementation stores values as either ByteStrings or Strings
* (see {@link LazyStringArrayList}) depending on how the value was initialized
* or last read, and it is often more efficient to deal with lists of
* ByteStrings when handling protos that have been deserialized from bytes.
*/
public interface ProtocolStringList extends List<String> {
/** Returns a view of the data as a list of ByteStrings. */
List<ByteString> asByteStringList();
}

@ -401,6 +401,20 @@ class RopeByteString extends ByteString {
right.writeTo(outputStream);
}
@Override
void writeToInternal(OutputStream out, int sourceOffset,
int numberToWrite) throws IOException {
if (sourceOffset + numberToWrite <= leftLength) {
left.writeToInternal(out, sourceOffset, numberToWrite);
} else if (sourceOffset >= leftLength) {
right.writeToInternal(out, sourceOffset - leftLength, numberToWrite);
} else {
int numberToWriteInLeft = leftLength - sourceOffset;
left.writeToInternal(out, sourceOffset, numberToWriteInLeft);
right.writeToInternal(out, 0, numberToWrite - numberToWriteInLeft);
}
}
@Override
public String toString(String charsetName)
throws UnsupportedEncodingException {

@ -91,9 +91,8 @@ public final class RpcUtil {
@SuppressWarnings("unchecked")
private static <Type extends Message> Type copyAsType(
final Type typeDefaultInstance, final Message source) {
return (Type)typeDefaultInstance.newBuilderForType()
.mergeFrom(source)
.build();
return (Type) typeDefaultInstance
.newBuilderForType().mergeFrom(source).build();
}
/**

File diff suppressed because it is too large Load Diff

@ -91,6 +91,7 @@ public final class UnknownFieldSet implements MessageLite {
}
private Map<Integer, Field> fields;
@Override
public boolean equals(final Object other) {
if (this == other) {
@ -367,6 +368,22 @@ public final class UnknownFieldSet implements MessageLite {
reinitialize();
return this;
}
/** Clear fields from the set with a given field number. */
public Builder clearField(final int number) {
if (number == 0) {
throw new IllegalArgumentException("Zero is not a valid field number.");
}
if (lastField != null && lastFieldNumber == number) {
// Discard this.
lastField = null;
lastFieldNumber = 0;
}
if (fields.containsKey(number)) {
fields.remove(number);
}
return this;
}
/**
* Merge the fields from {@code other} into this set. If a field number

@ -31,10 +31,12 @@
package com.google.protobuf;
import java.util.AbstractList;
import java.util.RandomAccess;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Iterator;
import java.util.RandomAccess;
/**
* An implementation of {@link LazyStringList} that wraps another
@ -72,6 +74,36 @@ public class UnmodifiableLazyStringList extends AbstractList<String>
}
//@Override (Java 1.6 override semantics, but we must support 1.5)
public void set(int index, ByteString element) {
throw new UnsupportedOperationException();
}
//@Override (Java 1.6 override semantics, but we must support 1.5)
public boolean addAllByteString(Collection<? extends ByteString> element) {
throw new UnsupportedOperationException();
}
//@Override (Java 1.6 override semantics, but we must support 1.5)
public byte[] getByteArray(int index) {
return list.getByteArray(index);
}
//@Override (Java 1.6 override semantics, but we must support 1.5)
public void add(byte[] element) {
throw new UnsupportedOperationException();
}
//@Override (Java 1.6 override semantics, but we must support 1.5)
public void set(int index, byte[] element) {
throw new UnsupportedOperationException();
}
//@Override (Java 1.6 override semantics, but we must support 1.5)
public boolean addAllByteArray(Collection<byte[]> element) {
throw new UnsupportedOperationException();
}
@Override
public ListIterator<String> listIterator(final int index) {
return new ListIterator<String>() {
ListIterator<String> iter = list.listIterator(index);
@ -145,8 +177,29 @@ public class UnmodifiableLazyStringList extends AbstractList<String>
};
}
//@Override (Java 1.6 override semantics, but we must support 1.5)
public List<?> getUnderlyingElements() {
// The returned value is already unmodifiable.
return list.getUnderlyingElements();
}
//@Override (Java 1.6 override semantics, but we must support 1.5)
public void mergeFrom(LazyStringList other) {
throw new UnsupportedOperationException();
}
//@Override (Java 1.6 override semantics, but we must support 1.5)
public List<byte[]> asByteArrayList() {
return Collections.unmodifiableList(list.asByteArrayList());
}
//@Override (Java 1.6 override semantics, but we must support 1.5)
public List<ByteString> asByteStringList() {
return Collections.unmodifiableList(list.asByteStringList());
}
//@Override (Java 1.6 override semantics, but we must support 1.5)
public LazyStringList getUnmodifiableView() {
return this;
}
}

@ -506,4 +506,22 @@ public class AbstractMessageTest extends TestCase {
String.format("%s should have a different hash code from %s", m1, m2),
m1.hashCode() == m2.hashCode());
}
public void testCheckByteStringIsUtf8OnUtf8() {
ByteString byteString = ByteString.copyFromUtf8("some text");
AbstractMessageLite.checkByteStringIsUtf8(byteString);
// No exception thrown.
}
public void testCheckByteStringIsUtf8OnNonUtf8() {
ByteString byteString =
ByteString.copyFrom(new byte[]{(byte) 0x80}); // A lone continuation byte.
try {
AbstractMessageLite.checkByteStringIsUtf8(byteString);
fail("Expected AbstractMessageLite.checkByteStringIsUtf8 to throw IllegalArgumentException");
} catch (IllegalArgumentException exception) {
assertEquals("Byte string is not UTF-8.", exception.getMessage());
}
}
}

@ -35,6 +35,7 @@ import com.google.protobuf.ByteString.Output;
import junit.framework.TestCase;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -676,6 +677,21 @@ public class ByteStringTest extends TestCase {
assertTrue(ByteString.EMPTY.startsWith(ByteString.EMPTY));
}
public void testEndsWith() {
byte[] bytes = getTestBytes(1000, 1234L);
ByteString string = ByteString.copyFrom(bytes);
ByteString prefix = ByteString.copyFrom(bytes, 0, 500);
ByteString suffix = ByteString.copyFrom(bytes, 400, 600);
assertTrue(string.endsWith(ByteString.EMPTY));
assertTrue(string.endsWith(string));
assertTrue(string.endsWith(suffix));
assertFalse(string.endsWith(prefix));
assertFalse(suffix.endsWith(prefix));
assertFalse(prefix.endsWith(suffix));
assertFalse(ByteString.EMPTY.endsWith(suffix));
assertTrue(ByteString.EMPTY.endsWith(ByteString.EMPTY));
}
static List<ByteString> makeConcretePieces(byte[] referenceBytes) {
List<ByteString> pieces = new ArrayList<ByteString>();
// Starting length should be small enough that we'll do some concatenating by
@ -689,4 +705,55 @@ public class ByteStringTest extends TestCase {
}
return pieces;
}
private byte[] substringUsingWriteTo(
ByteString data, int offset, int length) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
data.writeTo(output, offset, length);
return output.toByteArray();
}
public void testWriteToOutputStream() throws Exception {
// Choose a size large enough so when two ByteStrings are concatenated they
// won't be merged into one byte array due to some optimizations.
final int dataSize = ByteString.CONCATENATE_BY_COPY_SIZE + 1;
byte[] data1 = new byte[dataSize];
for (int i = 0; i < data1.length; i++) {
data1[i] = (byte) 1;
}
data1[1] = (byte) 11;
// Test LiteralByteString.writeTo(OutputStream,int,int)
LiteralByteString left = new LiteralByteString(data1);
byte[] result = substringUsingWriteTo(left, 1, 1);
assertEquals(1, result.length);
assertEquals((byte) 11, result[0]);
byte[] data2 = new byte[dataSize];
for (int i = 0; i < data1.length; i++) {
data2[i] = (byte) 2;
}
LiteralByteString right = new LiteralByteString(data2);
// Concatenate two ByteStrings to create a RopeByteString.
ByteString root = left.concat(right);
// Make sure we are actually testing a RopeByteString with a simple tree
// structure.
assertEquals(1, root.getTreeDepth());
// Write parts of the left node.
result = substringUsingWriteTo(root, 0, dataSize);
assertEquals(dataSize, result.length);
assertEquals((byte) 1, result[0]);
assertEquals((byte) 1, result[dataSize - 1]);
// Write parts of the right node.
result = substringUsingWriteTo(root, dataSize, dataSize);
assertEquals(dataSize, result.length);
assertEquals((byte) 2, result[0]);
assertEquals((byte) 2, result[dataSize - 1]);
// Write a segment of bytes that runs across both nodes.
result = substringUsingWriteTo(root, dataSize / 2, dataSize);
assertEquals(dataSize, result.length);
assertEquals((byte) 1, result[0]);
assertEquals((byte) 1, result[dataSize - dataSize / 2 - 1]);
assertEquals((byte) 2, result[dataSize - dataSize / 2]);
assertEquals((byte) 2, result[dataSize - 1]);
}
}

@ -0,0 +1,141 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// 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 proto2_test_check_utf8.TestCheckUtf8.BytesWrapper;
import proto2_test_check_utf8.TestCheckUtf8.StringWrapper;
import proto2_test_check_utf8_size.TestCheckUtf8Size.BytesWrapperSize;
import proto2_test_check_utf8_size.TestCheckUtf8Size.StringWrapperSize;
import junit.framework.TestCase;
/**
* Test that protos generated with file option java_string_check_utf8 do in
* fact perform appropriate UTF-8 checks.
*
* @author jbaum@google.com (Jacob Butcher)
*/
public class CheckUtf8Test extends TestCase {
private static final String UTF8_BYTE_STRING_TEXT = "some text";
private static final ByteString UTF8_BYTE_STRING =
ByteString.copyFromUtf8(UTF8_BYTE_STRING_TEXT);
private static final ByteString NON_UTF8_BYTE_STRING =
ByteString.copyFrom(new byte[]{(byte) 0x80}); // A lone continuation byte.
public void testBuildRequiredStringWithGoodUtf8() throws Exception {
assertEquals(UTF8_BYTE_STRING_TEXT,
StringWrapper.newBuilder().setReqBytes(UTF8_BYTE_STRING).getReq());
}
public void testParseRequiredStringWithGoodUtf8() throws Exception {
ByteString serialized =
BytesWrapper.newBuilder().setReq(UTF8_BYTE_STRING).build().toByteString();
assertEquals(UTF8_BYTE_STRING_TEXT,
StringWrapper.PARSER.parseFrom(serialized).getReq());
}
public void testBuildRequiredStringWithBadUtf8() throws Exception {
try {
StringWrapper.newBuilder().setReqBytes(NON_UTF8_BYTE_STRING);
fail("Expected IllegalArgumentException for non UTF-8 byte string.");
} catch (IllegalArgumentException exception) {
assertEquals("Byte string is not UTF-8.", exception.getMessage());
}
}
public void testBuildOptionalStringWithBadUtf8() throws Exception {
try {
StringWrapper.newBuilder().setOptBytes(NON_UTF8_BYTE_STRING);
fail("Expected IllegalArgumentException for non UTF-8 byte string.");
} catch (IllegalArgumentException exception) {
assertEquals("Byte string is not UTF-8.", exception.getMessage());
}
}
public void testBuildRepeatedStringWithBadUtf8() throws Exception {
try {
StringWrapper.newBuilder().addRepBytes(NON_UTF8_BYTE_STRING);
fail("Expected IllegalArgumentException for non UTF-8 byte string.");
} catch (IllegalArgumentException exception) {
assertEquals("Byte string is not UTF-8.", exception.getMessage());
}
}
public void testParseRequiredStringWithBadUtf8() throws Exception {
ByteString serialized =
BytesWrapper.newBuilder().setReq(NON_UTF8_BYTE_STRING).build().toByteString();
try {
StringWrapper.PARSER.parseFrom(serialized);
fail("Expected InvalidProtocolBufferException for non UTF-8 byte string.");
} catch (InvalidProtocolBufferException exception) {
assertEquals("Protocol message had invalid UTF-8.", exception.getMessage());
}
}
public void testBuildRequiredStringWithBadUtf8Size() throws Exception {
try {
StringWrapperSize.newBuilder().setReqBytes(NON_UTF8_BYTE_STRING);
fail("Expected IllegalArgumentException for non UTF-8 byte string.");
} catch (IllegalArgumentException exception) {
assertEquals("Byte string is not UTF-8.", exception.getMessage());
}
}
public void testBuildOptionalStringWithBadUtf8Size() throws Exception {
try {
StringWrapperSize.newBuilder().setOptBytes(NON_UTF8_BYTE_STRING);
fail("Expected IllegalArgumentException for non UTF-8 byte string.");
} catch (IllegalArgumentException exception) {
assertEquals("Byte string is not UTF-8.", exception.getMessage());
}
}
public void testBuildRepeatedStringWithBadUtf8Size() throws Exception {
try {
StringWrapperSize.newBuilder().addRepBytes(NON_UTF8_BYTE_STRING);
fail("Expected IllegalArgumentException for non UTF-8 byte string.");
} catch (IllegalArgumentException exception) {
assertEquals("Byte string is not UTF-8.", exception.getMessage());
}
}
public void testParseRequiredStringWithBadUtf8Size() throws Exception {
ByteString serialized =
BytesWrapperSize.newBuilder().setReq(NON_UTF8_BYTE_STRING).build().toByteString();
try {
StringWrapperSize.PARSER.parseFrom(serialized);
fail("Expected InvalidProtocolBufferException for non UTF-8 byte string.");
} catch (InvalidProtocolBufferException exception) {
assertEquals("Protocol message had invalid UTF-8.", exception.getMessage());
}
}
}

@ -30,15 +30,20 @@
package com.google.protobuf;
import protobuf_unittest.UnittestProto.BoolMessage;
import protobuf_unittest.UnittestProto.Int32Message;
import protobuf_unittest.UnittestProto.Int64Message;
import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestRecursiveMessage;
import junit.framework.TestCase;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FilterInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
/**
* Unit test for {@link CodedInputStream}.
@ -85,28 +90,54 @@ public class CodedInputStreamTest extends TestCase {
}
}
private void assertDataConsumed(byte[] data, CodedInputStream input)
throws IOException {
assertEquals(data.length, input.getTotalBytesRead());
assertTrue(input.isAtEnd());
}
/**
* Parses the given bytes using readRawVarint32() and readRawVarint64() and
* checks that the result matches the given value.
*/
private void assertReadVarint(byte[] data, long value) throws Exception {
CodedInputStream input = CodedInputStream.newInstance(data);
assertEquals((int)value, input.readRawVarint32());
assertEquals((int) value, input.readRawVarint32());
assertDataConsumed(data, input);
input = CodedInputStream.newInstance(data);
assertEquals(value, input.readRawVarint64());
assertTrue(input.isAtEnd());
assertDataConsumed(data, input);
input = CodedInputStream.newInstance(data);
assertEquals(value, input.readRawVarint64SlowPath());
assertDataConsumed(data, input);
input = CodedInputStream.newInstance(data);
assertTrue(input.skipField(WireFormat.WIRETYPE_VARINT));
assertDataConsumed(data, input);
// Try different block sizes.
for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
input = CodedInputStream.newInstance(
new SmallBlockInputStream(data, blockSize));
assertEquals((int)value, input.readRawVarint32());
assertEquals((int) value, input.readRawVarint32());
assertDataConsumed(data, input);
input = CodedInputStream.newInstance(
new SmallBlockInputStream(data, blockSize));
assertEquals(value, input.readRawVarint64());
assertTrue(input.isAtEnd());
assertDataConsumed(data, input);
input = CodedInputStream.newInstance(
new SmallBlockInputStream(data, blockSize));
assertEquals(value, input.readRawVarint64SlowPath());
assertDataConsumed(data, input);
input = CodedInputStream.newInstance(
new SmallBlockInputStream(data, blockSize));
assertTrue(input.skipField(WireFormat.WIRETYPE_VARINT));
assertDataConsumed(data, input);
}
// Try reading direct from an InputStream. We want to verify that it
@ -115,7 +146,7 @@ public class CodedInputStreamTest extends TestCase {
byte[] longerData = new byte[data.length + 1];
System.arraycopy(data, 0, longerData, 0, data.length);
InputStream rawInput = new ByteArrayInputStream(longerData);
assertEquals((int)value, CodedInputStream.readRawVarint32(rawInput));
assertEquals((int) value, CodedInputStream.readRawVarint32(rawInput));
assertEquals(1, rawInput.available());
}
@ -143,6 +174,14 @@ public class CodedInputStreamTest extends TestCase {
assertEquals(expected.getMessage(), e.getMessage());
}
input = CodedInputStream.newInstance(data);
try {
input.readRawVarint64SlowPath();
fail("Should have thrown an exception.");
} catch (InvalidProtocolBufferException e) {
assertEquals(expected.getMessage(), e.getMessage());
}
// Make sure we get the same error when reading direct from an InputStream.
try {
CodedInputStream.readRawVarint32(new ByteArrayInputStream(data));
@ -311,6 +350,7 @@ public class CodedInputStreamTest extends TestCase {
}
}
/**
* Test that a bug in skipRawBytes() has been fixed: if the skip skips
* exactly up to a limit, this should not break things.
@ -350,7 +390,7 @@ public class CodedInputStreamTest extends TestCase {
// Allocate and initialize a 1MB blob.
byte[] blob = new byte[1 << 20];
for (int i = 0; i < blob.length; i++) {
blob[i] = (byte)i;
blob[i] = (byte) i;
}
// Make a message containing it.
@ -437,16 +477,23 @@ public class CodedInputStreamTest extends TestCase {
}
}
private void checkSizeLimitExceeded(InvalidProtocolBufferException e) {
assertEquals(
InvalidProtocolBufferException.sizeLimitExceeded().getMessage(),
e.getMessage());
}
public void testSizeLimit() throws Exception {
CodedInputStream input = CodedInputStream.newInstance(
TestUtil.getAllSet().toByteString().newInput());
new SmallBlockInputStream(
TestUtil.getAllSet().toByteString().newInput(), 16));
input.setSizeLimit(16);
try {
TestAllTypes.parseFrom(input);
fail("Should have thrown an exception!");
} catch (InvalidProtocolBufferException e) {
// success.
} catch (InvalidProtocolBufferException expected) {
checkSizeLimitExceeded(expected);
}
}
@ -460,8 +507,8 @@ public class CodedInputStreamTest extends TestCase {
try {
input.readRawByte();
fail("Should have thrown an exception!");
} catch (InvalidProtocolBufferException e) {
// success.
} catch (InvalidProtocolBufferException expected) {
checkSizeLimitExceeded(expected);
}
input.resetSizeCounter();
@ -469,28 +516,50 @@ public class CodedInputStreamTest extends TestCase {
input.readRawByte(); // No exception thrown.
input.resetSizeCounter();
assertEquals(0, input.getTotalBytesRead());
input.readRawBytes(16);
assertEquals(16, input.getTotalBytesRead());
input.resetSizeCounter();
try {
input.readRawBytes(16); // Hits limit again.
input.readRawBytes(17); // Hits limit again.
fail("Should have thrown an exception!");
} catch (InvalidProtocolBufferException e) {
// success.
} catch (InvalidProtocolBufferException expected) {
checkSizeLimitExceeded(expected);
}
}
public void testSizeLimitMultipleMessages() throws Exception {
byte[] bytes = new byte[256];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) i;
}
CodedInputStream input = CodedInputStream.newInstance(
new SmallBlockInputStream(bytes, 7));
input.setSizeLimit(16);
for (int i = 0; i < 256 / 16; i++) {
byte[] message = input.readRawBytes(16);
for (int j = 0; j < message.length; j++) {
assertEquals(i * 16 + j, message[j] & 0xff);
}
assertEquals(16, input.getTotalBytesRead());
input.resetSizeCounter();
assertEquals(0, input.getTotalBytesRead());
}
}
/**
* Tests that if we read an string that contains invalid UTF-8, no exception
* Tests that if we readString invalid UTF-8 bytes, no exception
* is thrown. Instead, the invalid bytes are replaced with the Unicode
* "replacement character" U+FFFD.
*/
public void testReadInvalidUtf8() throws Exception {
public void testReadStringInvalidUtf8() throws Exception {
ByteString.Output rawOutput = ByteString.newOutput();
CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
output.writeRawVarint32(tag);
output.writeRawVarint32(1);
output.writeRawBytes(new byte[] { (byte)0x80 });
output.writeRawBytes(new byte[] { (byte) 0x80 });
output.flush();
CodedInputStream input = rawOutput.toByteString().newCodedInput();
@ -499,13 +568,37 @@ public class CodedInputStreamTest extends TestCase {
assertEquals(0xfffd, text.charAt(0));
}
/**
* Tests that if we readStringRequireUtf8 invalid UTF-8 bytes, an
* InvalidProtocolBufferException is thrown.
*/
public void testReadStringRequireUtf8InvalidUtf8() throws Exception {
ByteString.Output rawOutput = ByteString.newOutput();
CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
output.writeRawVarint32(tag);
output.writeRawVarint32(1);
output.writeRawBytes(new byte[] { (byte) 0x80 });
output.flush();
CodedInputStream input = rawOutput.toByteString().newCodedInput();
assertEquals(tag, input.readTag());
try {
input.readStringRequireUtf8();
fail("Expected invalid UTF-8 exception.");
} catch (InvalidProtocolBufferException exception) {
assertEquals("Protocol message had invalid UTF-8.", exception.getMessage());
}
}
public void testReadFromSlice() throws Exception {
byte[] bytes = bytes(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
CodedInputStream in = CodedInputStream.newInstance(bytes, 3, 5);
assertEquals(0, in.getTotalBytesRead());
for (int i = 3; i < 8; i++) {
assertEquals(i, in.readRawByte());
assertEquals(i-2, in.getTotalBytesRead());
assertEquals(i - 2, in.getTotalBytesRead());
}
// eof
assertEquals(0, in.readTag());
@ -525,4 +618,152 @@ public class CodedInputStreamTest extends TestCase {
}
}
}
public void testReadByteArray() throws Exception {
ByteString.Output rawOutput = ByteString.newOutput();
CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
// Zero-sized bytes field.
output.writeRawVarint32(0);
// One one-byte bytes field
output.writeRawVarint32(1);
output.writeRawBytes(new byte[] { (byte) 23 });
// Another one-byte bytes field
output.writeRawVarint32(1);
output.writeRawBytes(new byte[] { (byte) 45 });
// A bytes field large enough that won't fit into the 4K buffer.
final int bytesLength = 16 * 1024;
byte[] bytes = new byte[bytesLength];
bytes[0] = (byte) 67;
bytes[bytesLength - 1] = (byte) 89;
output.writeRawVarint32(bytesLength);
output.writeRawBytes(bytes);
output.flush();
CodedInputStream inputStream = rawOutput.toByteString().newCodedInput();
byte[] result = inputStream.readByteArray();
assertEquals(0, result.length);
result = inputStream.readByteArray();
assertEquals(1, result.length);
assertEquals((byte) 23, result[0]);
result = inputStream.readByteArray();
assertEquals(1, result.length);
assertEquals((byte) 45, result[0]);
result = inputStream.readByteArray();
assertEquals(bytesLength, result.length);
assertEquals((byte) 67, result[0]);
assertEquals((byte) 89, result[bytesLength - 1]);
}
public void testReadByteBuffer() throws Exception {
ByteString.Output rawOutput = ByteString.newOutput();
CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
// Zero-sized bytes field.
output.writeRawVarint32(0);
// One one-byte bytes field
output.writeRawVarint32(1);
output.writeRawBytes(new byte[]{(byte) 23});
// Another one-byte bytes field
output.writeRawVarint32(1);
output.writeRawBytes(new byte[]{(byte) 45});
// A bytes field large enough that won't fit into the 4K buffer.
final int bytesLength = 16 * 1024;
byte[] bytes = new byte[bytesLength];
bytes[0] = (byte) 67;
bytes[bytesLength - 1] = (byte) 89;
output.writeRawVarint32(bytesLength);
output.writeRawBytes(bytes);
output.flush();
CodedInputStream inputStream = rawOutput.toByteString().newCodedInput();
ByteBuffer result = inputStream.readByteBuffer();
assertEquals(0, result.capacity());
result = inputStream.readByteBuffer();
assertEquals(1, result.capacity());
assertEquals((byte) 23, result.get());
result = inputStream.readByteBuffer();
assertEquals(1, result.capacity());
assertEquals((byte) 45, result.get());
result = inputStream.readByteBuffer();
assertEquals(bytesLength, result.capacity());
assertEquals((byte) 67, result.get());
result.position(bytesLength - 1);
assertEquals((byte) 89, result.get());
}
public void testReadByteBufferAliasing() throws Exception {
ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
CodedOutputStream output = CodedOutputStream.newInstance(byteArrayStream);
// Zero-sized bytes field.
output.writeRawVarint32(0);
// One one-byte bytes field
output.writeRawVarint32(1);
output.writeRawBytes(new byte[]{(byte) 23});
// Another one-byte bytes field
output.writeRawVarint32(1);
output.writeRawBytes(new byte[]{(byte) 45});
// A bytes field large enough that won't fit into the 4K buffer.
final int bytesLength = 16 * 1024;
byte[] bytes = new byte[bytesLength];
bytes[0] = (byte) 67;
bytes[bytesLength - 1] = (byte) 89;
output.writeRawVarint32(bytesLength);
output.writeRawBytes(bytes);
output.flush();
byte[] data = byteArrayStream.toByteArray();
// Without aliasing
CodedInputStream inputStream = CodedInputStream.newInstance(data);
ByteBuffer result = inputStream.readByteBuffer();
assertEquals(0, result.capacity());
result = inputStream.readByteBuffer();
assertTrue(result.array() != data);
assertEquals(1, result.capacity());
assertEquals((byte) 23, result.get());
result = inputStream.readByteBuffer();
assertTrue(result.array() != data);
assertEquals(1, result.capacity());
assertEquals((byte) 45, result.get());
result = inputStream.readByteBuffer();
assertTrue(result.array() != data);
assertEquals(bytesLength, result.capacity());
assertEquals((byte) 67, result.get());
result.position(bytesLength - 1);
assertEquals((byte) 89, result.get());
// Enable aliasing
inputStream = CodedInputStream.newInstance(data);
inputStream.enableAliasing(true);
result = inputStream.readByteBuffer();
assertEquals(0, result.capacity());
result = inputStream.readByteBuffer();
assertTrue(result.array() == data);
assertEquals(1, result.capacity());
assertEquals((byte) 23, result.get());
result = inputStream.readByteBuffer();
assertTrue(result.array() == data);
assertEquals(1, result.capacity());
assertEquals((byte) 45, result.get());
result = inputStream.readByteBuffer();
assertTrue(result.array() == data);
assertEquals(bytesLength, result.capacity());
assertEquals((byte) 67, result.get());
result.position(bytesLength - 1);
assertEquals((byte) 89, result.get());
}
public void testCompatibleTypes() throws Exception {
long data = 0x100000000L;
Int64Message message = Int64Message.newBuilder().setData(data).build();
ByteString serialized = message.toByteString();
// Test int64(long) is compatible with bool(boolean)
BoolMessage msg2 = BoolMessage.parseFrom(serialized);
assertTrue(msg2.getData());
// Test int64(long) is compatible with int32(int)
Int32Message msg3 = Int32Message.parseFrom(serialized);
assertEquals((int) data, msg3.getData());
}
}

@ -38,6 +38,7 @@ import protobuf_unittest.UnittestProto.TestSparseEnum;
import junit.framework.TestCase;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
@ -314,4 +315,87 @@ public class CodedOutputStreamTest extends TestCase {
SparseEnumMessage message2 = SparseEnumMessage.parseFrom(rawBytes);
assertEquals(TestSparseEnum.SPARSE_E, message2.getSparseEnum());
}
/** Test getTotalBytesWritten() */
public void testGetTotalBytesWritten() throws Exception {
final int BUFFER_SIZE = 4 * 1024;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(BUFFER_SIZE);
CodedOutputStream codedStream = CodedOutputStream.newInstance(outputStream);
byte[] value = "abcde".getBytes("UTF-8");
for (int i = 0; i < 1024; ++i) {
codedStream.writeRawBytes(value, 0, value.length);
}
// Make sure we have written more bytes than the buffer could hold. This is
// to make the test complete.
assertTrue(codedStream.getTotalBytesWritten() > BUFFER_SIZE);
assertEquals(value.length * 1024, codedStream.getTotalBytesWritten());
}
public void testWriteToByteBuffer() throws Exception {
final int bufferSize = 16 * 1024;
ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
CodedOutputStream codedStream = CodedOutputStream.newInstance(buffer);
// Write raw bytes into the ByteBuffer.
final int length1 = 5000;
for (int i = 0; i < length1; i++) {
codedStream.writeRawByte((byte) 1);
}
final int length2 = 8 * 1024;
byte[] data = new byte[length2];
for (int i = 0; i < length2; i++) {
data[i] = (byte) 2;
}
codedStream.writeRawBytes(data);
final int length3 = bufferSize - length1 - length2;
for (int i = 0; i < length3; i++) {
codedStream.writeRawByte((byte) 3);
}
codedStream.flush();
// Check that data is correctly written to the ByteBuffer.
assertEquals(0, buffer.remaining());
buffer.flip();
for (int i = 0; i < length1; i++) {
assertEquals((byte) 1, buffer.get());
}
for (int i = 0; i < length2; i++) {
assertEquals((byte) 2, buffer.get());
}
for (int i = 0; i < length3; i++) {
assertEquals((byte) 3, buffer.get());
}
}
public void testWriteByteBuffer() throws Exception {
byte[] value = "abcde".getBytes("UTF-8");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CodedOutputStream codedStream = CodedOutputStream.newInstance(outputStream);
ByteBuffer byteBuffer = ByteBuffer.wrap(value, 0, 1);
// This will actually write 5 bytes into the CodedOutputStream as the
// ByteBuffer's capacity() is 5.
codedStream.writeRawBytes(byteBuffer);
// The above call shouldn't affect the ByteBuffer's state.
assertEquals(0, byteBuffer.position());
assertEquals(1, byteBuffer.limit());
// The correct way to write part of an array using ByteBuffer.
codedStream.writeRawBytes(ByteBuffer.wrap(value, 2, 1).slice());
codedStream.flush();
byte[] result = outputStream.toByteArray();
assertEquals(6, result.length);
for (int i = 0; i < 5; i++) {
assertEquals(value[i], result[i]);
}
assertEquals(value[2], result[5]);
}
public void testWriteByteArrayWithOffsets() throws Exception {
byte[] fullArray = bytes(0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88);
byte[] destination = new byte[4];
CodedOutputStream codedStream = CodedOutputStream.newInstance(destination);
codedStream.writeByteArrayNoTag(fullArray, 2, 2);
assertEqualBytes(bytes(0x02, 0x33, 0x44, 0x00), destination);
assertEquals(3, codedStream.getTotalBytesWritten());
}
}

@ -39,6 +39,7 @@ import com.google.protobuf.Descriptors.DescriptorValidationException;
import com.google.protobuf.Descriptors.FileDescriptor;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.OneofDescriptor;
import com.google.protobuf.Descriptors.EnumDescriptor;
import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.ServiceDescriptor;
@ -53,10 +54,13 @@ import protobuf_unittest.UnittestProto.ForeignMessage;
import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestAllExtensions;
import protobuf_unittest.UnittestProto.TestExtremeDefaultValues;
import protobuf_unittest.UnittestProto.TestMultipleExtensionRanges;
import protobuf_unittest.UnittestProto.TestRequired;
import protobuf_unittest.UnittestProto.TestService;
import protobuf_unittest.UnittestCustomOptions;
import protobuf_unittest.TestCustomOptions;
import junit.framework.TestCase;
@ -308,6 +312,7 @@ public class DescriptorsTest extends TestCase {
EnumValueDescriptor value = ForeignEnum.FOREIGN_FOO.getValueDescriptor();
assertEquals(value, enumType.getValues().get(0));
assertEquals("FOREIGN_FOO", value.getName());
assertEquals("FOREIGN_FOO", value.toString());
assertEquals(4, value.getNumber());
assertEquals(value, enumType.findValueByName("FOREIGN_FOO"));
assertEquals(value, enumType.findValueByNumber(4));
@ -324,7 +329,6 @@ public class DescriptorsTest extends TestCase {
assertEquals("protobuf_unittest.TestService", service.getFullName());
assertEquals(UnittestProto.getDescriptor(), service.getFile());
assertEquals(2, service.getMethods().size());
MethodDescriptor fooMethod = service.getMethods().get(0);
assertEquals("Foo", fooMethod.getName());
@ -351,8 +355,12 @@ public class DescriptorsTest extends TestCase {
public void testCustomOptions() throws Exception {
// Get the descriptor indirectly from a dependent proto class. This is to
// ensure that when a proto class is loaded, custom options defined in its
// dependencies are also properly initialized.
Descriptor descriptor =
UnittestCustomOptions.TestMessageWithCustomOptions.getDescriptor();
TestCustomOptions.TestMessageWithCustomOptionsContainer.getDescriptor()
.findFieldByName("field").getMessageType();
assertTrue(
descriptor.getOptions().hasExtension(UnittestCustomOptions.messageOpt1));
@ -511,9 +519,35 @@ public class DescriptorsTest extends TestCase {
assertTrue(barFound);
}
public void testDependencyOrder() throws Exception {
FileDescriptorProto fooProto = FileDescriptorProto.newBuilder()
.setName("foo.proto").build();
FileDescriptorProto barProto = FileDescriptorProto.newBuilder()
.setName("bar.proto")
.addDependency("foo.proto")
.build();
FileDescriptorProto bazProto = FileDescriptorProto.newBuilder()
.setName("baz.proto")
.addDependency("foo.proto")
.addDependency("bar.proto")
.addPublicDependency(0)
.addPublicDependency(1)
.build();
FileDescriptor fooFile = Descriptors.FileDescriptor.buildFrom(fooProto,
new FileDescriptor[0]);
FileDescriptor barFile = Descriptors.FileDescriptor.buildFrom(barProto,
new FileDescriptor[] {fooFile});
// Items in the FileDescriptor array can be in any order.
Descriptors.FileDescriptor.buildFrom(bazProto,
new FileDescriptor[] {fooFile, barFile});
Descriptors.FileDescriptor.buildFrom(bazProto,
new FileDescriptor[] {barFile, fooFile});
}
public void testInvalidPublicDependency() throws Exception {
FileDescriptorProto fooProto = FileDescriptorProto.newBuilder()
.setName("foo.proto") .build();
.setName("foo.proto").build();
FileDescriptorProto barProto = FileDescriptorProto.newBuilder()
.setName("boo.proto")
.addDependency("foo.proto")
@ -645,4 +679,30 @@ public class DescriptorsTest extends TestCase {
"a.b.c.d.bar.shared"));
}
}
public void testOneofDescriptor() throws Exception {
Descriptor messageType = TestAllTypes.getDescriptor();
FieldDescriptor field =
messageType.findFieldByName("oneof_nested_message");
OneofDescriptor oneofDescriptor = field.getContainingOneof();
assertNotNull(oneofDescriptor);
assertSame(oneofDescriptor, messageType.getOneofs().get(0));
assertEquals("oneof_field", oneofDescriptor.getName());
assertEquals(4, oneofDescriptor.getFieldCount());
assertSame(oneofDescriptor.getField(1), field);
}
public void testMessageDescriptorExtensions() throws Exception {
assertFalse(TestAllTypes.getDescriptor().isExtendable());
assertTrue(TestAllExtensions.getDescriptor().isExtendable());
assertTrue(TestMultipleExtensionRanges.getDescriptor().isExtendable());
assertFalse(TestAllTypes.getDescriptor().isExtensionNumber(3));
assertTrue(TestAllExtensions.getDescriptor().isExtensionNumber(3));
assertTrue(TestMultipleExtensionRanges.getDescriptor().isExtensionNumber(42));
assertFalse(TestMultipleExtensionRanges.getDescriptor().isExtensionNumber(43));
assertFalse(TestMultipleExtensionRanges.getDescriptor().isExtensionNumber(4142));
assertTrue(TestMultipleExtensionRanges.getDescriptor().isExtensionNumber(4143));
}
}

@ -30,6 +30,9 @@
package com.google.protobuf;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.OneofDescriptor;
import protobuf_unittest.UnittestProto.TestAllExtensions;
import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestEmptyMessage;
@ -241,6 +244,19 @@ public class DynamicMessageTest extends TestCase {
DynamicMessage copy = DynamicMessage.newBuilder(message).build();
reflectionTester.assertAllFieldsSetViaReflection(copy);
// Test oneof behavior
FieldDescriptor bytesField =
TestAllTypes.getDescriptor().findFieldByName("oneof_bytes");
FieldDescriptor uint32Field =
TestAllTypes.getDescriptor().findFieldByName("oneof_uint32");
assertTrue(copy.hasField(bytesField));
assertFalse(copy.hasField(uint32Field));
DynamicMessage copy2 =
DynamicMessage.newBuilder(message).setField(uint32Field, 123).build();
assertFalse(copy2.hasField(bytesField));
assertTrue(copy2.hasField(uint32Field));
assertEquals(123, copy2.getField(uint32Field));
}
public void testToBuilder() throws Exception {
@ -261,4 +277,34 @@ public class DynamicMessageTest extends TestCase {
assertEquals(Arrays.asList(unknownFieldVal),
derived.getUnknownFields().getField(unknownFieldNum).getVarintList());
}
public void testDynamicOneofMessage() throws Exception {
DynamicMessage.Builder builder =
DynamicMessage.newBuilder(TestAllTypes.getDescriptor());
OneofDescriptor oneof = TestAllTypes.getDescriptor().getOneofs().get(0);
assertFalse(builder.hasOneof(oneof));
assertSame(null, builder.getOneofFieldDescriptor(oneof));
reflectionTester.setAllFieldsViaReflection(builder);
assertTrue(builder.hasOneof(oneof));
FieldDescriptor field = oneof.getField(3);
assertSame(field, builder.getOneofFieldDescriptor(oneof));
DynamicMessage message = builder.buildPartial();
assertTrue(message.hasOneof(oneof));
DynamicMessage.Builder mergedBuilder =
DynamicMessage.newBuilder(TestAllTypes.getDescriptor());
FieldDescriptor mergedField = oneof.getField(0);
mergedBuilder.setField(mergedField, 123);
assertTrue(mergedBuilder.hasField(mergedField));
mergedBuilder.mergeFrom(message);
assertTrue(mergedBuilder.hasField(field));
assertFalse(mergedBuilder.hasField(mergedField));
builder.clearOneof(oneof);
assertSame(null, builder.getOneofFieldDescriptor(oneof));
message = builder.build();
assertSame(null, message.getOneofFieldDescriptor(oneof));
}
}

@ -45,6 +45,9 @@ import protobuf_unittest.NonNestedExtension.MyNonNestedExtension;
import protobuf_unittest.NonNestedExtensionLite;
import protobuf_unittest.NonNestedExtensionLite.MessageLiteToBeExtended;
import protobuf_unittest.NonNestedExtensionLite.MyNonNestedExtensionLite;
import protobuf_unittest.OuterClassNameTest2OuterClass;
import protobuf_unittest.OuterClassNameTest3OuterClass;
import protobuf_unittest.OuterClassNameTestOuterClass;
import protobuf_unittest.ServiceWithNoOuter;
import protobuf_unittest.UnittestOptimizeFor.TestOptimizedForSize;
import protobuf_unittest.UnittestOptimizeFor.TestOptionalOptimizedForSize;
@ -58,6 +61,7 @@ import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage;
import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder;
import protobuf_unittest.UnittestProto.TestExtremeDefaultValues;
import protobuf_unittest.UnittestProto.TestOneof2;
import protobuf_unittest.UnittestProto.TestPackedTypes;
import protobuf_unittest.UnittestProto.TestUnpackedTypes;
@ -69,6 +73,7 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
@ -399,6 +404,44 @@ public class GeneratedMessageTest extends TestCase {
// We expect this exception.
}
}
public void testRepeatedAppendIterateOnlyOnce() throws Exception {
// Create a Iterable that can only be iterated once.
Iterable<String> stringIterable = new Iterable<String>() {
private boolean called = false;
@Override
public Iterator<String> iterator() {
if (called) {
throw new IllegalStateException();
}
called = true;
return Arrays.asList("one", "two", "three").iterator();
}
};
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
builder.addAllRepeatedString(stringIterable);
assertEquals(3, builder.getRepeatedStringCount());
assertEquals("one", builder.getRepeatedString(0));
assertEquals("two", builder.getRepeatedString(1));
assertEquals("three", builder.getRepeatedString(2));
try {
builder.addAllRepeatedString(stringIterable);
fail("Exception was not thrown");
} catch (IllegalStateException e) {
// We expect this exception.
}
}
public void testMergeFromOtherRejectsNull() throws Exception {
try {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
builder.mergeFrom((TestAllTypes) null);
fail("Exception was not thrown");
} catch (NullPointerException e) {
// We expect this exception.
}
}
public void testSettingForeignMessageUsingBuilder() throws Exception {
TestAllTypes message = TestAllTypes.newBuilder()
@ -496,6 +539,34 @@ public class GeneratedMessageTest extends TestCase {
TestAllTypes.newBuilder().build());
}
public void testReflectionGetOneof() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
reflectionTester.setAllFieldsViaReflection(builder);
Descriptors.OneofDescriptor oneof =
TestAllTypes.getDescriptor().getOneofs().get(0);
Descriptors.FieldDescriptor field =
TestAllTypes.getDescriptor().findFieldByName("oneof_bytes");
assertSame(field, builder.getOneofFieldDescriptor(oneof));
TestAllTypes message = builder.build();
assertSame(field, message.getOneofFieldDescriptor(oneof));
}
public void testReflectionClearOneof() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
reflectionTester.setAllFieldsViaReflection(builder);
Descriptors.OneofDescriptor oneof =
TestAllTypes.getDescriptor().getOneofs().get(0);
Descriptors.FieldDescriptor field =
TestAllTypes.getDescriptor().findFieldByName("oneof_bytes");
assertTrue(builder.hasOneof(oneof));
assertTrue(builder.hasField(field));
builder.clearOneof(oneof);
assertFalse(builder.hasOneof(oneof));
assertFalse(builder.hasField(field));
}
public void testEnumInterface() throws Exception {
assertTrue(TestAllTypes.getDefaultInstance().getDefaultNestedEnum()
instanceof ProtocolMessageEnum);
@ -697,6 +768,15 @@ public class GeneratedMessageTest extends TestCase {
// =================================================================
// multiple_files_test
// Test that custom options of an file level enum are properly initialized.
// This test needs to be put before any other access to MultipleFilesTestProto
// or messages defined in multiple_files_test.proto because the class loading
// order affects initialization process of custom options.
public void testEnumValueOptionsInMultipleFilesMode() throws Exception {
assertEquals(12345, EnumWithNoOuter.FOO.getValueDescriptor().getOptions()
.getExtension(MultipleFilesTestProto.enumValueOption).intValue());
}
public void testMultipleFilesOption() throws Exception {
// We mostly just want to check that things compile.
MessageWithNoOuter message =
@ -795,7 +875,7 @@ public class GeneratedMessageTest extends TestCase {
UnittestProto.TestRecursiveMessage message =
UnittestProto.TestRecursiveMessage.getDefaultInstance();
assertTrue(message != null);
assertTrue(message.getA() != null);
assertNotNull(message.getA());
assertTrue(message.getA() == message);
}
@ -1143,4 +1223,293 @@ public class GeneratedMessageTest extends TestCase {
// We expect this exception.
}
}
// Test that when the default outer class name conflicts with another type
// defined in the proto the compiler will append a suffix to avoid the
// conflict.
public void testConflictingOuterClassName() {
// We just need to make sure we can refer to the outer class with the
// expected name. There is nothing else to test.
OuterClassNameTestOuterClass.OuterClassNameTest message =
OuterClassNameTestOuterClass.OuterClassNameTest.newBuilder().build();
assertTrue(message.getDescriptorForType() ==
OuterClassNameTestOuterClass.OuterClassNameTest.getDescriptor());
OuterClassNameTest2OuterClass.TestMessage2.NestedMessage.OuterClassNameTest2
message2 = OuterClassNameTest2OuterClass.TestMessage2.NestedMessage
.OuterClassNameTest2.newBuilder().build();
assertEquals(0, message2.getSerializedSize());
OuterClassNameTest3OuterClass.TestMessage3.NestedMessage.OuterClassNameTest3
enumValue = OuterClassNameTest3OuterClass.TestMessage3.NestedMessage
.OuterClassNameTest3.DUMMY_VALUE;
assertEquals(1, enumValue.getNumber());
}
// =================================================================
// oneof generated code test
public void testOneofEnumCase() throws Exception {
TestOneof2 message = TestOneof2.newBuilder()
.setFooInt(123).setFooString("foo").setFooCord("bar").build();
TestUtil.assertAtMostOneFieldSetOneof(message);
}
public void testClearOneof() throws Exception {
TestOneof2.Builder builder = TestOneof2.newBuilder().setFooInt(123);
assertEquals(TestOneof2.FooCase.FOO_INT, builder.getFooCase());
builder.clearFoo();
assertEquals(TestOneof2.FooCase.FOO_NOT_SET, builder.getFooCase());
}
public void testSetOneofClearsOthers() throws Exception {
TestOneof2.Builder builder = TestOneof2.newBuilder();
TestOneof2 message =
builder.setFooInt(123).setFooString("foo").buildPartial();
assertTrue(message.hasFooString());
TestUtil.assertAtMostOneFieldSetOneof(message);
message = builder.setFooCord("bar").buildPartial();
assertTrue(message.hasFooCord());
TestUtil.assertAtMostOneFieldSetOneof(message);
message = builder.setFooStringPiece("baz").buildPartial();
assertTrue(message.hasFooStringPiece());
TestUtil.assertAtMostOneFieldSetOneof(message);
message = builder.setFooBytes(TestUtil.toBytes("qux")).buildPartial();
assertTrue(message.hasFooBytes());
TestUtil.assertAtMostOneFieldSetOneof(message);
message = builder.setFooEnum(TestOneof2.NestedEnum.FOO).buildPartial();
assertTrue(message.hasFooEnum());
TestUtil.assertAtMostOneFieldSetOneof(message);
message = builder.setFooMessage(
TestOneof2.NestedMessage.newBuilder().setQuxInt(234).build()).buildPartial();
assertTrue(message.hasFooMessage());
TestUtil.assertAtMostOneFieldSetOneof(message);
message = builder.setFooInt(123).buildPartial();
assertTrue(message.hasFooInt());
TestUtil.assertAtMostOneFieldSetOneof(message);
}
public void testOneofTypes() throws Exception {
// Primitive
{
TestOneof2.Builder builder = TestOneof2.newBuilder();
assertEquals(builder.getFooInt(), 0);
assertFalse(builder.hasFooInt());
assertTrue(builder.setFooInt(123).hasFooInt());
assertEquals(builder.getFooInt(), 123);
TestOneof2 message = builder.buildPartial();
assertTrue(message.hasFooInt());
assertEquals(message.getFooInt(), 123);
assertFalse(builder.clearFooInt().hasFooInt());
TestOneof2 message2 = builder.build();
assertFalse(message2.hasFooInt());
assertEquals(message2.getFooInt(), 0);
}
// Enum
{
TestOneof2.Builder builder = TestOneof2.newBuilder();
assertEquals(builder.getFooEnum(), TestOneof2.NestedEnum.FOO);
assertTrue(builder.setFooEnum(TestOneof2.NestedEnum.BAR).hasFooEnum());
assertEquals(builder.getFooEnum(), TestOneof2.NestedEnum.BAR);
TestOneof2 message = builder.buildPartial();
assertTrue(message.hasFooEnum());
assertEquals(message.getFooEnum(), TestOneof2.NestedEnum.BAR);
assertFalse(builder.clearFooEnum().hasFooEnum());
TestOneof2 message2 = builder.build();
assertFalse(message2.hasFooEnum());
assertEquals(message2.getFooEnum(), TestOneof2.NestedEnum.FOO);
}
// String
{
TestOneof2.Builder builder = TestOneof2.newBuilder();
assertEquals(builder.getFooString(), "");
builder.setFooString("foo");
assertTrue(builder.hasFooString());
assertEquals(builder.getFooString(), "foo");
TestOneof2 message = builder.buildPartial();
assertTrue(message.hasFooString());
assertEquals(message.getFooString(), "foo");
assertEquals(message.getFooStringBytes(), TestUtil.toBytes("foo"));
assertFalse(builder.clearFooString().hasFooString());
TestOneof2 message2 = builder.buildPartial();
assertFalse(message2.hasFooString());
assertEquals(message2.getFooString(), "");
assertEquals(message2.getFooStringBytes(), TestUtil.toBytes(""));
// Get method should not change the oneof value.
builder.setFooInt(123);
assertEquals(builder.getFooString(), "");
assertEquals(builder.getFooStringBytes(), TestUtil.toBytes(""));
assertEquals(123, builder.getFooInt());
message = builder.build();
assertEquals(message.getFooString(), "");
assertEquals(message.getFooStringBytes(), TestUtil.toBytes(""));
assertEquals(123, message.getFooInt());
}
// Cord
{
TestOneof2.Builder builder = TestOneof2.newBuilder();
assertEquals(builder.getFooCord(), "");
builder.setFooCord("foo");
assertTrue(builder.hasFooCord());
assertEquals(builder.getFooCord(), "foo");
TestOneof2 message = builder.buildPartial();
assertTrue(message.hasFooCord());
assertEquals(message.getFooCord(), "foo");
assertEquals(message.getFooCordBytes(), TestUtil.toBytes("foo"));
assertFalse(builder.clearFooCord().hasFooCord());
TestOneof2 message2 = builder.build();
assertFalse(message2.hasFooCord());
assertEquals(message2.getFooCord(), "");
assertEquals(message2.getFooCordBytes(), TestUtil.toBytes(""));
}
// StringPiece
{
TestOneof2.Builder builder = TestOneof2.newBuilder();
assertEquals(builder.getFooStringPiece(), "");
builder.setFooStringPiece("foo");
assertTrue(builder.hasFooStringPiece());
assertEquals(builder.getFooStringPiece(), "foo");
TestOneof2 message = builder.buildPartial();
assertTrue(message.hasFooStringPiece());
assertEquals(message.getFooStringPiece(), "foo");
assertEquals(message.getFooStringPieceBytes(), TestUtil.toBytes("foo"));
assertFalse(builder.clearFooStringPiece().hasFooStringPiece());
TestOneof2 message2 = builder.build();
assertFalse(message2.hasFooStringPiece());
assertEquals(message2.getFooStringPiece(), "");
assertEquals(message2.getFooStringPieceBytes(), TestUtil.toBytes(""));
}
// Message
{
// set
TestOneof2.Builder builder = TestOneof2.newBuilder();
assertEquals(builder.getFooMessage().getQuxInt(), 0);
builder.setFooMessage(
TestOneof2.NestedMessage.newBuilder().setQuxInt(234).build());
assertTrue(builder.hasFooMessage());
assertEquals(builder.getFooMessage().getQuxInt(), 234);
TestOneof2 message = builder.buildPartial();
assertTrue(message.hasFooMessage());
assertEquals(message.getFooMessage().getQuxInt(), 234);
// clear
assertFalse(builder.clearFooMessage().hasFooString());
message = builder.build();
assertFalse(message.hasFooMessage());
assertEquals(message.getFooMessage().getQuxInt(), 0);
// nested builder
builder = TestOneof2.newBuilder();
assertSame(builder.getFooMessageOrBuilder(),
TestOneof2.NestedMessage.getDefaultInstance());
assertFalse(builder.hasFooMessage());
builder.getFooMessageBuilder().setQuxInt(123);
assertTrue(builder.hasFooMessage());
assertEquals(builder.getFooMessage().getQuxInt(), 123);
message = builder.build();
assertTrue(message.hasFooMessage());
assertEquals(message.getFooMessage().getQuxInt(), 123);
}
// LazyMessage is tested in LazyMessageLiteTest.java
}
public void testOneofMerge() throws Exception {
// Primitive Type
{
TestOneof2.Builder builder = TestOneof2.newBuilder();
TestOneof2 message = builder.setFooInt(123).build();
TestOneof2 message2 = TestOneof2.newBuilder().mergeFrom(message).build();
assertTrue(message2.hasFooInt());
assertEquals(message2.getFooInt(), 123);
}
// String
{
TestOneof2.Builder builder = TestOneof2.newBuilder();
TestOneof2 message = builder.setFooString("foo").build();
TestOneof2 message2 = TestOneof2.newBuilder().mergeFrom(message).build();
assertTrue(message2.hasFooString());
assertEquals(message2.getFooString(), "foo");
}
// Enum
{
TestOneof2.Builder builder = TestOneof2.newBuilder();
TestOneof2 message = builder.setFooEnum(TestOneof2.NestedEnum.BAR).build();
TestOneof2 message2 = TestOneof2.newBuilder().mergeFrom(message).build();
assertTrue(message2.hasFooEnum());
assertEquals(message2.getFooEnum(), TestOneof2.NestedEnum.BAR);
}
// Message
{
TestOneof2.Builder builder = TestOneof2.newBuilder();
TestOneof2 message = builder.setFooMessage(
TestOneof2.NestedMessage.newBuilder().setQuxInt(234).build()).build();
TestOneof2 message2 = TestOneof2.newBuilder().mergeFrom(message).build();
assertTrue(message2.hasFooMessage());
assertEquals(message2.getFooMessage().getQuxInt(), 234);
}
}
public void testOneofSerialization() throws Exception {
// Primitive Type
{
TestOneof2.Builder builder = TestOneof2.newBuilder();
TestOneof2 message = builder.setFooInt(123).build();
ByteString serialized = message.toByteString();
TestOneof2 message2 = TestOneof2.parseFrom(serialized);
assertTrue(message2.hasFooInt());
assertEquals(message2.getFooInt(), 123);
}
// String
{
TestOneof2.Builder builder = TestOneof2.newBuilder();
TestOneof2 message = builder.setFooString("foo").build();
ByteString serialized = message.toByteString();
TestOneof2 message2 = TestOneof2.parseFrom(serialized);
assertTrue(message2.hasFooString());
assertEquals(message2.getFooString(), "foo");
}
// Enum
{
TestOneof2.Builder builder = TestOneof2.newBuilder();
TestOneof2 message = builder.setFooEnum(TestOneof2.NestedEnum.BAR).build();
ByteString serialized = message.toByteString();
TestOneof2 message2 = TestOneof2.parseFrom(serialized);
assertTrue(message2.hasFooEnum());
assertEquals(message2.getFooEnum(), TestOneof2.NestedEnum.BAR);
}
// Message
{
TestOneof2.Builder builder = TestOneof2.newBuilder();
TestOneof2 message = builder.setFooMessage(
TestOneof2.NestedMessage.newBuilder().setQuxInt(234).build()).build();
ByteString serialized = message.toByteString();
TestOneof2 message2 = TestOneof2.parseFrom(serialized);
assertTrue(message2.hasFooMessage());
assertEquals(message2.getFooMessage().getQuxInt(), 234);
}
}
}

@ -0,0 +1,134 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// 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.UnittestProto.TestAllExtensions;
import protobuf_unittest.UnittestProto.TestAllTypes;
import java.io.IOException;
import junit.framework.TestCase;
/**
* Unit test for {@link LazyFieldLite}.
*
* @author xiangl@google.com (Xiang Li)
*/
public class LazyFieldLiteTest extends TestCase {
public void testGetValue() {
MessageLite message = TestUtil.getAllSet();
LazyFieldLite lazyField = createLazyFieldLiteFromMessage(message);
assertEquals(message, lazyField.getValue(TestAllTypes.getDefaultInstance()));
changeValue(lazyField);
assertNotEqual(message, lazyField.getValue(TestAllTypes.getDefaultInstance()));
}
public void testGetValueEx() throws Exception {
TestAllExtensions message = TestUtil.getAllExtensionsSet();
LazyFieldLite lazyField = createLazyFieldLiteFromMessage(message);
assertEquals(message, lazyField.getValue(TestAllExtensions.getDefaultInstance()));
changeValue(lazyField);
assertNotEqual(message, lazyField.getValue(TestAllExtensions.getDefaultInstance()));
}
public void testSetValue() {
MessageLite message = TestUtil.getAllSet();
LazyFieldLite lazyField = createLazyFieldLiteFromMessage(message);
changeValue(lazyField);
assertNotEqual(message, lazyField.getValue(TestAllTypes.getDefaultInstance()));
message = lazyField.getValue(TestAllTypes.getDefaultInstance());
changeValue(lazyField);
assertEquals(message, lazyField.getValue(TestAllTypes.getDefaultInstance()));
}
public void testSetValueEx() throws Exception {
TestAllExtensions message = TestUtil.getAllExtensionsSet();
LazyFieldLite lazyField = createLazyFieldLiteFromMessage(message);
changeValue(lazyField);
assertNotEqual(message, lazyField.getValue(TestAllExtensions.getDefaultInstance()));
MessageLite value = lazyField.getValue(TestAllExtensions.getDefaultInstance());
changeValue(lazyField);
assertEquals(value, lazyField.getValue(TestAllExtensions.getDefaultInstance()));
}
public void testGetSerializedSize() {
MessageLite message = TestUtil.getAllSet();
LazyFieldLite lazyField = createLazyFieldLiteFromMessage(message);
assertEquals(message.getSerializedSize(), lazyField.getSerializedSize());
changeValue(lazyField);
assertNotEqual(message.getSerializedSize(), lazyField.getSerializedSize());
}
public void testGetSerializedSizeEx() throws Exception {
TestAllExtensions message = TestUtil.getAllExtensionsSet();
LazyFieldLite lazyField = createLazyFieldLiteFromMessage(message);
assertEquals(message.getSerializedSize(), lazyField.getSerializedSize());
changeValue(lazyField);
assertNotEqual(message.getSerializedSize(), lazyField.getSerializedSize());
}
public void testGetByteString() {
MessageLite message = TestUtil.getAllSet();
LazyFieldLite lazyField = createLazyFieldLiteFromMessage(message);
assertEquals(message.toByteString(), lazyField.toByteString());
changeValue(lazyField);
assertNotEqual(message.toByteString(), lazyField.toByteString());
}
public void testGetByteStringEx() throws Exception {
TestAllExtensions message = TestUtil.getAllExtensionsSet();
LazyFieldLite lazyField = createLazyFieldLiteFromMessage(message);
assertEquals(message.toByteString(), lazyField.toByteString());
changeValue(lazyField);
assertNotEqual(message.toByteString(), lazyField.toByteString());
}
// Help methods.
private LazyFieldLite createLazyFieldLiteFromMessage(MessageLite message) {
ByteString bytes = message.toByteString();
return new LazyFieldLite(TestUtil.getExtensionRegistry(), bytes);
}
private void changeValue(LazyFieldLite lazyField) {
TestAllTypes.Builder builder = TestUtil.getAllSet().toBuilder();
builder.addRepeatedBool(true);
MessageLite newMessage = builder.build();
lazyField.setValue(newMessage);
}
private void assertNotEqual(Object unexpected, Object actual) {
assertFalse(unexpected == actual
|| (unexpected != null && unexpected.equals(actual)));
}
}

@ -0,0 +1,121 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// 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.UnittestProto.TestAllExtensions;
import protobuf_unittest.UnittestProto.TestAllTypes;
import java.io.IOException;
import junit.framework.TestCase;
/**
* Unit test for {@link LazyField}.
*
* @author xiangl@google.com (Xiang Li)
*/
public class LazyFieldTest extends TestCase {
public void testHashCode() {
MessageLite message = TestUtil.getAllSet();
LazyField lazyField =
createLazyFieldFromMessage(message);
assertEquals(message.hashCode(), lazyField.hashCode());
lazyField.getValue();
assertEquals(message.hashCode(), lazyField.hashCode());
changeValue(lazyField);
// make sure two messages have different hash code
assertNotEqual(message.hashCode(), lazyField.hashCode());
}
public void testHashCodeEx() throws Exception {
TestAllExtensions message = TestUtil.getAllExtensionsSet();
LazyField lazyField = createLazyFieldFromMessage(message);
assertEquals(message.hashCode(), lazyField.hashCode());
lazyField.getValue();
assertEquals(message.hashCode(), lazyField.hashCode());
changeValue(lazyField);
// make sure two messages have different hash code
assertNotEqual(message.hashCode(), lazyField.hashCode());
}
public void testGetValue() {
MessageLite message = TestUtil.getAllSet();
LazyField lazyField = createLazyFieldFromMessage(message);
assertEquals(message, lazyField.getValue());
changeValue(lazyField);
assertNotEqual(message, lazyField.getValue());
}
public void testGetValueEx() throws Exception {
TestAllExtensions message = TestUtil.getAllExtensionsSet();
LazyField lazyField = createLazyFieldFromMessage(message);
assertEquals(message, lazyField.getValue());
changeValue(lazyField);
assertNotEqual(message, lazyField.getValue());
}
public void testEqualsObject() {
MessageLite message = TestUtil.getAllSet();
LazyField lazyField = createLazyFieldFromMessage(message);
assertTrue(lazyField.equals(message));
changeValue(lazyField);
assertFalse(lazyField.equals(message));
assertFalse(message.equals(lazyField.getValue()));
}
public void testEqualsObjectEx() throws Exception {
TestAllExtensions message = TestUtil.getAllExtensionsSet();
LazyField lazyField = createLazyFieldFromMessage(message);
assertTrue(lazyField.equals(message));
changeValue(lazyField);
assertFalse(lazyField.equals(message));
assertFalse(message.equals(lazyField.getValue()));
}
// Help methods.
private LazyField createLazyFieldFromMessage(MessageLite message) {
ByteString bytes = message.toByteString();
return new LazyField(message.getDefaultInstanceForType(),
TestUtil.getExtensionRegistry(), bytes);
}
private void changeValue(LazyField lazyField) {
TestAllTypes.Builder builder = TestUtil.getAllSet().toBuilder();
builder.addRepeatedBool(true);
MessageLite newMessage = builder.build();
lazyField.setValue(newMessage);
}
private void assertNotEqual(Object unexpected, Object actual) {
assertFalse(unexpected == actual
|| (unexpected != null && unexpected.equals(actual)));
}
}

@ -0,0 +1,319 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// 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.LazyFieldsLite.LazyInnerMessageLite;
import protobuf_unittest.LazyFieldsLite.LazyMessageLite;
import protobuf_unittest.LazyFieldsLite.LazyNestedInnerMessageLite;
import junit.framework.TestCase;
import org.easymock.classextension.EasyMock;
import java.util.ArrayList;
/**
* Unit test for messages with lazy fields.
*
* @author niwasaki@google.com (Naoki Iwasaki)
*/
public class LazyMessageLiteTest extends TestCase {
private Parser<LazyInnerMessageLite> originalLazyInnerMessageLiteParser;
@Override
protected void setUp() throws Exception {
super.setUp();
originalLazyInnerMessageLiteParser = LazyInnerMessageLite.PARSER;
}
@Override
protected void tearDown() throws Exception {
LazyInnerMessageLite.PARSER = originalLazyInnerMessageLiteParser;
super.tearDown();
}
public void testSetValues() {
LazyNestedInnerMessageLite nested = LazyNestedInnerMessageLite.newBuilder()
.setNum(3)
.build();
LazyInnerMessageLite inner = LazyInnerMessageLite.newBuilder()
.setNum(2)
.setNested(nested)
.build();
LazyMessageLite outer = LazyMessageLite.newBuilder()
.setNum(1)
.setInner(inner)
.setOneofNum(123)
.setOneofInner(inner)
.build();
assertEquals(1, outer.getNum());
assertEquals(421, outer.getNumWithDefault());
assertEquals(2, outer.getInner().getNum());
assertEquals(42, outer.getInner().getNumWithDefault());
assertEquals(3, outer.getInner().getNested().getNum());
assertEquals(4, outer.getInner().getNested().getNumWithDefault());
assertFalse(outer.hasOneofNum());
assertTrue(outer.hasOneofInner());
assertEquals(2, outer.getOneofInner().getNum());
assertEquals(42, outer.getOneofInner().getNumWithDefault());
assertEquals(3, outer.getOneofInner().getNested().getNum());
assertEquals(4, outer.getOneofInner().getNested().getNumWithDefault());
}
public void testSetRepeatedValues() {
LazyMessageLite outer = LazyMessageLite.newBuilder()
.setNum(1)
.addRepeatedInner(LazyInnerMessageLite.newBuilder().setNum(119))
.addRepeatedInner(LazyInnerMessageLite.newBuilder().setNum(122))
.build();
assertEquals(1, outer.getNum());
assertEquals(2, outer.getRepeatedInnerCount());
assertEquals(119, outer.getRepeatedInner(0).getNum());
assertEquals(122, outer.getRepeatedInner(1).getNum());
}
public void testAddAll() {
ArrayList<LazyInnerMessageLite> inners = new ArrayList<LazyInnerMessageLite>();
int count = 4;
for (int i = 0; i < count; i++) {
LazyInnerMessageLite inner = LazyInnerMessageLite.newBuilder()
.setNum(i)
.build();
inners.add(inner);
}
LazyMessageLite outer = LazyMessageLite.newBuilder()
.addAllRepeatedInner(inners)
.build();
assertEquals(count, outer.getRepeatedInnerCount());
for (int i = 0; i < count; i++) {
assertEquals(i, outer.getRepeatedInner(i).getNum());
}
}
public void testGetDefaultValues() {
LazyMessageLite outer = LazyMessageLite.newBuilder()
.build();
assertEquals(0, outer.getNum());
assertEquals(421, outer.getNumWithDefault());
assertEquals(0, outer.getInner().getNum());
assertEquals(42, outer.getInner().getNumWithDefault());
assertEquals(0, outer.getInner().getNested().getNum());
assertEquals(4, outer.getInner().getNested().getNumWithDefault());
assertEquals(0, outer.getOneofNum());
assertEquals(0, outer.getOneofInner().getNum());
assertEquals(42, outer.getOneofInner().getNumWithDefault());
assertEquals(0, outer.getOneofInner().getNested().getNum());
assertEquals(4, outer.getOneofInner().getNested().getNumWithDefault());
}
public void testClearValues() {
LazyInnerMessageLite inner = LazyInnerMessageLite.newBuilder()
.setNum(115)
.build();
LazyMessageLite.Builder outerBuilder = LazyMessageLite.newBuilder();
assertEquals(0, outerBuilder.build().getNum());
// Set/Clear num
outerBuilder.setNum(100);
assertEquals(100, outerBuilder.build().getNum());
assertEquals(421, outerBuilder.build().getNumWithDefault());
assertFalse(outerBuilder.build().hasInner());
outerBuilder.clearNum();
assertEquals(0, outerBuilder.build().getNum());
assertEquals(421, outerBuilder.build().getNumWithDefault());
assertFalse(outerBuilder.build().hasInner());
// Set/Clear all
outerBuilder.setNum(100)
.setInner(inner)
.addRepeatedInner(LazyInnerMessageLite.newBuilder().setNum(119))
.addRepeatedInner(LazyInnerMessageLite.newBuilder().setNum(122))
.setOneofInner(LazyInnerMessageLite.newBuilder().setNum(123));
LazyMessageLite outer = outerBuilder.build();
assertEquals(100, outer.getNum());
assertEquals(421, outer.getNumWithDefault());
assertTrue(outer.hasInner());
assertEquals(115, outer.getInner().getNum());
assertEquals(2, outer.getRepeatedInnerCount());
assertEquals(119, outer.getRepeatedInner(0).getNum());
assertEquals(122, outer.getRepeatedInner(1).getNum());
assertTrue(outer.hasOneofInner());
assertEquals(123, outer.getOneofInner().getNum());
outerBuilder.clear();
outer = outerBuilder.build();
assertEquals(0, outer.getNum());
assertEquals(421, outer.getNumWithDefault());
assertFalse(outer.hasInner());
assertEquals(0, outer.getRepeatedInnerCount());
assertFalse(outer.hasOneofInner());
assertEquals(0, outer.getOneofInner().getNum());
}
public void testMergeValues() {
LazyMessageLite outerBase = LazyMessageLite.newBuilder()
.setNumWithDefault(122)
.build();
LazyInnerMessageLite innerMerging = LazyInnerMessageLite.newBuilder()
.setNum(115)
.build();
LazyMessageLite outerMerging = LazyMessageLite.newBuilder()
.setNum(119)
.setInner(innerMerging)
.setOneofInner(innerMerging)
.build();
LazyMessageLite merged = LazyMessageLite
.newBuilder(outerBase)
.mergeFrom(outerMerging)
.build();
assertEquals(119, merged.getNum());
assertEquals(122, merged.getNumWithDefault());
assertEquals(115, merged.getInner().getNum());
assertEquals(42, merged.getInner().getNumWithDefault());
assertEquals(115, merged.getOneofInner().getNum());
assertEquals(42, merged.getOneofInner().getNumWithDefault());
}
public void testMergeDefaultValues() {
LazyInnerMessageLite innerBase = LazyInnerMessageLite.newBuilder()
.setNum(115)
.build();
LazyMessageLite outerBase = LazyMessageLite.newBuilder()
.setNum(119)
.setNumWithDefault(122)
.setInner(innerBase)
.setOneofInner(innerBase)
.build();
LazyMessageLite outerMerging = LazyMessageLite.newBuilder()
.build();
LazyMessageLite merged = LazyMessageLite
.newBuilder(outerBase)
.mergeFrom(outerMerging)
.build();
// Merging default-instance shouldn't overwrite values in the base message.
assertEquals(119, merged.getNum());
assertEquals(122, merged.getNumWithDefault());
assertEquals(115, merged.getInner().getNum());
assertEquals(42, merged.getInner().getNumWithDefault());
assertEquals(115, merged.getOneofInner().getNum());
assertEquals(42, merged.getOneofInner().getNumWithDefault());
}
public void testSerialize() throws InvalidProtocolBufferException {
LazyNestedInnerMessageLite nested = LazyNestedInnerMessageLite.newBuilder()
.setNum(3)
.build();
LazyInnerMessageLite inner = LazyInnerMessageLite.newBuilder()
.setNum(2)
.setNested(nested)
.build();
LazyMessageLite outer = LazyMessageLite.newBuilder()
.setNum(1)
.setInner(inner)
.setOneofInner(inner)
.build();
ByteString bytes = outer.toByteString();
assertEquals(bytes.size(), outer.getSerializedSize());
LazyMessageLite deserialized = LazyMessageLite.parseFrom(bytes);
assertEquals(1, deserialized.getNum());
assertEquals(421, deserialized.getNumWithDefault());
assertEquals(2, deserialized.getInner().getNum());
assertEquals(42, deserialized.getInner().getNumWithDefault());
assertEquals(3, deserialized.getInner().getNested().getNum());
assertEquals(4, deserialized.getInner().getNested().getNumWithDefault());
assertEquals(2, deserialized.getOneofInner().getNum());
assertEquals(42, deserialized.getOneofInner().getNumWithDefault());
assertEquals(3, deserialized.getOneofInner().getNested().getNum());
assertEquals(4, deserialized.getOneofInner().getNested().getNumWithDefault());
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);
}
}

@ -67,6 +67,14 @@ public class LazyStringArrayListTest extends TestCase {
list.remove(1);
assertSame(STRING_A, list.get(0));
assertSame(STRING_C, list.get(1));
List<ByteString> byteStringList = list.asByteStringList();
assertEquals(BYTE_STRING_A, byteStringList.get(0));
assertEquals(BYTE_STRING_C, byteStringList.get(1));
// Underlying list should be transformed.
assertSame(byteStringList.get(0), list.getByteString(0));
assertSame(byteStringList.get(1), list.getByteString(1));
}
public void testJustByteString() {
@ -83,6 +91,10 @@ public class LazyStringArrayListTest extends TestCase {
list.remove(1);
assertSame(BYTE_STRING_A, list.getByteString(0));
assertSame(BYTE_STRING_C, list.getByteString(1));
List<ByteString> byteStringList = list.asByteStringList();
assertSame(BYTE_STRING_A, byteStringList.get(0));
assertSame(BYTE_STRING_C, byteStringList.get(1));
}
public void testConversionBackAndForth() {

@ -0,0 +1,85 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// 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.Bar;
import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.BarPrime;
import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Foo;
import junit.framework.TestCase;
/**
* Test generate equal and hash methods for the lite runtime.
*
* @author pbogle@google.com Phil Bogle
*/
public class LiteEqualsAndHashTest extends TestCase {
public void testEquals() throws Exception {
// Since the generated equals and hashCode methods for lite messages are a
// mostly complete subset of those for regular messages, we can mostly assume
// that the generated methods are already thoroughly tested by the regular tests.
// This test mostly just verifies is that a proto with
// optimize_for = LITE_RUNTIME and java_generates_equals_and_hash_compiles
// correctly when linked only against the lite library.
// We do however do some basic testing to make sure that equals is actually
// overriden to test for value equality rather than simple object equality.
// Check that two identical objs are equal.
Foo foo1a = Foo.newBuilder()
.setValue(1)
.addBar(Bar.newBuilder().setName("foo1"))
.build();
Foo foo1b = Foo.newBuilder()
.setValue(1)
.addBar(Bar.newBuilder().setName("foo1"))
.build();
Foo foo2 = Foo.newBuilder()
.setValue(1)
.addBar(Bar.newBuilder().setName("foo2"))
.build();
// Check that equals is doing value rather than object equality.
assertEquals(foo1a, foo1b);
assertEquals(foo1a.hashCode(), foo1b.hashCode());
// Check that a diffeent object is not equal.
assertFalse(foo1a.equals(foo2));
// Check that two objects which have different types but the same field values are not
// considered to be equal.
Bar bar = Bar.newBuilder().setName("bar").build();
BarPrime barPrime = BarPrime.newBuilder().setName("bar").build();
assertFalse(bar.equals(barPrime));
}
}

@ -33,15 +33,14 @@ package com.google.protobuf;
import com.google.protobuf.UnittestLite.TestAllTypesLite;
import com.google.protobuf.UnittestLite.TestPackedExtensionsLite;
import com.google.protobuf.UnittestLite.TestParsingMergeLite;
import com.google.protobuf.UnittestLite;
import protobuf_unittest.UnittestOptimizeFor.TestOptimizedForSize;
import protobuf_unittest.UnittestOptimizeFor.TestRequiredOptimizedForSize;
import protobuf_unittest.UnittestOptimizeFor;
import protobuf_unittest.UnittestProto.ForeignMessage;
import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestEmptyMessage;
import protobuf_unittest.UnittestProto.TestRequired;
import protobuf_unittest.UnittestProto.TestParsingMerge;
import protobuf_unittest.UnittestProto.TestRequired;
import protobuf_unittest.UnittestProto;
import junit.framework.TestCase;
@ -84,12 +83,15 @@ public class ParserTest extends TestCase {
CodedInputStream.newInstance(data), registry));
}
@SuppressWarnings("unchecked")
private void assertRoundTripEquals(MessageLite message) throws Exception {
final byte[] data = message.toByteArray();
final int offset = 20;
final int length = data.length;
final int padding = 30;
Parser<? extends MessageLite> parser = message.getParserForType();
Parser<MessageLite> parser =
(Parser<MessageLite>) message.getParserForType();
assertMessageEquals(message, parser.parseFrom(data));
assertMessageEquals(message, parser.parseFrom(
generatePaddingArray(data, offset, padding),
@ -101,7 +103,8 @@ public class ParserTest extends TestCase {
CodedInputStream.newInstance(data)));
}
private void assertMessageEquals(MessageLite expected, MessageLite actual)
private void assertMessageEquals(
MessageLite expected, MessageLite actual)
throws Exception {
if (expected instanceof Message) {
assertEquals(expected, actual);
@ -120,15 +123,17 @@ public class ParserTest extends TestCase {
assertRoundTripEquals(TestUtil.getAllSet());
}
public void testParsePartial() throws Exception {
Parser<TestRequired> parser = TestRequired.PARSER;
assertParsePartial(TestRequired.PARSER,
TestRequired.newBuilder().setA(1).buildPartial());
}
private <T extends MessageLite> void assertParsePartial(
Parser<T> parser, T partialMessage) throws Exception {
final String errorString =
"Should throw exceptions when the parsed message isn't initialized.";
// TestRequired.b and TestRequired.c are not set.
TestRequired partialMessage = TestRequired.newBuilder()
.setA(1).buildPartial();
// parsePartialFrom should pass.
byte[] data = partialMessage.toByteArray();
assertEquals(partialMessage, parser.parsePartialFrom(data));
@ -218,6 +223,7 @@ public class ParserTest extends TestCase {
emptyMessage.toByteString());
}
public void testOptimizeForSize() throws Exception {
TestOptimizedForSize.Builder builder = TestOptimizedForSize.newBuilder();
builder.setI(12).setMsg(ForeignMessage.newBuilder().setC(34).build());

@ -60,4 +60,37 @@ public class TestBadIdentifiers extends TestCase {
.Descriptor.NestedDescriptor.getDefaultInstance()
.getDescriptorForType();
}
public void testConflictingFieldNames() throws Exception {
TestBadIdentifiersProto.TestConflictingFieldNames message =
TestBadIdentifiersProto.TestConflictingFieldNames.getDefaultInstance();
// Make sure generated accessors are properly named.
assertEquals(0, message.getInt32Field1Count());
assertEquals(0, message.getEnumField2Count());
assertEquals(0, message.getStringField3Count());
assertEquals(0, message.getBytesField4Count());
assertEquals(0, message.getMessageField5Count());
assertEquals(0, message.getInt32FieldCount11());
assertEquals(1, message.getEnumFieldCount12().getNumber());
assertEquals("", message.getStringFieldCount13());
assertEquals(ByteString.EMPTY, message.getBytesFieldCount14());
assertEquals(0, message.getMessageFieldCount15().getSerializedSize());
assertEquals(0, message.getInt32Field21Count());
assertEquals(0, message.getEnumField22Count());
assertEquals(0, message.getStringField23Count());
assertEquals(0, message.getBytesField24Count());
assertEquals(0, message.getMessageField25Count());
assertEquals(0, message.getInt32Field1List().size());
assertEquals(0, message.getInt32FieldList31());
assertEquals(0, message.getInt64FieldCount());
assertEquals(0L, message.getExtension(
TestBadIdentifiersProto.TestConflictingFieldNames.int64FieldCount).longValue());
assertEquals(0L, message.getExtension(
TestBadIdentifiersProto.TestConflictingFieldNames.int64FieldList).longValue());
}
}

@ -56,6 +56,11 @@ import static protobuf_unittest.UnittestProto.defaultImportEnumExtension;
import static protobuf_unittest.UnittestProto.defaultStringPieceExtension;
import static protobuf_unittest.UnittestProto.defaultCordExtension;
import static protobuf_unittest.UnittestProto.oneofUint32Extension;
import static protobuf_unittest.UnittestProto.oneofNestedMessageExtension;
import static protobuf_unittest.UnittestProto.oneofStringExtension;
import static protobuf_unittest.UnittestProto.oneofBytesExtension;
import static protobuf_unittest.UnittestProto.optionalInt32Extension;
import static protobuf_unittest.UnittestProto.optionalInt64Extension;
import static protobuf_unittest.UnittestProto.optionalUint32Extension;
@ -148,6 +153,11 @@ import static com.google.protobuf.UnittestLite.defaultImportEnumExtensionLite;
import static com.google.protobuf.UnittestLite.defaultStringPieceExtensionLite;
import static com.google.protobuf.UnittestLite.defaultCordExtensionLite;
import static com.google.protobuf.UnittestLite.oneofUint32ExtensionLite;
import static com.google.protobuf.UnittestLite.oneofNestedMessageExtensionLite;
import static com.google.protobuf.UnittestLite.oneofStringExtensionLite;
import static com.google.protobuf.UnittestLite.oneofBytesExtensionLite;
import static com.google.protobuf.UnittestLite.optionalInt32ExtensionLite;
import static com.google.protobuf.UnittestLite.optionalInt64ExtensionLite;
import static com.google.protobuf.UnittestLite.optionalUint32ExtensionLite;
@ -223,6 +233,7 @@ import protobuf_unittest.UnittestProto.TestAllExtensions;
import protobuf_unittest.UnittestProto.TestAllExtensionsOrBuilder;
import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder;
import protobuf_unittest.UnittestProto.TestOneof2;
import protobuf_unittest.UnittestProto.TestPackedExtensions;
import protobuf_unittest.UnittestProto.TestPackedTypes;
import protobuf_unittest.UnittestProto.TestUnpackedTypes;
@ -469,6 +480,12 @@ public final class TestUtil {
message.setDefaultStringPiece("424");
message.setDefaultCord("425");
message.setOneofUint32(601);
message.setOneofNestedMessage(
TestAllTypes.NestedMessage.newBuilder().setBb(602).build());
message.setOneofString("603");
message.setOneofBytes(toBytes("604"));
}
// -------------------------------------------------------------------
@ -718,6 +735,13 @@ public final class TestUtil {
Assert.assertEquals("424", message.getDefaultStringPiece());
Assert.assertEquals("425", message.getDefaultCord());
Assert.assertFalse(message.hasOneofUint32());
Assert.assertFalse(message.hasOneofNestedMessage());
Assert.assertFalse(message.hasOneofString());
Assert.assertTrue(message.hasOneofBytes());
Assert.assertEquals(toBytes("604"), message.getOneofBytes());
}
// -------------------------------------------------------------------
@ -872,6 +896,11 @@ public final class TestUtil {
Assert.assertEquals("abc", message.getDefaultStringPiece());
Assert.assertEquals("123", message.getDefaultCord());
Assert.assertFalse(message.hasOneofUint32());
Assert.assertFalse(message.hasOneofNestedMessage());
Assert.assertFalse(message.hasOneofString());
Assert.assertFalse(message.hasOneofBytes());
}
// -------------------------------------------------------------------
@ -1358,6 +1387,12 @@ public final class TestUtil {
message.setExtension(defaultStringPieceExtension, "424");
message.setExtension(defaultCordExtension, "425");
message.setExtension(oneofUint32Extension, 601);
message.setExtension(oneofNestedMessageExtension,
TestAllTypes.NestedMessage.newBuilder().setBb(602).build());
message.setExtension(oneofStringExtension, "603");
message.setExtension(oneofBytesExtension, toBytes("604"));
}
// -------------------------------------------------------------------
@ -1621,6 +1656,10 @@ public final class TestUtil {
assertEqualsExactType("424", message.getExtension(defaultStringPieceExtension));
assertEqualsExactType("425", message.getExtension(defaultCordExtension));
Assert.assertTrue(message.hasExtension(oneofBytesExtension));
assertEqualsExactType(toBytes("604"), message.getExtension(oneofBytesExtension));
}
// -------------------------------------------------------------------
@ -1807,6 +1846,11 @@ public final class TestUtil {
assertEqualsExactType("abc", message.getExtension(defaultStringPieceExtension));
assertEqualsExactType("123", message.getExtension(defaultCordExtension));
Assert.assertFalse(message.hasExtension(oneofUint32Extension));
Assert.assertFalse(message.hasExtension(oneofNestedMessageExtension));
Assert.assertFalse(message.hasExtension(oneofStringExtension));
Assert.assertFalse(message.hasExtension(oneofBytesExtension));
}
// -------------------------------------------------------------------
@ -2133,6 +2177,12 @@ public final class TestUtil {
message.setExtension(defaultStringPieceExtensionLite, "424");
message.setExtension(defaultCordExtensionLite, "425");
message.setExtension(oneofUint32ExtensionLite, 601);
message.setExtension(oneofNestedMessageExtensionLite,
TestAllTypesLite.NestedMessage.newBuilder().setBb(602).build());
message.setExtension(oneofStringExtensionLite, "603");
message.setExtension(oneofBytesExtensionLite, toBytes("604"));
}
// -------------------------------------------------------------------
@ -2397,6 +2447,10 @@ public final class TestUtil {
assertEqualsExactType("424", message.getExtension(defaultStringPieceExtensionLite));
assertEqualsExactType("425", message.getExtension(defaultCordExtensionLite));
Assert.assertTrue(message.hasExtension(oneofBytesExtensionLite));
assertEqualsExactType(toBytes("604"), message.getExtension(oneofBytesExtensionLite));
}
// -------------------------------------------------------------------
@ -2562,6 +2616,11 @@ public final class TestUtil {
assertEqualsExactType("abc", message.getExtension(defaultStringPieceExtensionLite));
assertEqualsExactType("123", message.getExtension(defaultCordExtensionLite));
Assert.assertFalse(message.hasExtension(oneofUint32ExtensionLite));
Assert.assertFalse(message.hasExtension(oneofNestedMessageExtensionLite));
Assert.assertFalse(message.hasExtension(oneofStringExtensionLite));
Assert.assertFalse(message.hasExtension(oneofBytesExtensionLite));
}
// -------------------------------------------------------------------
@ -2749,6 +2808,82 @@ public final class TestUtil {
message.getExtension(packedEnumExtensionLite, 1));
}
// ===================================================================
// oneof
public static void setOneof(TestOneof2.Builder message) {
message.setFooLazyMessage(
TestOneof2.NestedMessage.newBuilder().setQuxInt(100).build());
message.setBarString("101");
message.setBazInt(102);
message.setBazString("103");
}
public static void assertOneofSet(TestOneof2 message) {
Assert.assertTrue(message.hasFooLazyMessage ());
Assert.assertTrue(message.getFooLazyMessage().hasQuxInt());
Assert.assertTrue(message.hasBarString());
Assert.assertTrue(message.hasBazInt ());
Assert.assertTrue(message.hasBazString());
Assert.assertEquals(100 , message.getFooLazyMessage().getQuxInt());
Assert.assertEquals("101", message.getBarString ());
Assert.assertEquals(102 , message.getBazInt ());
Assert.assertEquals("103", message.getBazString ());
}
public static void assertAtMostOneFieldSetOneof(TestOneof2 message) {
int count = 0;
if (message.hasFooInt()) { ++count; }
if (message.hasFooString()) { ++count; }
if (message.hasFooCord()) { ++count; }
if (message.hasFooStringPiece()) { ++count; }
if (message.hasFooBytes()) { ++count; }
if (message.hasFooEnum()) { ++count; }
if (message.hasFooMessage()) { ++count; }
if (message.hasFooGroup()) { ++count; }
if (message.hasFooLazyMessage()) { ++count; }
Assert.assertTrue(count <= 1);
count = 0;
if (message.hasBarInt()) { ++count; }
if (message.hasBarString()) { ++count; }
if (message.hasBarCord()) { ++count; }
if (message.hasBarStringPiece()) { ++count; }
if (message.hasBarBytes()) { ++count; }
if (message.hasBarEnum()) { ++count; }
Assert.assertTrue(count <= 1);
switch (message.getFooCase()) {
case FOO_INT:
Assert.assertTrue(message.hasFooInt());
break;
case FOO_STRING:
Assert.assertTrue(message.hasFooString());
break;
case FOO_CORD:
Assert.assertTrue(message.hasFooCord());
break;
case FOO_BYTES:
Assert.assertTrue(message.hasFooBytes());
break;
case FOO_ENUM:
Assert.assertTrue(message.hasFooEnum());
break;
case FOO_MESSAGE:
Assert.assertTrue(message.hasFooMessage());
break;
case FOOGROUP:
Assert.assertTrue(message.hasFooGroup());
break;
case FOO_LAZY_MESSAGE:
Assert.assertTrue(message.hasFooLazyMessage());
break;
case FOO_NOT_SET:
break;
}
}
// =================================================================
/**
@ -2915,8 +3050,8 @@ public final class TestUtil {
return parent.newBuilderForField(field);
} else {
ExtensionRegistry.ExtensionInfo extension =
extensionRegistry.findExtensionByNumber(field.getContainingType(),
field.getNumber());
extensionRegistry.findImmutableExtensionByNumber(
field.getContainingType(), field.getNumber());
Assert.assertNotNull(extension);
Assert.assertNotNull(extension.defaultInstance);
return extension.defaultInstance.newBuilderForType();
@ -3078,6 +3213,13 @@ public final class TestUtil {
message.setField(f("default_string_piece" ), "424");
message.setField(f("default_cord" ), "425");
message.setField(f("oneof_uint32" ), 601);
message.setField(f("oneof_nested_message"),
newBuilderForField(message, f("oneof_nested_message"))
.setField(nestedB, 602).build());
message.setField(f("oneof_string" ), "603");
message.setField(f("oneof_bytes" ), toBytes("604"));
}
// -------------------------------------------------------------------
@ -3372,6 +3514,24 @@ public final class TestUtil {
Assert.assertEquals("424", message.getField(f("default_string_piece")));
Assert.assertEquals("425", message.getField(f("default_cord")));
Assert.assertTrue(message.hasField(f("oneof_bytes")));
Assert.assertEquals(toBytes("604"), message.getField(f("oneof_bytes")));
if (extensionRegistry == null) {
Assert.assertFalse(message.hasField(f("oneof_uint32")));
Assert.assertFalse(message.hasField(f("oneof_nested_message")));
Assert.assertFalse(message.hasField(f("oneof_string")));
} else {
Assert.assertTrue(message.hasField(f("oneof_uint32")));
Assert.assertTrue(message.hasField(f("oneof_nested_message")));
Assert.assertTrue(message.hasField(f("oneof_string")));
Assert.assertEquals(601, message.getField(f("oneof_uint32")));
Assert.assertEquals(602,
((MessageOrBuilder) message.getField(f("oneof_nested_message")))
.getField(nestedB));
Assert.assertEquals("603", message.getField(f("oneof_string")));
}
}
// -------------------------------------------------------------------
@ -3549,6 +3709,15 @@ public final class TestUtil {
Assert.assertEquals("abc", message.getField(f("default_string_piece")));
Assert.assertEquals("123", message.getField(f("default_cord")));
Assert.assertFalse(message.hasField(f("oneof_uint32")));
Assert.assertFalse(message.hasField(f("oneof_nested_message")));
Assert.assertFalse(message.hasField(f("oneof_string")));
Assert.assertFalse(message.hasField(f("oneof_bytes")));
Assert.assertEquals(0, message.getField(f("oneof_uint32")));
Assert.assertEquals("", message.getField(f("oneof_string")));
Assert.assertEquals(toBytes(""), message.getField(f("oneof_bytes")));
}
@ -3910,7 +4079,7 @@ public final class TestUtil {
*/
public static ByteString getGoldenMessage() {
if (goldenMessage == null) {
goldenMessage = readBytesFromFile("golden_message");
goldenMessage = readBytesFromFile("golden_message_oneof_implemented");
}
return goldenMessage;
}

@ -31,6 +31,7 @@
package com.google.protobuf;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.TextFormat.Parser.SingularOverwritePolicy;
import protobuf_unittest.UnittestMset.TestMessageSet;
import protobuf_unittest.UnittestMset.TestMessageSetExtension1;
import protobuf_unittest.UnittestMset.TestMessageSetExtension2;
@ -39,6 +40,7 @@ import protobuf_unittest.UnittestProto.TestAllExtensions;
import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage;
import protobuf_unittest.UnittestProto.TestEmptyMessage;
import protobuf_unittest.UnittestProto.TestOneof2;
import junit.framework.TestCase;
@ -64,7 +66,7 @@ public class TextFormatTest extends TestCase {
+ "and \\t tabs and \\001 slashes \\\\";
private static String allFieldsSetText = TestUtil.readTextFromFile(
"text_format_unittest_data.txt");
"text_format_unittest_data_oneof_implemented.txt");
private static String allExtensionsSetText = TestUtil.readTextFromFile(
"text_format_unittest_extensions_data.txt");
@ -109,6 +111,26 @@ public class TextFormatTest extends TestCase {
" str: \"foo\"\n" +
"}\n";
private String messageSetTextWithRepeatedExtension =
"[protobuf_unittest.TestMessageSetExtension1] {\n" +
" i: 123\n" +
"}\n" +
"[protobuf_unittest.TestMessageSetExtension1] {\n" +
" i: 456\n" +
"}\n";
private final TextFormat.Parser parserAllowingUnknownFields =
TextFormat.Parser.newBuilder().setAllowUnknownFields(true).build();
private final TextFormat.Parser parserWithOverwriteForbidden =
TextFormat.Parser.newBuilder()
.setSingularOverwritePolicy(
SingularOverwritePolicy.FORBID_SINGULAR_OVERWRITES)
.build();
private final TextFormat.Parser defaultParser =
TextFormat.Parser.newBuilder().build();
/** Print TestAllTypes and compare with golden file. */
public void testPrintMessage() throws Exception {
String javaText = TextFormat.printToString(TestUtil.getAllSet());
@ -250,8 +272,8 @@ public class TextFormatTest extends TestCase {
.addRepeatedInt32 (1 << 31)
.addRepeatedUint32(1 << 31)
.addRepeatedInt64 (1l << 63)
.addRepeatedUint64(1l << 63)
.addRepeatedInt64 (1L << 63)
.addRepeatedUint64(1L << 63)
// Floats of various precisions and exponents.
.addRepeatedDouble(123)
@ -371,6 +393,40 @@ public class TextFormatTest extends TestCase {
TestMessageSetExtension2.messageSetExtension));
assertEquals("foo", messageSet.getExtension(
TestMessageSetExtension2.messageSetExtension).getStr());
builder = TestMessageSet.newBuilder();
TextFormat.merge(messageSetTextWithRepeatedExtension, extensionRegistry,
builder);
messageSet = builder.build();
assertEquals(456, messageSet.getExtension(
TestMessageSetExtension1.messageSetExtension).getI());
}
public void testParseMessageSetWithOverwriteForbidden() throws Exception {
ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance();
extensionRegistry.add(TestMessageSetExtension1.messageSetExtension);
extensionRegistry.add(TestMessageSetExtension2.messageSetExtension);
TestMessageSet.Builder builder = TestMessageSet.newBuilder();
parserWithOverwriteForbidden.merge(
messageSetText, extensionRegistry, builder);
TestMessageSet messageSet = builder.build();
assertEquals(123, messageSet.getExtension(
TestMessageSetExtension1.messageSetExtension).getI());
assertEquals("foo", messageSet.getExtension(
TestMessageSetExtension2.messageSetExtension).getStr());
builder = TestMessageSet.newBuilder();
try {
parserWithOverwriteForbidden.merge(
messageSetTextWithRepeatedExtension, extensionRegistry, builder);
fail("expected parse exception");
} catch (TextFormat.ParseException e) {
assertEquals("6:1: Non-repeated field "
+ "\"protobuf_unittest.TestMessageSetExtension1.message_set_extension\""
+ " cannot be overwritten.",
e.getMessage());
}
}
public void testParseNumericEnum() throws Exception {
@ -407,12 +463,51 @@ public class TextFormatTest extends TestCase {
}
}
private void assertParseErrorWithUnknownFields(String error, String text) {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
try {
parserAllowingUnknownFields.merge(
text, TestUtil.getExtensionRegistry(), builder);
fail("Expected parse exception.");
} catch (TextFormat.ParseException e) {
assertEquals(error, e.getMessage());
}
}
private TestAllTypes assertParseSuccessWithUnknownFields(String text)
throws TextFormat.ParseException {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
parserAllowingUnknownFields.merge(
text, TestUtil.getExtensionRegistry(), builder);
return builder.build();
}
private void assertParseErrorWithOverwriteForbidden(String error,
String text) {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
try {
parserWithOverwriteForbidden.merge(
text, TestUtil.getExtensionRegistry(), builder);
fail("Expected parse exception.");
} catch (TextFormat.ParseException e) {
assertEquals(error, e.getMessage());
}
}
private TestAllTypes assertParseSuccessWithOverwriteForbidden(
String text) throws TextFormat.ParseException {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
parserWithOverwriteForbidden.merge(
text, TestUtil.getExtensionRegistry(), builder);
return builder.build();
}
public void testParseErrors() throws Exception {
assertParseError(
"1:16: Expected \":\".",
"optional_int32 123");
assertParseError(
"1:23: Expected identifier.",
"1:23: Expected identifier. Found '?'",
"optional_nested_enum: ?");
assertParseError(
"1:18: Couldn't parse integer: Number must be positive: -1",
@ -469,10 +564,10 @@ public class TextFormatTest extends TestCase {
// Delimiters must match.
assertParseError(
"1:22: Expected identifier.",
"1:22: Expected identifier. Found '}'",
"OptionalGroup < a: 1 }");
assertParseError(
"1:22: Expected identifier.",
"1:22: Expected identifier. Found '>'",
"OptionalGroup { a: 1 >");
}
@ -762,7 +857,7 @@ public class TextFormatTest extends TestCase {
TextFormat.shortDebugString(makeUnknownFieldSet()));
}
public void testPrintToUnicodeString() {
public void testPrintToUnicodeString() throws Exception {
assertEquals(
"optional_string: \"abc\u3042efg\"\n" +
"optional_bytes: \"\\343\\201\\202\"\n" +
@ -772,6 +867,49 @@ public class TextFormatTest extends TestCase {
.setOptionalBytes(bytes(0xe3, 0x81, 0x82))
.addRepeatedString("\u3093XYZ")
.build()));
// Double quotes and backslashes should be escaped
assertEquals(
"optional_string: \"a\\\\bc\\\"ef\\\"g\"\n",
TextFormat.printToUnicodeString(TestAllTypes.newBuilder()
.setOptionalString("a\\bc\"ef\"g")
.build()));
// Test escaping roundtrip
TestAllTypes message = TestAllTypes.newBuilder()
.setOptionalString("a\\bc\\\"ef\"g")
.build();
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
TextFormat.merge(TextFormat.printToUnicodeString(message), builder);
assertEquals(message.getOptionalString(), builder.getOptionalString());
}
public void testPrintToUnicodeStringWithNewlines() {
// No newlines at start and end
assertEquals("optional_string: \"test newlines\n\nin\nstring\"\n",
TextFormat.printToUnicodeString(TestAllTypes.newBuilder()
.setOptionalString("test newlines\n\nin\nstring")
.build()));
// Newlines at start and end
assertEquals("optional_string: \"\ntest\nnewlines\n\nin\nstring\n\"\n",
TextFormat.printToUnicodeString(TestAllTypes.newBuilder()
.setOptionalString("\ntest\nnewlines\n\nin\nstring\n")
.build()));
// Strings with 0, 1 and 2 newlines.
assertEquals("optional_string: \"\"\n",
TextFormat.printToUnicodeString(TestAllTypes.newBuilder()
.setOptionalString("")
.build()));
assertEquals("optional_string: \"\n\"\n",
TextFormat.printToUnicodeString(TestAllTypes.newBuilder()
.setOptionalString("\n")
.build()));
assertEquals("optional_string: \"\n\n\"\n",
TextFormat.printToUnicodeString(TestAllTypes.newBuilder()
.setOptionalString("\n\n")
.build()));
}
public void testPrintToUnicodeString_unknown() {
@ -783,4 +921,177 @@ public class TextFormatTest extends TestCase {
.addLengthDelimited(bytes(0xe3, 0x81, 0x82)).build())
.build()));
}
public void testParseUnknownFields() throws Exception {
assertParseSuccessWithUnknownFields("unknown_field: 12345");
assertParseSuccessWithUnknownFields("unknown_field: -12345");
assertParseSuccessWithUnknownFields("unknown_field: 1.2345");
assertParseSuccessWithUnknownFields("unknown_field: -1.2345");
assertParseSuccessWithUnknownFields("unknown_field: 1.2345f");
assertParseSuccessWithUnknownFields("unknown_field: -1.2345f");
assertParseSuccessWithUnknownFields("unknown_field: inf");
assertParseSuccessWithUnknownFields("unknown_field: -inf");
assertParseSuccessWithUnknownFields("unknown_field: TYPE_STRING");
assertParseSuccessWithUnknownFields("unknown_field: \"string value\"");
// Invalid field value
assertParseErrorWithUnknownFields(
"1:16: Invalid field value: -TYPE_STRING",
"unknown_field: -TYPE_STRING");
// Two or more unknown fields
assertParseSuccessWithUnknownFields("unknown_field1: TYPE_STRING\n" +
"unknown_field2: 12345");
// Unknown nested message
assertParseSuccessWithUnknownFields("unknown_message1: {}\n" +
"unknown_message2 {\n" +
" unknown_field: 12345\n" +
"}\n" +
"unknown_message3 <\n" +
" unknown_nested_message {\n" +
" unknown_field: 12345\n" +
" }\n" +
">");
// Unmatched delimeters for message body
assertParseErrorWithUnknownFields(
"1:19: Expected \"}\".", "unknown_message: {>");
// Unknown extension
assertParseSuccessWithUnknownFields(
"[somewhere.unknown_extension1]: 12345\n" +
"[somewhere.unknown_extension2] {\n" +
" unknown_field: 12345\n" +
"}");
// Unknown fields between known fields.
TestAllTypes expected = TestAllTypes.newBuilder()
.setOptionalInt32(1)
.setOptionalString("string")
.setOptionalNestedMessage(NestedMessage.newBuilder()
.setBb(2))
.build();
assertEquals(expected, assertParseSuccessWithUnknownFields(
"optional_int32: 1\n" +
"unknown_field: 12345\n" +
"optional_string: \"string\"\n" +
"unknown_message { unknown : 0 }\n" +
"optional_nested_message { bb: 2 }"));
// Nested unknown extensions.
assertParseSuccessWithUnknownFields(
"[test.extension1] <\n" +
" unknown_nested_message <\n" +
" [test.extension2] <\n" +
" unknown_field: 12345\n" +
" >\n" +
" >\n" +
">");
assertParseSuccessWithUnknownFields(
"[test.extension1] {\n" +
" unknown_nested_message {\n" +
" [test.extension2] {\n" +
" unknown_field: 12345\n" +
" }\n" +
" }\n" +
"}");
assertParseSuccessWithUnknownFields(
"[test.extension1] <\n" +
" some_unknown_fields: <\n" +
" unknown_field: 12345\n" +
" >\n" +
">");
assertParseSuccessWithUnknownFields(
"[test.extension1] {\n" +
" some_unknown_fields: {\n" +
" unknown_field: 12345\n" +
" }\n" +
"}");
}
public void testParseNonRepeatedFields() throws Exception {
assertParseSuccessWithOverwriteForbidden(
"repeated_int32: 1\n" +
"repeated_int32: 2\n");
assertParseSuccessWithOverwriteForbidden(
"RepeatedGroup { a: 1 }\n" +
"RepeatedGroup { a: 2 }\n");
assertParseSuccessWithOverwriteForbidden(
"repeated_nested_message { bb: 1 }\n" +
"repeated_nested_message { bb: 2 }\n");
assertParseErrorWithOverwriteForbidden(
"3:17: Non-repeated field " +
"\"protobuf_unittest.TestAllTypes.optional_int32\" " +
"cannot be overwritten.",
"optional_int32: 1\n" +
"optional_bool: true\n" +
"optional_int32: 1\n");
assertParseErrorWithOverwriteForbidden(
"2:17: Non-repeated field " +
"\"protobuf_unittest.TestAllTypes.optionalgroup\" " +
"cannot be overwritten.",
"OptionalGroup { a: 1 }\n" +
"OptionalGroup { }\n");
assertParseErrorWithOverwriteForbidden(
"2:33: Non-repeated field " +
"\"protobuf_unittest.TestAllTypes.optional_nested_message\" " +
"cannot be overwritten.",
"optional_nested_message { }\n" +
"optional_nested_message { bb: 3 }\n");
assertParseErrorWithOverwriteForbidden(
"2:16: Non-repeated field " +
"\"protobuf_unittest.TestAllTypes.default_int32\" " +
"cannot be overwritten.",
"default_int32: 41\n" + // the default value
"default_int32: 41\n");
assertParseErrorWithOverwriteForbidden(
"2:17: Non-repeated field " +
"\"protobuf_unittest.TestAllTypes.default_string\" " +
"cannot be overwritten.",
"default_string: \"zxcv\"\n" +
"default_string: \"asdf\"\n");
}
public void testParseShortRepeatedFormOfRepeatedFields() throws Exception {
assertParseSuccessWithOverwriteForbidden("repeated_foreign_enum: [FOREIGN_FOO, FOREIGN_BAR]");
assertParseSuccessWithOverwriteForbidden("repeated_int32: [ 1, 2 ]\n");
assertParseSuccessWithOverwriteForbidden("RepeatedGroup [{ a: 1 },{ a: 2 }]\n");
assertParseSuccessWithOverwriteForbidden("repeated_nested_message [{ bb: 1 }, { bb: 2 }]\n");
}
public void testParseShortRepeatedFormOfNonRepeatedFields() throws Exception {
assertParseErrorWithOverwriteForbidden(
"1:17: Couldn't parse integer: For input string: \"[\"",
"optional_int32: [1]\n");
}
// =======================================================================
// test oneof
public void testOneofTextFormat() throws Exception {
TestOneof2.Builder builder = TestOneof2.newBuilder();
TestUtil.setOneof(builder);
TestOneof2 message = builder.build();
TestOneof2.Builder dest = TestOneof2.newBuilder();
TextFormat.merge(TextFormat.printToUnicodeString(message), dest);
TestUtil.assertOneofSet(dest.build());
}
public void testOneofOverwriteForbidden() throws Exception {
String input = "foo_string: \"stringvalue\" foo_int: 123";
TestOneof2.Builder builder = TestOneof2.newBuilder();
try {
parserWithOverwriteForbidden.merge(
input, TestUtil.getExtensionRegistry(), builder);
fail("Expected parse exception.");
} catch (TextFormat.ParseException e) {
assertEquals("1:36: Field \"protobuf_unittest.TestOneof2.foo_int\""
+ " is specified along with field \"protobuf_unittest.TestOneof2.foo_string\","
+ " another member of oneof \"foo\".", e.getMessage());
}
}
public void testOneofOverwriteAllowed() throws Exception {
String input = "foo_string: \"stringvalue\" foo_int: 123";
TestOneof2.Builder builder = TestOneof2.newBuilder();
defaultParser.merge(input, TestUtil.getExtensionRegistry(), builder);
// Only the last value sticks.
TestOneof2 oneof = builder.build();
assertFalse(oneof.hasFooString());
assertTrue(oneof.hasFooInt());
}
}

@ -31,10 +31,13 @@
package com.google.protobuf;
import protobuf_unittest.UnittestProto;
import protobuf_unittest.UnittestProto.ForeignEnum;
import protobuf_unittest.UnittestProto.TestAllExtensions;
import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestEmptyMessage;
import protobuf_unittest.UnittestProto.TestEmptyMessageWithExtensions;
import protobuf_unittest.UnittestProto.TestPackedExtensions;
import protobuf_unittest.UnittestProto.TestPackedTypes;
import junit.framework.TestCase;
@ -204,6 +207,13 @@ public class UnknownFieldSetTest extends TestCase {
TestEmptyMessage.newBuilder().mergeFrom(emptyMessage).clear().build();
assertEquals(0, message.getSerializedSize());
}
public void testClearField() throws Exception {
int fieldNumber = unknownFields.asMap().keySet().iterator().next();
UnknownFieldSet fields =
UnknownFieldSet.newBuilder().mergeFrom(unknownFields).clearField(fieldNumber).build();
assertFalse(fields.hasField(fieldNumber));
}
public void testParseKnownAndUnknown() throws Exception {
// Test mixing known and unknown fields when parsing.
@ -434,4 +444,210 @@ public class UnknownFieldSetTest extends TestCase {
assertEquals(copy, set);
assertEquals(set.hashCode(), copy.hashCode());
}
// =================================================================
public void testSerializeLite() throws Exception {
UnittestLite.TestEmptyMessageLite emptyMessageLite =
UnittestLite.TestEmptyMessageLite.parseFrom(allFieldsData);
assertEquals(allFieldsData.size(), emptyMessageLite.getSerializedSize());
ByteString data = emptyMessageLite.toByteString();
TestAllTypes message = TestAllTypes.parseFrom(data);
TestUtil.assertAllFieldsSet(message);
assertEquals(allFieldsData, data);
}
public void testAllExtensionsLite() throws Exception {
TestAllExtensions allExtensions = TestUtil.getAllExtensionsSet();
ByteString allExtensionsData = allExtensions.toByteString();
UnittestLite.TestEmptyMessageLite emptyMessageLite =
UnittestLite.TestEmptyMessageLite.PARSER.parseFrom(allExtensionsData);
ByteString data = emptyMessageLite.toByteString();
TestAllExtensions message =
TestAllExtensions.parseFrom(data, TestUtil.getExtensionRegistry());
TestUtil.assertAllExtensionsSet(message);
assertEquals(allExtensionsData, data);
}
public void testAllPackedFieldsLite() throws Exception {
TestPackedTypes allPackedFields = TestUtil.getPackedSet();
ByteString allPackedData = allPackedFields.toByteString();
UnittestLite.TestEmptyMessageLite emptyMessageLite =
UnittestLite.TestEmptyMessageLite.parseFrom(allPackedData);
ByteString data = emptyMessageLite.toByteString();
TestPackedTypes message =
TestPackedTypes.parseFrom(data, TestUtil.getExtensionRegistry());
TestUtil.assertPackedFieldsSet(message);
assertEquals(allPackedData, data);
}
public void testAllPackedExtensionsLite() throws Exception {
TestPackedExtensions allPackedExtensions = TestUtil.getPackedExtensionsSet();
ByteString allPackedExtensionsData = allPackedExtensions.toByteString();
UnittestLite.TestEmptyMessageLite emptyMessageLite =
UnittestLite.TestEmptyMessageLite.parseFrom(allPackedExtensionsData);
ByteString data = emptyMessageLite.toByteString();
TestPackedExtensions message =
TestPackedExtensions.parseFrom(data, TestUtil.getExtensionRegistry());
TestUtil.assertPackedExtensionsSet(message);
assertEquals(allPackedExtensionsData, data);
}
public void testCopyFromLite() throws Exception {
UnittestLite.TestEmptyMessageLite emptyMessageLite =
UnittestLite.TestEmptyMessageLite.parseFrom(allFieldsData);
UnittestLite.TestEmptyMessageLite emptyMessageLite2 =
UnittestLite.TestEmptyMessageLite.newBuilder()
.mergeFrom(emptyMessageLite).build();
assertEquals(emptyMessageLite.toByteString(), emptyMessageLite2.toByteString());
}
public void testMergeFromLite() throws Exception {
TestAllTypes message1 = TestAllTypes.newBuilder()
.setOptionalInt32(1)
.setOptionalString("foo")
.addRepeatedString("bar")
.setOptionalNestedEnum(TestAllTypes.NestedEnum.BAZ)
.build();
TestAllTypes message2 = TestAllTypes.newBuilder()
.setOptionalInt64(2)
.setOptionalString("baz")
.addRepeatedString("qux")
.setOptionalForeignEnum(ForeignEnum.FOREIGN_BAZ)
.build();
ByteString data1 = message1.toByteString();
UnittestLite.TestEmptyMessageLite emptyMessageLite1 =
UnittestLite.TestEmptyMessageLite.parseFrom(data1);
ByteString data2 = message2.toByteString();
UnittestLite.TestEmptyMessageLite emptyMessageLite2 =
UnittestLite.TestEmptyMessageLite.parseFrom(data2);
message1 = TestAllTypes.newBuilder(message1).mergeFrom(message2).build();
emptyMessageLite1 = UnittestLite.TestEmptyMessageLite.newBuilder(emptyMessageLite1)
.mergeFrom(emptyMessageLite2).build();
data1 = emptyMessageLite1.toByteString();
message2 = TestAllTypes.parseFrom(data1);
assertEquals(message1, message2);
}
public void testWrongTypeTreatedAsUnknownLite() throws Exception {
// Test that fields of the wrong wire type are treated like unknown fields
// when parsing.
ByteString bizarroData = getBizarroData();
TestAllTypes allTypesMessage = TestAllTypes.parseFrom(bizarroData);
UnittestLite.TestEmptyMessageLite emptyMessageLite =
UnittestLite.TestEmptyMessageLite.parseFrom(bizarroData);
ByteString data = emptyMessageLite.toByteString();
TestAllTypes allTypesMessage2 = TestAllTypes.parseFrom(data);
assertEquals(allTypesMessage.toString(), allTypesMessage2.toString());
}
public void testUnknownExtensionsLite() throws Exception {
// Make sure fields are properly parsed to the UnknownFieldSet even when
// they are declared as extension numbers.
UnittestLite.TestEmptyMessageWithExtensionsLite message =
UnittestLite.TestEmptyMessageWithExtensionsLite.parseFrom(allFieldsData);
assertEquals(allFieldsData, message.toByteString());
}
public void testWrongExtensionTypeTreatedAsUnknownLite() throws Exception {
// Test that fields of the wrong wire type are treated like unknown fields
// when parsing extensions.
ByteString bizarroData = getBizarroData();
TestAllExtensions allExtensionsMessage =
TestAllExtensions.parseFrom(bizarroData);
UnittestLite.TestEmptyMessageLite emptyMessageLite =
UnittestLite.TestEmptyMessageLite.parseFrom(bizarroData);
// All fields should have been interpreted as unknown, so the byte strings
// should be the same.
assertEquals(emptyMessageLite.toByteString(),
allExtensionsMessage.toByteString());
}
public void testParseUnknownEnumValueLite() throws Exception {
Descriptors.FieldDescriptor singularField =
TestAllTypes.getDescriptor().findFieldByName("optional_nested_enum");
Descriptors.FieldDescriptor repeatedField =
TestAllTypes.getDescriptor().findFieldByName("repeated_nested_enum");
assertNotNull(singularField);
assertNotNull(repeatedField);
ByteString data =
UnknownFieldSet.newBuilder()
.addField(singularField.getNumber(),
UnknownFieldSet.Field.newBuilder()
.addVarint(TestAllTypes.NestedEnum.BAR.getNumber())
.addVarint(5) // not valid
.build())
.addField(repeatedField.getNumber(),
UnknownFieldSet.Field.newBuilder()
.addVarint(TestAllTypes.NestedEnum.FOO.getNumber())
.addVarint(4) // not valid
.addVarint(TestAllTypes.NestedEnum.BAZ.getNumber())
.addVarint(6) // not valid
.build())
.build()
.toByteString();
UnittestLite.TestEmptyMessageLite emptyMessageLite =
UnittestLite.TestEmptyMessageLite.parseFrom(data);
data = emptyMessageLite.toByteString();
{
TestAllTypes message = TestAllTypes.parseFrom(data);
assertEquals(TestAllTypes.NestedEnum.BAR,
message.getOptionalNestedEnum());
assertEquals(
Arrays.asList(TestAllTypes.NestedEnum.FOO, TestAllTypes.NestedEnum.BAZ),
message.getRepeatedNestedEnumList());
assertEquals(Arrays.asList(5L),
message.getUnknownFields()
.getField(singularField.getNumber())
.getVarintList());
assertEquals(Arrays.asList(4L, 6L),
message.getUnknownFields()
.getField(repeatedField.getNumber())
.getVarintList());
}
{
TestAllExtensions message =
TestAllExtensions.parseFrom(data, TestUtil.getExtensionRegistry());
assertEquals(TestAllTypes.NestedEnum.BAR,
message.getExtension(UnittestProto.optionalNestedEnumExtension));
assertEquals(
Arrays.asList(TestAllTypes.NestedEnum.FOO, TestAllTypes.NestedEnum.BAZ),
message.getExtension(UnittestProto.repeatedNestedEnumExtension));
assertEquals(Arrays.asList(5L),
message.getUnknownFields()
.getField(singularField.getNumber())
.getVarintList());
assertEquals(Arrays.asList(4L, 6L),
message.getUnknownFields()
.getField(repeatedField.getNumber())
.getVarintList());
}
}
public void testClearLite() throws Exception {
UnittestLite.TestEmptyMessageLite emptyMessageLite1 =
UnittestLite.TestEmptyMessageLite.parseFrom(allFieldsData);
UnittestLite.TestEmptyMessageLite emptyMessageLite2 =
UnittestLite.TestEmptyMessageLite.newBuilder()
.mergeFrom(emptyMessageLite1).clear().build();
assertEquals(0, emptyMessageLite2.getSerializedSize());
ByteString data = emptyMessageLite2.toByteString();
assertEquals(0, data.size());
}
}

@ -33,6 +33,7 @@ package com.google.protobuf;
import junit.framework.TestCase;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/**
@ -60,6 +61,11 @@ public class UnmodifiableLazyStringListTest extends TestCase {
assertEquals(BYTE_STRING_A, list.getByteString(0));
assertEquals(BYTE_STRING_B, list.getByteString(1));
assertEquals(BYTE_STRING_C, list.getByteString(2));
List<ByteString> byteStringList = list.asByteStringList();
assertSame(list.getByteString(0), byteStringList.get(0));
assertSame(list.getByteString(1), byteStringList.get(1));
assertSame(list.getByteString(2), byteStringList.get(2));
}
public void testModifyMethods() {
@ -88,6 +94,35 @@ public class UnmodifiableLazyStringListTest extends TestCase {
} catch (UnsupportedOperationException e) {
// expected
}
assertEquals(3, list.size());
List<ByteString> byteStringList = list.asByteStringList();
try {
byteStringList.remove(0);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
assertEquals(3, list.size());
assertEquals(3, byteStringList.size());
try {
byteStringList.add(BYTE_STRING_B);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
assertEquals(3, list.size());
assertEquals(3, byteStringList.size());
try {
byteStringList.set(1, BYTE_STRING_B);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
assertEquals(3, list.size());
assertEquals(3, byteStringList.size());
}
public void testIterator() {
@ -108,6 +143,20 @@ public class UnmodifiableLazyStringListTest extends TestCase {
}
assertEquals(3, count);
List<ByteString> byteStringList = list.asByteStringList();
Iterator<ByteString> byteIter = byteStringList.iterator();
count = 0;
while (byteIter.hasNext()) {
byteIter.next();
count++;
try {
byteIter.remove();
fail();
} catch (UnsupportedOperationException e) {
// expected
}
}
assertEquals(3, count);
}
public void testListIterator() {
@ -140,6 +189,32 @@ public class UnmodifiableLazyStringListTest extends TestCase {
}
assertEquals(3, count);
List<ByteString> byteStringList = list.asByteStringList();
ListIterator<ByteString> byteIter = byteStringList.listIterator();
count = 0;
while (byteIter.hasNext()) {
byteIter.next();
count++;
try {
byteIter.remove();
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
byteIter.set(BYTE_STRING_A);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
byteIter.add(BYTE_STRING_A);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
}
assertEquals(3, count);
}
private LazyStringArrayList createSampleList() {

@ -40,6 +40,8 @@ import protobuf_unittest.UnittestProto;
import protobuf_unittest.UnittestProto.TestAllExtensions;
import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestFieldOrderings;
import protobuf_unittest.UnittestProto.TestOneof2;
import protobuf_unittest.UnittestProto.TestOneofBackwardsCompatible;
import protobuf_unittest.UnittestProto.TestPackedExtensions;
import protobuf_unittest.UnittestProto.TestPackedTypes;
import protobuf_unittest.UnittestMset.TestMessageSet;
@ -218,8 +220,8 @@ public class WireFormatTest extends TestCase {
}
public void testExtensionsSerializedSize() throws Exception {
assertEquals(TestUtil.getAllSet().getSerializedSize(),
TestUtil.getAllExtensionsSet().getSerializedSize());
assertNotSame(TestUtil.getAllSet().getSerializedSize(),
TestUtil.getAllExtensionsSet().getSerializedSize());
}
public void testSerializeDelimited() throws Exception {
@ -577,4 +579,28 @@ public class WireFormatTest extends TestCase {
assertEquals(123, messageSet.getExtension(
TestMessageSetExtension1.messageSetExtension).getI());
}
// ================================================================
// oneof
public void testOneofWireFormat() throws Exception {
TestOneof2.Builder builder = TestOneof2.newBuilder();
TestUtil.setOneof(builder);
TestOneof2 message = builder.build();
ByteString rawBytes = message.toByteString();
assertEquals(rawBytes.size(), message.getSerializedSize());
TestOneof2 message2 = TestOneof2.parseFrom(rawBytes);
TestUtil.assertOneofSet(message2);
}
public void testOneofOnlyLastSet() throws Exception {
TestOneofBackwardsCompatible source = TestOneofBackwardsCompatible
.newBuilder().setFooInt(100).setFooString("101").build();
ByteString rawBytes = source.toByteString();
TestOneof2 message = TestOneof2.parseFrom(rawBytes);
assertFalse(message.hasFooInt());
assertTrue(message.hasFooString());
}
}

@ -0,0 +1,61 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// 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.
// Author: Naoki Iwasaki (niwasaki@google.com)
//
// A proto file with lazy fields
package protobuf_unittest;
option optimize_for = LITE_RUNTIME;
message LazyMessageLite {
optional int32 num = 1;
optional int32 num_with_default = 2 [default = 421];
optional LazyInnerMessageLite inner = 3 [lazy = true];
repeated LazyInnerMessageLite repeated_inner = 4 [lazy = true];
oneof oneof_field {
int32 oneof_num = 5;
LazyInnerMessageLite oneof_inner = 6 [lazy = true];
}
}
message LazyInnerMessageLite {
optional int32 num = 1;
optional int32 num_with_default = 2 [default = 42];
optional LazyNestedInnerMessageLite nested = 3 [lazy = true];
}
message LazyNestedInnerMessageLite {
optional int32 num = 1;
optional int32 num_with_default = 2 [default = 4];
}

@ -28,36 +28,28 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: qrczak@google.com (Marcin Kowalczyk)
// Author: pbogle@google.com (Phil Bogle)
#include <google/protobuf/pyext/python_protobuf.h>
namespace google {
namespace protobuf {
namespace python {
package protobuf_unittest.lite_equals_and_hash;
static const Message* GetCProtoInsidePyProtoStub(PyObject* msg) {
return NULL;
}
static Message* MutableCProtoInsidePyProtoStub(PyObject* msg) {
return NULL;
}
// This proto definition is used to test that java_generate_equals_and_hash
// works correctly with the LITE_RUNTIME.
option java_generate_equals_and_hash = true;
option optimize_for = LITE_RUNTIME;
// This is initialized with a default, stub implementation.
// If python-google.protobuf.cc is loaded, the function pointer is overridden
// with a full implementation.
const Message* (*GetCProtoInsidePyProtoPtr)(PyObject* msg) =
GetCProtoInsidePyProtoStub;
Message* (*MutableCProtoInsidePyProtoPtr)(PyObject* msg) =
MutableCProtoInsidePyProtoStub;
message Foo {
optional int32 value = 1;
repeated Bar bar = 2;
}
const Message* GetCProtoInsidePyProto(PyObject* msg) {
return GetCProtoInsidePyProtoPtr(msg);
message Bar {
optional string name = 1;
}
Message* MutableCProtoInsidePyProto(PyObject* msg) {
return MutableCProtoInsidePyProtoPtr(msg);
message BarPrime {
optional string name = 1;
}
} // namespace python
} // namespace protobuf
} // namespace google
message Empty {
}

@ -38,12 +38,14 @@
option java_generic_services = true; // auto-added
import "google/protobuf/unittest.proto";
import "google/protobuf/descriptor.proto";
package protobuf_unittest;
option java_multiple_files = true;
option java_outer_classname = "MultipleFilesTestProto";
message MessageWithNoOuter {
message NestedMessage {
optional int32 i = 1;
@ -57,8 +59,12 @@ message MessageWithNoOuter {
optional EnumWithNoOuter foreign_enum = 4;
}
extend google.protobuf.EnumValueOptions {
optional int32 enum_value_option = 7654321;
}
enum EnumWithNoOuter {
FOO = 1;
FOO = 1 [(enum_value_option) = 12345];
BAR = 2;
}

@ -38,6 +38,7 @@ import "com/google/protobuf/non_nested_extension.proto";
package protobuf_unittest;
message MyNestedExtension {
extend MessageToBeExtended {
optional MessageToBeExtended recursiveExtension = 2;

@ -35,6 +35,7 @@
package protobuf_unittest;
message MessageToBeExtended {
extensions 1 to max;
}

@ -0,0 +1,38 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// 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 protobuf_unittest;
// This message's name is the same with the default outer class name of this
// proto file. It's used to test if the compiler can avoid this conflict
// correctly.
message OuterClassNameTest {
}

@ -0,0 +1,42 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// 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 protobuf_unittest;
message TestMessage2 {
message NestedMessage {
// This message's name is the same with the default outer class name of this
// proto file. It's used to test if the compiler can avoid this conflict
// correctly.
message OuterClassNameTest2 {
}
}
}

@ -0,0 +1,43 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// 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 protobuf_unittest;
message TestMessage3 {
message NestedMessage {
// This enum's name is the same with the default outer class name of this
// proto file. It's used to test if the compiler can avoid this conflict
// correctly.
enum OuterClassNameTest3 {
DUMMY_VALUE = 1;
}
}
}

@ -42,8 +42,12 @@ package io_protocol_tests;
option java_package = "com.google.protobuf";
option java_outer_classname = "TestBadIdentifiersProto";
option java_generate_equals_and_hash = true;
message TestMessage {
optional string cached_size = 1;
optional string serialized_size = 2;
optional string class = 3;
}
message Descriptor {
@ -54,6 +58,9 @@ message Descriptor {
optional string descriptor = 1;
}
optional NestedDescriptor nested_descriptor = 2;
enum NestedEnum {
FOO = 1;
}
}
message Parser {
@ -66,6 +73,9 @@ message Parser {
message Deprecated {
enum TestEnum {
FOO = 1;
// Test if @Deprecated annotation conflicts with Deprecated message name.
BAR = 2 [ deprecated = true ];
}
optional int32 field1 = 1 [deprecated=true];
@ -106,3 +116,42 @@ service TestConflictingMethodNames {
rpc Override(TestMessage) returns (TestMessage);
}
message TestConflictingFieldNames {
enum TestEnum {
FOO = 1;
}
message TestMessage {
}
repeated int32 int32_field = 1;
repeated TestEnum enum_field = 2;
repeated string string_field = 3;
repeated bytes bytes_field = 4;
repeated TestMessage message_field = 5;
optional int32 int32_field_count = 11;
optional TestEnum enum_field_count = 12;
optional string string_field_count = 13;
optional bytes bytes_field_count = 14;
optional TestMessage message_field_count = 15;
repeated int32 Int32Field = 21;
repeated TestEnum EnumField = 22;
repeated string StringField = 23;
repeated bytes BytesField = 24;
repeated TestMessage MessageField = 25;
// This field conflicts with "int32_field" as they both generate
// the method getInt32FieldList().
required int32 int32_field_list = 31;
extensions 1000 to max;
repeated int64 int64_field = 41;
extend TestConflictingFieldNames {
// 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;
}
}

@ -0,0 +1,50 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// 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.
// Author: Jacob Butcher (jbaum@google.com)
//
// Test file option java_string_check_utf8.
package proto2_test_check_utf8;
option java_outer_classname = "TestCheckUtf8";
option java_string_check_utf8 = true;
message StringWrapper {
required string req = 1;
optional string opt = 2;
repeated string rep = 3;
}
message BytesWrapper {
required bytes req = 1;
optional bytes opt = 2;
repeated bytes rep = 3;
}

@ -0,0 +1,51 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// 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.
// Author: Jacob Butcher (jbaum@google.com)
//
// Test file option java_string_check_utf8.
package proto2_test_check_utf8_size;
option java_outer_classname = "TestCheckUtf8Size";
option java_string_check_utf8 = true;
option optimize_for = CODE_SIZE;
message StringWrapperSize {
required string req = 1;
optional string opt = 2;
repeated string rep = 3;
}
message BytesWrapperSize {
required bytes req = 1;
optional bytes opt = 2;
repeated bytes rep = 3;
}

@ -0,0 +1,43 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// 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.
// Author: Feng Xiao (xiaofeng@google.com)
//
// Test that custom options defined in a proto file's dependencies are properly
// initialized.
package protobuf_unittest;
import "google/protobuf/unittest_custom_options.proto";
message TestMessageWithCustomOptionsContainer {
optional TestMessageWithCustomOptions field = 1;
}

@ -28,60 +28,33 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: petar@google.com (Petar Petrov)
// Author: Darick Tong (darick@google.com)
#ifndef GOOGLE_PROTOBUF_PYTHON_DESCRIPTOR_H__
#define GOOGLE_PROTOBUF_PYTHON_DESCRIPTOR_H__
package protobuf_unittest;
#include <Python.h>
#include <structmember.h>
message Proto1 {
option experimental_java_message_interface =
"com.google.protobuf.ExtraInterfaces.HasBoolValue";
#include <google/protobuf/descriptor.h>
option experimental_java_interface_extends =
"com.google.protobuf.ExtraInterfaces.HasByteValue";
#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
typedef int Py_ssize_t;
#define PY_SSIZE_T_MAX INT_MAX
#define PY_SSIZE_T_MIN INT_MIN
#endif
option experimental_java_message_interface =
"com.google.protobuf.ExtraInterfaces.HasStringValue<Proto1>";
namespace google {
namespace protobuf {
namespace python {
option experimental_java_builder_interface =
"com.google.protobuf.ExtraInterfaces.HasStringValueBuilder"
"<Proto1, Builder>";
typedef struct {
PyObject_HEAD
optional string string_value = 1;
optional bool bool_value = 2;
optional bytes byte_value = 3;
optional int32 int_value = 4;
}
// The proto2 descriptor that this object represents.
const google::protobuf::FieldDescriptor* descriptor;
message Proto2 {
option experimental_java_message_interface =
"com.google.protobuf.ExtraInterfaces.HasBoolValue";
// Full name of the field (PyString).
PyObject* full_name;
// Name of the field (PyString).
PyObject* name;
// C++ type of the field (PyLong).
PyObject* cpp_type;
// Name of the field (PyLong).
PyObject* label;
// Identity of the descriptor (PyLong used as a poiner).
PyObject* id;
} CFieldDescriptor;
extern PyTypeObject CFieldDescriptor_Type;
extern PyTypeObject CDescriptorPool_Type;
PyObject* Python_NewCDescriptorPool(PyObject* ignored, PyObject* args);
PyObject* Python_BuildFile(PyObject* ignored, PyObject* args);
bool InitDescriptor();
google::protobuf::DescriptorPool* GetDescriptorPool();
} // namespace python
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_PYTHON_DESCRIPTOR_H__
optional bool bool_value = 1;
}

File diff suppressed because it is too large Load Diff

@ -1,337 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// 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.
// Author: petar@google.com (Petar Petrov)
#include <Python.h>
#include <string>
#include <google/protobuf/pyext/python_descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#define C(str) const_cast<char*>(str)
namespace google {
namespace protobuf {
namespace python {
static void CFieldDescriptorDealloc(CFieldDescriptor* self);
static google::protobuf::DescriptorPool* g_descriptor_pool = NULL;
static PyObject* CFieldDescriptor_GetFullName(
CFieldDescriptor* self, void *closure) {
Py_XINCREF(self->full_name);
return self->full_name;
}
static PyObject* CFieldDescriptor_GetName(
CFieldDescriptor *self, void *closure) {
Py_XINCREF(self->name);
return self->name;
}
static PyObject* CFieldDescriptor_GetCppType(
CFieldDescriptor *self, void *closure) {
Py_XINCREF(self->cpp_type);
return self->cpp_type;
}
static PyObject* CFieldDescriptor_GetLabel(
CFieldDescriptor *self, void *closure) {
Py_XINCREF(self->label);
return self->label;
}
static PyObject* CFieldDescriptor_GetID(
CFieldDescriptor *self, void *closure) {
Py_XINCREF(self->id);
return self->id;
}
static PyGetSetDef CFieldDescriptorGetters[] = {
{ C("full_name"),
(getter)CFieldDescriptor_GetFullName, NULL, "Full name", NULL},
{ C("name"),
(getter)CFieldDescriptor_GetName, NULL, "last name", NULL},
{ C("cpp_type"),
(getter)CFieldDescriptor_GetCppType, NULL, "C++ Type", NULL},
{ C("label"),
(getter)CFieldDescriptor_GetLabel, NULL, "Label", NULL},
{ C("id"),
(getter)CFieldDescriptor_GetID, NULL, "ID", NULL},
{NULL}
};
PyTypeObject CFieldDescriptor_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
C("google.protobuf.internal."
"_net_proto2___python."
"CFieldDescriptor"), // tp_name
sizeof(CFieldDescriptor), // tp_basicsize
0, // tp_itemsize
(destructor)CFieldDescriptorDealloc, // tp_dealloc
0, // tp_print
0, // tp_getattr
0, // tp_setattr
0, // tp_compare
0, // tp_repr
0, // tp_as_number
0, // tp_as_sequence
0, // tp_as_mapping
0, // tp_hash
0, // tp_call
0, // tp_str
0, // tp_getattro
0, // tp_setattro
0, // tp_as_buffer
Py_TPFLAGS_DEFAULT, // tp_flags
C("A Field Descriptor"), // tp_doc
0, // tp_traverse
0, // tp_clear
0, // tp_richcompare
0, // tp_weaklistoffset
0, // tp_iter
0, // tp_iternext
0, // tp_methods
0, // tp_members
CFieldDescriptorGetters, // tp_getset
0, // tp_base
0, // tp_dict
0, // tp_descr_get
0, // tp_descr_set
0, // tp_dictoffset
0, // tp_init
PyType_GenericAlloc, // tp_alloc
PyType_GenericNew, // tp_new
PyObject_Del, // tp_free
};
static void CFieldDescriptorDealloc(CFieldDescriptor* self) {
Py_DECREF(self->full_name);
Py_DECREF(self->name);
Py_DECREF(self->cpp_type);
Py_DECREF(self->label);
Py_DECREF(self->id);
self->ob_type->tp_free(reinterpret_cast<PyObject*>(self));
}
typedef struct {
PyObject_HEAD
const google::protobuf::DescriptorPool* pool;
} CDescriptorPool;
static void CDescriptorPoolDealloc(CDescriptorPool* self);
static PyObject* CDescriptorPool_NewCDescriptor(
const google::protobuf::FieldDescriptor* field_descriptor) {
CFieldDescriptor* cfield_descriptor = PyObject_New(
CFieldDescriptor, &CFieldDescriptor_Type);
if (cfield_descriptor == NULL) {
return NULL;
}
cfield_descriptor->descriptor = field_descriptor;
cfield_descriptor->full_name = PyString_FromString(
field_descriptor->full_name().c_str());
cfield_descriptor->name = PyString_FromString(
field_descriptor->name().c_str());
cfield_descriptor->cpp_type = PyLong_FromLong(field_descriptor->cpp_type());
cfield_descriptor->label = PyLong_FromLong(field_descriptor->label());
cfield_descriptor->id = PyLong_FromVoidPtr(cfield_descriptor);
return reinterpret_cast<PyObject*>(cfield_descriptor);
}
static PyObject* CDescriptorPool_FindFieldByName(
CDescriptorPool* self, PyObject* arg) {
const char* full_field_name = PyString_AsString(arg);
if (full_field_name == NULL) {
return NULL;
}
const google::protobuf::FieldDescriptor* field_descriptor = NULL;
field_descriptor = self->pool->FindFieldByName(full_field_name);
if (field_descriptor == NULL) {
PyErr_Format(PyExc_TypeError, "Couldn't find field %.200s",
full_field_name);
return NULL;
}
return CDescriptorPool_NewCDescriptor(field_descriptor);
}
static PyObject* CDescriptorPool_FindExtensionByName(
CDescriptorPool* self, PyObject* arg) {
const char* full_field_name = PyString_AsString(arg);
if (full_field_name == NULL) {
return NULL;
}
const google::protobuf::FieldDescriptor* field_descriptor =
self->pool->FindExtensionByName(full_field_name);
if (field_descriptor == NULL) {
PyErr_Format(PyExc_TypeError, "Couldn't find field %.200s",
full_field_name);
return NULL;
}
return CDescriptorPool_NewCDescriptor(field_descriptor);
}
static PyMethodDef CDescriptorPoolMethods[] = {
{ C("FindFieldByName"),
(PyCFunction)CDescriptorPool_FindFieldByName,
METH_O,
C("Searches for a field descriptor by full name.") },
{ C("FindExtensionByName"),
(PyCFunction)CDescriptorPool_FindExtensionByName,
METH_O,
C("Searches for extension descriptor by full name.") },
{NULL}
};
PyTypeObject CDescriptorPool_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
C("google.protobuf.internal."
"_net_proto2___python."
"CFieldDescriptor"), // tp_name
sizeof(CDescriptorPool), // tp_basicsize
0, // tp_itemsize
(destructor)CDescriptorPoolDealloc, // tp_dealloc
0, // tp_print
0, // tp_getattr
0, // tp_setattr
0, // tp_compare
0, // tp_repr
0, // tp_as_number
0, // tp_as_sequence
0, // tp_as_mapping
0, // tp_hash
0, // tp_call
0, // tp_str
0, // tp_getattro
0, // tp_setattro
0, // tp_as_buffer
Py_TPFLAGS_DEFAULT, // tp_flags
C("A Descriptor Pool"), // tp_doc
0, // tp_traverse
0, // tp_clear
0, // tp_richcompare
0, // tp_weaklistoffset
0, // tp_iter
0, // tp_iternext
CDescriptorPoolMethods, // tp_methods
0, // tp_members
0, // tp_getset
0, // tp_base
0, // tp_dict
0, // tp_descr_get
0, // tp_descr_set
0, // tp_dictoffset
0, // tp_init
PyType_GenericAlloc, // tp_alloc
PyType_GenericNew, // tp_new
PyObject_Del, // tp_free
};
static void CDescriptorPoolDealloc(CDescriptorPool* self) {
self->ob_type->tp_free(reinterpret_cast<PyObject*>(self));
}
google::protobuf::DescriptorPool* GetDescriptorPool() {
if (g_descriptor_pool == NULL) {
g_descriptor_pool = new google::protobuf::DescriptorPool(
google::protobuf::DescriptorPool::generated_pool());
}
return g_descriptor_pool;
}
PyObject* Python_NewCDescriptorPool(PyObject* ignored, PyObject* args) {
CDescriptorPool* cdescriptor_pool = PyObject_New(
CDescriptorPool, &CDescriptorPool_Type);
if (cdescriptor_pool == NULL) {
return NULL;
}
cdescriptor_pool->pool = GetDescriptorPool();
return reinterpret_cast<PyObject*>(cdescriptor_pool);
}
PyObject* Python_BuildFile(PyObject* ignored, PyObject* arg) {
char* message_type;
Py_ssize_t message_len;
if (PyString_AsStringAndSize(arg, &message_type, &message_len) < 0) {
return NULL;
}
google::protobuf::FileDescriptorProto file_proto;
if (!file_proto.ParseFromArray(message_type, message_len)) {
PyErr_SetString(PyExc_TypeError, "Couldn't parse file content!");
return NULL;
}
if (google::protobuf::DescriptorPool::generated_pool()->FindFileByName(
file_proto.name()) != NULL) {
Py_RETURN_NONE;
}
const google::protobuf::FileDescriptor* descriptor = GetDescriptorPool()->BuildFile(
file_proto);
if (descriptor == NULL) {
PyErr_SetString(PyExc_TypeError,
"Couldn't build proto file into descriptor pool!");
return NULL;
}
Py_RETURN_NONE;
}
bool InitDescriptor() {
CFieldDescriptor_Type.tp_new = PyType_GenericNew;
if (PyType_Ready(&CFieldDescriptor_Type) < 0)
return false;
CDescriptorPool_Type.tp_new = PyType_GenericNew;
if (PyType_Ready(&CDescriptorPool_Type) < 0)
return false;
return true;
}
} // namespace python
} // namespace protobuf
} // namespace google

@ -72,7 +72,11 @@ def GenerateUnittestProtos():
generate_proto("../src/google/protobuf/unittest_import_public.proto")
generate_proto("../src/google/protobuf/unittest_mset.proto")
generate_proto("../src/google/protobuf/unittest_no_generic_services.proto")
generate_proto("google/protobuf/internal/compatibility_mode_test.proto")
generate_proto("google/protobuf/internal/descriptor_pool_test1.proto")
generate_proto("google/protobuf/internal/descriptor_pool_test2.proto")
generate_proto("google/protobuf/internal/test_bad_identifiers.proto")
generate_proto("google/protobuf/internal/missing_enum_values.proto")
generate_proto("google/protobuf/internal/more_extensions.proto")
generate_proto("google/protobuf/internal/more_extensions_dynamic.proto")
generate_proto("google/protobuf/internal/more_messages.proto")
@ -102,6 +106,22 @@ def MakeTestSuite():
import google.protobuf.internal.message_cpp_test as message_cpp_test
import google.protobuf.internal.reflection_cpp_generated_test \
as reflection_cpp_generated_test
import google.protobuf.internal.api_implementation_default_test \
as api_implementation_default_test
import google.protobuf.internal.descriptor_cpp2_test as descriptor_cpp2_test
import google.protobuf.internal.descriptor_python_test \
as descriptor_python_test
import google.protobuf.internal.message_factory_cpp2_test \
as message_factory_cpp2_test
import google.protobuf.internal.message_factory_cpp_test \
as message_factory_cpp_test
import google.protobuf.internal.message_factory_python_test \
as message_factory_python_test
import google.protobuf.internal.message_python_test as message_python_test
import google.protobuf.internal.reflection_cpp2_generated_test \
as reflection_cpp2_generated_test
import google.protobuf.internal.symbol_database_test as symbol_database_test
import google.protobuf.internal.text_encoding_test as text_encoding_test
loader = unittest.defaultTestLoader
suite = unittest.TestSuite()
@ -110,7 +130,23 @@ def MakeTestSuite():
reflection_test,
service_reflection_test,
text_format_test,
wire_format_test ]:
wire_format_test,
unknown_fields_test,
descriptor_pool_test,
message_factory_test,
message_cpp_test,
reflection_cpp_generated_test,
api_implementation_default_test,
descriptor_cpp2_test,
descriptor_python_test,
message_factory_cpp2_test,
message_factory_cpp_test,
message_factory_python_test,
message_python_test,
reflection_cpp2_generated_test,
symbol_database_test,
text_encoding_test ]:
suite.addTest(loader.loadTestsFromModule(test))
return suite

@ -40,11 +40,13 @@ MAINTAINERCLEANFILES = \
nobase_include_HEADERS = \
google/protobuf/stubs/atomicops.h \
google/protobuf/stubs/atomicops_internals_arm_gcc.h \
google/protobuf/stubs/atomicops_internals_arm64_gcc.h \
google/protobuf/stubs/atomicops_internals_arm_qnx.h \
google/protobuf/stubs/atomicops_internals_atomicword_compat.h \
google/protobuf/stubs/atomicops_internals_macosx.h \
google/protobuf/stubs/atomicops_internals_mips_gcc.h \
google/protobuf/stubs/atomicops_internals_pnacl.h \
google/protobuf/stubs/atomicops_internals_tsan.h \
google/protobuf/stubs/atomicops_internals_x86_gcc.h \
google/protobuf/stubs/atomicops_internals_x86_msvc.h \
google/protobuf/stubs/common.h \
@ -73,6 +75,7 @@ nobase_include_HEADERS = \
google/protobuf/io/coded_stream.h \
$(GZHEADERS) \
google/protobuf/io/printer.h \
google/protobuf/io/strtod.h \
google/protobuf/io/tokenizer.h \
google/protobuf/io/zero_copy_stream.h \
google/protobuf/io/zero_copy_stream_impl.h \
@ -97,7 +100,7 @@ libprotobuf_lite_la_SOURCES = \
google/protobuf/stubs/common.cc \
google/protobuf/stubs/once.cc \
google/protobuf/stubs/hash.h \
google/protobuf/stubs/map-util.h \
google/protobuf/stubs/map_util.h \
google/protobuf/stubs/stl_util.h \
google/protobuf/stubs/stringprintf.cc \
google/protobuf/stubs/stringprintf.h \
@ -134,6 +137,7 @@ libprotobuf_la_SOURCES = \
google/protobuf/wire_format.cc \
google/protobuf/io/gzip_stream.cc \
google/protobuf/io/printer.cc \
google/protobuf/io/strtod.cc \
google/protobuf/io/tokenizer.cc \
google/protobuf/io/zero_copy_stream_impl.cc \
google/protobuf/compiler/importer.cc \
@ -174,6 +178,8 @@ libprotoc_la_SOURCES = \
google/protobuf/compiler/cpp/cpp_service.h \
google/protobuf/compiler/cpp/cpp_string_field.cc \
google/protobuf/compiler/cpp/cpp_string_field.h \
google/protobuf/compiler/java/java_context.cc \
google/protobuf/compiler/java/java_context.h \
google/protobuf/compiler/java/java_enum.cc \
google/protobuf/compiler/java/java_enum.h \
google/protobuf/compiler/java/java_enum_field.cc \
@ -185,14 +191,22 @@ libprotoc_la_SOURCES = \
google/protobuf/compiler/java/java_file.cc \
google/protobuf/compiler/java/java_file.h \
google/protobuf/compiler/java/java_generator.cc \
google/protobuf/compiler/java/java_generator_factory.cc \
google/protobuf/compiler/java/java_generator_factory.h \
google/protobuf/compiler/java/java_helpers.cc \
google/protobuf/compiler/java/java_helpers.h \
google/protobuf/compiler/java/java_lazy_message_field.cc \
google/protobuf/compiler/java/java_lazy_message_field.h \
google/protobuf/compiler/java/java_message.cc \
google/protobuf/compiler/java/java_message.h \
google/protobuf/compiler/java/java_message_field.cc \
google/protobuf/compiler/java/java_message_field.h \
google/protobuf/compiler/java/java_name_resolver.cc \
google/protobuf/compiler/java/java_name_resolver.h \
google/protobuf/compiler/java/java_primitive_field.cc \
google/protobuf/compiler/java/java_primitive_field.h \
google/protobuf/compiler/java/java_shared_code_generator.cc \
google/protobuf/compiler/java/java_shared_code_generator.h \
google/protobuf/compiler/java/java_service.cc \
google/protobuf/compiler/java/java_service.h \
google/protobuf/compiler/java/java_string_field.cc \
@ -229,9 +243,10 @@ EXTRA_DIST = \
google/protobuf/io/gzip_stream.h \
google/protobuf/io/gzip_stream_unittest.sh \
google/protobuf/testdata/golden_message \
google/protobuf/testdata/golden_message_oneof_implemented \
google/protobuf/testdata/golden_packed_fields_message \
google/protobuf/testdata/text_format_unittest_data.txt \
google/protobuf/testdata/text_format_unittest_extensions_data.txt \
google/protobuf/testdata/text_format_unittest_data_oneof_implemented.txt \
google/protobuf/testdata/text_format_unittest_extensions_data.txt \
google/protobuf/package_info.h \
google/protobuf/io/package_info.h \
google/protobuf/compiler/package_info.h \
@ -352,7 +367,8 @@ nodist_protobuf_test_SOURCES = $(protoc_outputs)
# Run cpp_unittest again with PROTOBUF_TEST_NO_DESCRIPTORS defined.
protobuf_lazy_descriptor_test_LDADD = $(PTHREAD_LIBS) libprotobuf.la \
$(top_builddir)/gtest/lib/libgtest.la \
libprotoc.la \
$(top_builddir)/gtest/lib/libgtest.la \
$(top_builddir)/gtest/lib/libgtest_main.la
protobuf_lazy_descriptor_test_CPPFLAGS = -I$(top_srcdir)/gtest/include \
-I$(top_builddir)/gtest/include \

@ -44,6 +44,11 @@ namespace compiler {
CodeGenerator::~CodeGenerator() {}
GeneratorContext::~GeneratorContext() {}
io::ZeroCopyOutputStream*
GeneratorContext::OpenForAppend(const string& filename) {
return NULL;
}
io::ZeroCopyOutputStream* GeneratorContext::OpenForInsert(
const string& filename, const string& insertion_point) {
GOOGLE_LOG(FATAL) << "This GeneratorContext does not support insertion.";
@ -58,8 +63,7 @@ void GeneratorContext::ListParsedFiles(
// Parses a set of comma-delimited name/value pairs.
void ParseGeneratorParameter(const string& text,
vector<pair<string, string> >* output) {
vector<string> parts;
SplitStringUsing(text, ",", &parts);
vector<string> parts = Split(text, ",", true);
for (int i = 0; i < parts.size(); i++) {
string::size_type equals_pos = parts[i].find_first_of('=');

@ -104,6 +104,9 @@ class LIBPROTOC_EXPORT GeneratorContext {
// contain "." or ".." components.
virtual io::ZeroCopyOutputStream* Open(const string& filename) = 0;
// Similar to Open() but the output will be appended to the file if exists
virtual io::ZeroCopyOutputStream* OpenForAppend(const string& filename);
// Creates a ZeroCopyOutputStream which will insert code into the given file
// at the given insertion point. See plugin.proto (plugin.pb.h) for more
// information on insertion points. The default implementation

@ -49,8 +49,10 @@
#include <ctype.h>
#include <google/protobuf/stubs/hash.h>
#include <memory>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/stringprintf.h>
#include <google/protobuf/compiler/importer.h>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/plugin.pb.h>
@ -64,7 +66,7 @@
#include <google/protobuf/io/printer.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/substitute.h>
#include <google/protobuf/stubs/map-util.h>
#include <google/protobuf/stubs/map_util.h>
#include <google/protobuf/stubs/stl_util.h>
@ -160,8 +162,7 @@ bool VerifyDirectoryExists(const string& path) {
// directories listed in |filename|.
bool TryCreateParentDirectory(const string& prefix, const string& filename) {
// Recursively create parent directories to the output file.
vector<string> parts;
SplitStringUsing(filename, "/", &parts);
vector<string> parts = Split(filename, "/", true);
string path_so_far = prefix;
for (int i = 0; i < parts.size() - 1; i++) {
path_so_far += parts[i];
@ -252,6 +253,7 @@ class CommandLineInterface::GeneratorContextImpl : public GeneratorContext {
// implements GeneratorContext --------------------------------------
io::ZeroCopyOutputStream* Open(const string& filename);
io::ZeroCopyOutputStream* OpenForAppend(const string& filename);
io::ZeroCopyOutputStream* OpenForInsert(
const string& filename, const string& insertion_point);
void ListParsedFiles(vector<const FileDescriptor*>* output) {
@ -271,7 +273,8 @@ class CommandLineInterface::GeneratorContextImpl : public GeneratorContext {
class CommandLineInterface::MemoryOutputStream
: public io::ZeroCopyOutputStream {
public:
MemoryOutputStream(GeneratorContextImpl* directory, const string& filename);
MemoryOutputStream(GeneratorContextImpl* directory, const string& filename,
bool append_mode);
MemoryOutputStream(GeneratorContextImpl* directory, const string& filename,
const string& insertion_point);
virtual ~MemoryOutputStream();
@ -290,6 +293,9 @@ class CommandLineInterface::MemoryOutputStream
// The string we're building.
string data_;
// Whether we should append the output stream to the existing file.
bool append_mode_;
// StringOutputStream writing to data_.
scoped_ptr<io::StringOutputStream> inner_;
};
@ -434,7 +440,13 @@ void CommandLineInterface::GeneratorContextImpl::AddJarManifest() {
io::ZeroCopyOutputStream* CommandLineInterface::GeneratorContextImpl::Open(
const string& filename) {
return new MemoryOutputStream(this, filename);
return new MemoryOutputStream(this, filename, false);
}
io::ZeroCopyOutputStream*
CommandLineInterface::GeneratorContextImpl::OpenForAppend(
const string& filename) {
return new MemoryOutputStream(this, filename, true);
}
io::ZeroCopyOutputStream*
@ -446,9 +458,10 @@ CommandLineInterface::GeneratorContextImpl::OpenForInsert(
// -------------------------------------------------------------------
CommandLineInterface::MemoryOutputStream::MemoryOutputStream(
GeneratorContextImpl* directory, const string& filename)
GeneratorContextImpl* directory, const string& filename, bool append_mode)
: directory_(directory),
filename_(filename),
append_mode_(append_mode),
inner_(new io::StringOutputStream(&data_)) {
}
@ -471,8 +484,12 @@ CommandLineInterface::MemoryOutputStream::~MemoryOutputStream() {
if (insertion_point_.empty()) {
// This was just a regular Open().
if (*map_slot != NULL) {
cerr << filename_ << ": Tried to write the same file twice." << endl;
directory_->had_error_ = true;
if (append_mode_) {
(*map_slot)->append(data_);
} else {
cerr << filename_ << ": Tried to write the same file twice." << endl;
directory_->had_error_ = true;
}
return;
}
@ -565,6 +582,7 @@ CommandLineInterface::MemoryOutputStream::~MemoryOutputStream() {
CommandLineInterface::CommandLineInterface()
: mode_(MODE_COMPILE),
print_mode_(PRINT_NONE),
error_format_(ERROR_FORMAT_GCC),
imports_in_descriptor_set_(false),
source_info_in_descriptor_set_(false),
@ -632,7 +650,9 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) {
// Parse each file.
for (int i = 0; i < input_files_.size(); i++) {
// Import the file.
importer.AddUnusedImportTrackFile(input_files_[i]);
const FileDescriptor* parsed_file = importer.Import(input_files_[i]);
importer.ClearUnusedImportTrackFiles();
if (parsed_file == NULL) return 1;
parsed_files.push_back(parsed_file);
@ -721,6 +741,25 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) {
}
}
if (mode_ == MODE_PRINT) {
switch (print_mode_) {
case PRINT_FREE_FIELDS:
for (int i = 0; i < parsed_files.size(); ++i) {
const FileDescriptor* fd = parsed_files[i];
for (int j = 0; j < fd->message_type_count(); ++j) {
PrintFreeFieldNumbers(fd->message_type(j));
}
}
break;
case PRINT_NONE:
GOOGLE_LOG(ERROR) << "If the code reaches here, it usually means a bug of "
"flag parsing in the CommonadLineInterface.";
return 1;
// Do not add a default case.
}
}
return 0;
}
@ -735,6 +774,7 @@ void CommandLineInterface::Clear() {
descriptor_set_name_.clear();
mode_ = MODE_COMPILE;
print_mode_ = PRINT_NONE;
imports_in_descriptor_set_ = false;
source_info_in_descriptor_set_ = false;
disallow_services_ = false;
@ -889,7 +929,8 @@ bool CommandLineInterface::ParseArgument(const char* arg,
*name == "--include_imports" ||
*name == "--include_source_info" ||
*name == "--version" ||
*name == "--decode_raw") {
*name == "--decode_raw" ||
*name == "--print_free_field_numbers") {
// HACK: These are the only flags that don't take a value.
// They probably should not be hard-coded like this but for now it's
// not worth doing better.
@ -919,8 +960,8 @@ CommandLineInterface::InterpretArgument(const string& name,
// Java's -classpath (and some other languages) delimits path components
// with colons. Let's accept that syntax too just to make things more
// intuitive.
vector<string> parts;
SplitStringUsing(value, kPathSeparator, &parts);
vector<string> parts = Split(
value, kPathSeparator, true);
for (int i = 0; i < parts.size(); i++) {
string virtual_path;
@ -1061,6 +1102,19 @@ CommandLineInterface::InterpretArgument(const string& name,
plugins_[plugin_name] = path;
} else if (name == "--print_free_field_numbers") {
if (mode_ != MODE_COMPILE) {
cerr << "Cannot use " << name << " and use --encode, --decode or print "
<< "other info at the same time." << endl;
return PARSE_ARGUMENT_FAIL;
}
if (!output_directives_.empty() || !descriptor_set_name_.empty()) {
cerr << "Cannot use " << name
<< " and generate code or descriptors at the same time." << endl;
return PARSE_ARGUMENT_FAIL;
}
mode_ = MODE_PRINT;
print_mode_ = PRINT_FREE_FIELDS;
} else {
// Some other flag. Look it up in the generators list.
const GeneratorInfo* generator_info =
@ -1082,8 +1136,8 @@ CommandLineInterface::InterpretArgument(const string& name,
} else {
// It's an output flag. Add it to the output directives.
if (mode_ != MODE_COMPILE) {
cerr << "Cannot use --encode or --decode and generate code at the "
"same time." << endl;
cerr << "Cannot use --encode, --decode or print .proto info and "
"generate code at the same time." << endl;
return PARSE_ARGUMENT_FAIL;
}
@ -1151,7 +1205,12 @@ void CommandLineInterface::PrintHelpText() {
" well as surrounding comments.\n"
" --error_format=FORMAT Set the format in which to print errors.\n"
" FORMAT may be 'gcc' (the default) or 'msvs'\n"
" (Microsoft Visual Studio format)." << endl;
" (Microsoft Visual Studio format).\n"
" --print_free_field_numbers Print the free field numbers of the messages\n"
" defined in the given proto files. Groups share\n"
" the same field number space with the parent \n"
" message. Extension ranges are counted as \n"
" occupied fields numbers." << endl;
if (!plugin_prefix_.empty()) {
cerr <<
" --plugin=EXECUTABLE Specifies a plugin executable to use.\n"
@ -1431,6 +1490,113 @@ void CommandLineInterface::GetTransitiveDependencies(
}
}
namespace {
// Utility function for PrintFreeFieldNumbers.
// Stores occupied ranges into the ranges parameter, and next level of sub
// message types into the nested_messages parameter. The FieldRange is left
// inclusive, right exclusive. i.e. [a, b).
//
// Nested Messages:
// Note that it only stores the nested message type, iff the nested type is
// either a direct child of the given descriptor, or the nested type is a
// decendent of the given descriptor and all the nodes between the
// nested type and the given descriptor are group types. e.g.
//
// message Foo {
// message Bar {
// message NestedBar {}
// }
// group Baz = 1 {
// group NestedBazGroup = 2 {
// message Quz {
// message NestedQuz {}
// }
// }
// message NestedBaz {}
// }
// }
//
// In this case, Bar, Quz and NestedBaz will be added into the nested types.
// Since free field numbers of group types will not be printed, this makes sure
// the nested message types in groups will not be dropped. The nested_messages
// parameter will contain the direct children (when groups are ignored in the
// tree) of the given descriptor for the caller to traverse. The declaration
// order of the nested messages is also preserved.
typedef pair<int, int> FieldRange;
void GatherOccupiedFieldRanges(const Descriptor* descriptor,
set<FieldRange>* ranges,
vector<const Descriptor*>* nested_messages) {
set<const Descriptor*> groups;
for (int i = 0; i < descriptor->field_count(); ++i) {
const FieldDescriptor* fd = descriptor->field(i);
ranges->insert(FieldRange(fd->number(), fd->number() + 1));
if (fd->type() == FieldDescriptor::TYPE_GROUP) {
groups.insert(fd->message_type());
}
}
for (int i = 0; i < descriptor->extension_range_count(); ++i) {
ranges->insert(FieldRange(descriptor->extension_range(i)->start,
descriptor->extension_range(i)->end));
}
// Handle the nested messages/groups in declaration order to make it
// post-order strict.
for (int i = 0; i < descriptor->nested_type_count(); ++i) {
const Descriptor* nested_desc = descriptor->nested_type(i);
if (groups.find(nested_desc) != groups.end()) {
GatherOccupiedFieldRanges(nested_desc, ranges, nested_messages);
} else {
nested_messages->push_back(nested_desc);
}
}
}
// Utility function for PrintFreeFieldNumbers.
// Actually prints the formatted free field numbers for given message name and
// occupied ranges.
void FormatFreeFieldNumbers(const string& name,
const set<FieldRange>& ranges) {
string output;
StringAppendF(&output, "%-35s free:", name.c_str());
int next_free_number = 1;
for (set<FieldRange>::iterator i = ranges.begin();
i != ranges.end(); ++i) {
// This happens when groups re-use parent field numbers, in which
// case we skip the FieldRange entirely.
if (next_free_number >= i->second) continue;
if (next_free_number < i->first) {
if (next_free_number + 1 == i->first) {
// Singleton
StringAppendF(&output, " %d", next_free_number);
} else {
// Range
StringAppendF(&output, " %d-%d", next_free_number, i->first - 1);
}
}
next_free_number = i->second;
}
if (next_free_number <= FieldDescriptor::kMaxNumber) {
StringAppendF(&output, " %d-INF", next_free_number);
}
cout << output << endl;
}
} // namespace
void CommandLineInterface::PrintFreeFieldNumbers(
const Descriptor* descriptor) {
set<FieldRange> ranges;
vector<const Descriptor*> nested_messages;
GatherOccupiedFieldRanges(descriptor, &ranges, &nested_messages);
for (int i = 0; i < nested_messages.size(); ++i) {
PrintFreeFieldNumbers(nested_messages[i]);
}
FormatFreeFieldNumbers(descriptor->full_name(), ranges);
}
} // namespace compiler
} // namespace protobuf

@ -48,8 +48,9 @@
namespace google {
namespace protobuf {
class FileDescriptor; // descriptor.h
class Descriptor; // descriptor.h
class DescriptorPool; // descriptor.h
class FileDescriptor; // descriptor.h
class FileDescriptorProto; // descriptor.pb.h
template<typename T> class RepeatedPtrField; // repeated_field.h
@ -259,6 +260,22 @@ class LIBPROTOC_EXPORT CommandLineInterface {
set<const FileDescriptor*>* already_seen,
RepeatedPtrField<FileDescriptorProto>* output);
// Implements the --print_free_field_numbers. This function prints free field
// numbers into stdout for the message and it's nested message types in
// post-order, i.e. nested types first. Printed range are left-right
// inclusive, i.e. [a, b].
//
// Groups:
// For historical reasons, groups are considered to share the same
// field number space with the parent message, thus it will not print free
// field numbers for groups. The field numbers used in the groups are
// excluded in the free field numbers of the parent message.
//
// Extension Ranges:
// Extension ranges are considered ocuppied field numbers and they will not be
// listed as free numbers in the output.
void PrintFreeFieldNumbers(const Descriptor* descriptor);
// -----------------------------------------------------------------
// The name of the executable as invoked (i.e. argv[0]).
@ -295,11 +312,19 @@ class LIBPROTOC_EXPORT CommandLineInterface {
enum Mode {
MODE_COMPILE, // Normal mode: parse .proto files and compile them.
MODE_ENCODE, // --encode: read text from stdin, write binary to stdout.
MODE_DECODE // --decode: read binary from stdin, write text to stdout.
MODE_DECODE, // --decode: read binary from stdin, write text to stdout.
MODE_PRINT, // Print mode: print info of the given .proto files and exit.
};
Mode mode_;
enum PrintMode {
PRINT_NONE, // Not in MODE_PRINT
PRINT_FREE_FIELDS, // --print_free_fields
};
PrintMode print_mode_;
enum ErrorFormat {
ERROR_FORMAT_GCC, // GCC error output format (default).
ERROR_FORMAT_MSVS // Visual Studio output (--error_format=msvs).

@ -40,6 +40,7 @@
#else
#include <unistd.h>
#endif
#include <memory>
#include <vector>
#include <google/protobuf/descriptor.pb.h>
@ -126,6 +127,9 @@ class CommandLineInterfaceTest : public testing::Test {
void ExpectErrorSubstringWithZeroReturnCode(
const string& expected_substring);
// Checks that the captured stdout is the same as the expected_text.
void ExpectCapturedStdout(const string& expected_text);
// Returns true if ExpectErrorSubstring(expected_substring) would pass, but
// does not fail otherwise.
bool HasAlternateErrorSubstring(const string& expected_substring);
@ -182,6 +186,9 @@ class CommandLineInterfaceTest : public testing::Test {
// The captured stderr output.
string error_text_;
// The captured stdout.
string captured_stdout_;
// Pointers which need to be deleted later.
vector<CodeGenerator*> mock_generators_to_delete_;
@ -224,7 +231,7 @@ void CommandLineInterfaceTest::SetUp() {
}
// Create the temp directory.
GOOGLE_CHECK(File::CreateDir(temp_directory_.c_str(), DEFAULT_FILE_MODE));
GOOGLE_CHECK_OK(File::CreateDir(temp_directory_, 0777));
// Register generators.
CodeGenerator* generator = new MockCodeGenerator("test_generator");
@ -255,8 +262,7 @@ void CommandLineInterfaceTest::TearDown() {
}
void CommandLineInterfaceTest::Run(const string& command) {
vector<string> args;
SplitStringUsing(command, " ", &args);
vector<string> args = Split(command, " ", true);
if (!disallow_plugins_) {
cli_.AllowPlugins("prefix-");
@ -295,18 +301,20 @@ void CommandLineInterfaceTest::Run(const string& command) {
}
}
scoped_array<const char*> argv(new const char*[args.size()]);
scoped_array<const char*> argv(new const char* [args.size()]);
for (int i = 0; i < args.size(); i++) {
args[i] = StringReplace(args[i], "$tmpdir", temp_directory_, true);
argv[i] = args[i].c_str();
}
CaptureTestStdout();
CaptureTestStderr();
return_code_ = cli_.Run(args.size(), argv.get());
error_text_ = GetCapturedTestStderr();
captured_stdout_ = GetCapturedTestStdout();
}
// -------------------------------------------------------------------
@ -318,16 +326,20 @@ void CommandLineInterfaceTest::CreateTempFile(
string::size_type slash_pos = name.find_last_of('/');
if (slash_pos != string::npos) {
string dir = name.substr(0, slash_pos);
File::RecursivelyCreateDir(temp_directory_ + "/" + dir, 0777);
if (!File::Exists(temp_directory_ + "/" + dir)) {
GOOGLE_CHECK_OK(File::RecursivelyCreateDir(temp_directory_ + "/" + dir,
0777));
}
}
// Write file.
string full_name = temp_directory_ + "/" + name;
File::WriteStringToFileOrDie(contents, full_name);
GOOGLE_CHECK_OK(File::SetContents(full_name, contents, true));
}
void CommandLineInterfaceTest::CreateTempDir(const string& name) {
File::RecursivelyCreateDir(temp_directory_ + "/" + name, 0777);
GOOGLE_CHECK_OK(File::RecursivelyCreateDir(temp_directory_ + "/" + name,
0777));
}
// -------------------------------------------------------------------
@ -414,14 +426,18 @@ void CommandLineInterfaceTest::ReadDescriptorSet(
const string& filename, FileDescriptorSet* descriptor_set) {
string path = temp_directory_ + "/" + filename;
string file_contents;
if (!File::ReadFileToString(path, &file_contents)) {
FAIL() << "File not found: " << path;
}
GOOGLE_CHECK_OK(File::GetContents(path, &file_contents, true));
if (!descriptor_set->ParseFromString(file_contents)) {
FAIL() << "Could not parse file contents: " << path;
}
}
void CommandLineInterfaceTest::ExpectCapturedStdout(
const string& expected_text) {
EXPECT_EQ(expected_text, captured_stdout_);
}
// ===================================================================
TEST_F(CommandLineInterfaceTest, BasicOutput) {
@ -813,7 +829,7 @@ TEST_F(CommandLineInterfaceTest, WriteDescriptorSet) {
FileDescriptorSet descriptor_set;
ReadDescriptorSet("descriptor_set", &descriptor_set);
if (HasFatalFailure()) return;
ASSERT_EQ(1, descriptor_set.file_size());
EXPECT_EQ(1, descriptor_set.file_size());
EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
// Descriptor set should not have source code info.
EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
@ -838,7 +854,7 @@ TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithSourceInfo) {
FileDescriptorSet descriptor_set;
ReadDescriptorSet("descriptor_set", &descriptor_set);
if (HasFatalFailure()) return;
ASSERT_EQ(1, descriptor_set.file_size());
EXPECT_EQ(1, descriptor_set.file_size());
EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
// Source code info included.
EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
@ -863,7 +879,7 @@ TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSet) {
FileDescriptorSet descriptor_set;
ReadDescriptorSet("descriptor_set", &descriptor_set);
if (HasFatalFailure()) return;
ASSERT_EQ(2, descriptor_set.file_size());
EXPECT_EQ(2, descriptor_set.file_size());
if (descriptor_set.file(0).name() == "bar.proto") {
std::swap(descriptor_set.mutable_file()->mutable_data()[0],
descriptor_set.mutable_file()->mutable_data()[1]);
@ -894,7 +910,7 @@ TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSetWithSourceInfo) {
FileDescriptorSet descriptor_set;
ReadDescriptorSet("descriptor_set", &descriptor_set);
if (HasFatalFailure()) return;
ASSERT_EQ(2, descriptor_set.file_size());
EXPECT_EQ(2, descriptor_set.file_size());
if (descriptor_set.file(0).name() == "bar.proto") {
std::swap(descriptor_set.mutable_file()->mutable_data()[0],
descriptor_set.mutable_file()->mutable_data()[1]);
@ -1393,6 +1409,70 @@ TEST_F(CommandLineInterfaceTest, MissingValueAtEndError) {
ExpectErrorText("Missing value for flag: --test_out\n");
}
TEST_F(CommandLineInterfaceTest, PrintFreeFieldNumbers) {
CreateTempFile(
"foo.proto",
"syntax = \"proto2\";\n"
"package foo;\n"
"message Foo {\n"
" optional int32 a = 2;\n"
" optional string b = 4;\n"
" optional string c = 5;\n"
" optional int64 d = 8;\n"
" optional double e = 10;\n"
"}\n");
CreateTempFile(
"bar.proto",
"syntax = \"proto2\";\n"
"message Bar {\n"
" optional int32 a = 2;\n"
" extensions 4 to 5;\n"
" optional int64 d = 8;\n"
" extensions 10;\n"
"}\n");
CreateTempFile(
"baz.proto",
"syntax = \"proto2\";\n"
"message Baz {\n"
" optional int32 a = 2;\n"
" optional int64 d = 8;\n"
" extensions 15 to max;\n" // unordered.
" extensions 13;\n"
" extensions 10 to 12;\n"
" extensions 5;\n"
" extensions 4;\n"
"}\n");
CreateTempFile(
"quz.proto",
"syntax = \"proto2\";\n"
"message Quz {\n"
" message Foo {}\n" // nested message
" optional int32 a = 2;\n"
" optional group C = 4 {\n"
" optional int32 d = 5;\n"
" }\n"
" extensions 8 to 10;\n"
" optional group E = 11 {\n"
" optional int32 f = 9;\n" // explicitly reuse extension range 8-10
" optional group G = 15 {\n" // nested group
" message Foo {}\n" // nested message inside nested group
" }\n"
" }\n"
"}\n");
Run("protocol_compiler --print_free_field_numbers --proto_path=$tmpdir "
"foo.proto bar.proto baz.proto quz.proto");
ExpectNoErrors();
ExpectCapturedStdout(
"foo.Foo free: 1 3 6-7 9 11-INF\n"
"Bar free: 1 3 6-7 9 11-INF\n"
"Baz free: 1 3 6-7 9 14\n"
"Quz.Foo free: 1-INF\n"
"Quz.E.G.Foo free: 1-INF\n"
"Quz free: 1 3 6-7 12-14 16-INF\n");
}
// ===================================================================
// Test for --encode and --decode. Note that it would be easier to do this
@ -1412,7 +1492,7 @@ class EncodeDecodeTest : public testing::Test {
void RedirectStdinFromText(const string& input) {
string filename = TestTempDir() + "/test_stdin";
File::WriteStringToFileOrDie(input, filename);
GOOGLE_CHECK_OK(File::SetContents(filename, input, true));
GOOGLE_CHECK(RedirectStdinFromFile(filename));
}
@ -1446,7 +1526,7 @@ class EncodeDecodeTest : public testing::Test {
SplitStringUsing(command, " ", &args);
args.push_back("--proto_path=" + TestSourceDir());
scoped_array<const char*> argv(new const char*[args.size()]);
scoped_array<const char*> argv(new const char* [args.size()]);
for (int i = 0; i < args.size(); i++) {
argv[i] = args[i].c_str();
}
@ -1467,7 +1547,7 @@ class EncodeDecodeTest : public testing::Test {
void ExpectStdoutMatchesBinaryFile(const string& filename) {
string expected_output;
ASSERT_TRUE(File::ReadFileToString(filename, &expected_output));
GOOGLE_CHECK_OK(File::GetContents(filename, &expected_output, true));
// Don't use EXPECT_EQ because we don't want to print raw binary data to
// stdout on failure.
@ -1476,7 +1556,7 @@ class EncodeDecodeTest : public testing::Test {
void ExpectStdoutMatchesTextFile(const string& filename) {
string expected_output;
ASSERT_TRUE(File::ReadFileToString(filename, &expected_output));
GOOGLE_CHECK_OK(File::GetContents(filename, &expected_output, true));
ExpectStdoutMatchesText(expected_output);
}
@ -1496,22 +1576,23 @@ class EncodeDecodeTest : public testing::Test {
};
TEST_F(EncodeDecodeTest, Encode) {
RedirectStdinFromFile(TestSourceDir() +
"/google/protobuf/testdata/text_format_unittest_data.txt");
RedirectStdinFromFile(TestSourceDir() + "/google/protobuf/"
"testdata/text_format_unittest_data_oneof_implemented.txt");
EXPECT_TRUE(Run("google/protobuf/unittest.proto "
"--encode=protobuf_unittest.TestAllTypes"));
ExpectStdoutMatchesBinaryFile(TestSourceDir() +
"/google/protobuf/testdata/golden_message");
"/google/protobuf/testdata/golden_message_oneof_implemented");
ExpectStderrMatchesText("");
}
TEST_F(EncodeDecodeTest, Decode) {
RedirectStdinFromFile(TestSourceDir() +
"/google/protobuf/testdata/golden_message");
"/google/protobuf/testdata/golden_message_oneof_implemented");
EXPECT_TRUE(Run("google/protobuf/unittest.proto "
"--decode=protobuf_unittest.TestAllTypes"));
ExpectStdoutMatchesTextFile(TestSourceDir() +
"/google/protobuf/testdata/text_format_unittest_data.txt");
"/google/protobuf/"
"testdata/text_format_unittest_data_oneof_implemented.txt");
ExpectStderrMatchesText("");
}

@ -48,7 +48,7 @@
#include <google/protobuf/compiler/importer.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/stubs/map-util.h>
#include <google/protobuf/stubs/map_util.h>
#include <google/protobuf/stubs/stl_util.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/substitute.h>
@ -93,9 +93,9 @@ class MockGeneratorContext : public GeneratorContext {
<< "Generator failed to generate file: " << virtual_filename;
string actual_contents;
File::ReadFileToStringOrDie(
TestSourceDir() + "/" + physical_filename,
&actual_contents);
GOOGLE_CHECK_OK(
File::GetContents(TestSourceDir() + "/" + physical_filename,
&actual_contents, true));
EXPECT_TRUE(actual_contents == *expected_contents)
<< physical_filename << " needs to be regenerated. Please run "
"generate_descriptor_proto.sh and add this file "

@ -45,11 +45,27 @@ namespace protobuf {
namespace compiler {
namespace cpp {
namespace {
// The GOOGLE_ARRAYSIZE constant is the max enum value plus 1. If the max enum value
// is kint32max, GOOGLE_ARRAYSIZE will overflow. In such cases we should omit the
// generation of the GOOGLE_ARRAYSIZE constant.
bool ShouldGenerateArraySize(const EnumDescriptor* descriptor) {
int32 max_value = descriptor->value(0)->number();
for (int i = 0; i < descriptor->value_count(); i++) {
if (descriptor->value(i)->number() > max_value) {
max_value = descriptor->value(i)->number();
}
}
return max_value != kint32max;
}
} // namespace
EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor,
const Options& options)
: descriptor_(descriptor),
classname_(ClassName(descriptor, false)),
options_(options) {
options_(options),
generate_array_size_(ShouldGenerateArraySize(descriptor)) {
}
EnumGenerator::~EnumGenerator() {}
@ -67,7 +83,10 @@ void EnumGenerator::GenerateDefinition(io::Printer* printer) {
for (int i = 0; i < descriptor_->value_count(); i++) {
vars["name"] = descriptor_->value(i)->name();
vars["number"] = SimpleItoa(descriptor_->value(i)->number());
// In C++, an value of -2147483648 gets interpreted as the negative of
// 2147483648, and since 2147483648 can't fit in an integer, this produces a
// compiler warning. This works around that issue.
vars["number"] = Int32ToString(descriptor_->value(i)->number());
vars["prefix"] = (descriptor_->containing_type() == NULL) ?
"" : classname_ + "_";
@ -97,9 +116,13 @@ void EnumGenerator::GenerateDefinition(io::Printer* printer) {
printer->Print(vars,
"$dllexport$bool $classname$_IsValid(int value);\n"
"const $classname$ $prefix$$short_name$_MIN = $prefix$$min_name$;\n"
"const $classname$ $prefix$$short_name$_MAX = $prefix$$max_name$;\n"
"const int $prefix$$short_name$_ARRAYSIZE = $prefix$$short_name$_MAX + 1;\n"
"\n");
"const $classname$ $prefix$$short_name$_MAX = $prefix$$max_name$;\n");
if (generate_array_size_) {
printer->Print(vars,
"const int $prefix$$short_name$_ARRAYSIZE = "
"$prefix$$short_name$_MAX + 1;\n\n");
}
if (HasDescriptorMethods(descriptor_->file())) {
printer->Print(vars,
@ -123,6 +146,7 @@ void EnumGenerator::
GenerateGetEnumDescriptorSpecializations(io::Printer* printer) {
if (HasDescriptorMethods(descriptor_->file())) {
printer->Print(
"template <> struct is_proto_enum< $classname$> : ::google::protobuf::internal::true_type {};\n"
"template <>\n"
"inline const EnumDescriptor* GetEnumDescriptor< $classname$>() {\n"
" return $classname$_descriptor();\n"
@ -150,9 +174,12 @@ void EnumGenerator::GenerateSymbolImports(io::Printer* printer) {
"static const $nested_name$ $nested_name$_MIN =\n"
" $classname$_$nested_name$_MIN;\n"
"static const $nested_name$ $nested_name$_MAX =\n"
" $classname$_$nested_name$_MAX;\n"
"static const int $nested_name$_ARRAYSIZE =\n"
" $classname$_$nested_name$_ARRAYSIZE;\n");
" $classname$_$nested_name$_MAX;\n");
if (generate_array_size_) {
printer->Print(vars,
"static const int $nested_name$_ARRAYSIZE =\n"
" $classname$_$nested_name$_ARRAYSIZE;\n");
}
if (HasDescriptorMethods(descriptor_->file())) {
printer->Print(vars,
@ -218,7 +245,7 @@ void EnumGenerator::GenerateMethods(io::Printer* printer) {
iter != numbers.end(); ++iter) {
printer->Print(
" case $number$:\n",
"number", SimpleItoa(*iter));
"number", Int32ToString(*iter));
}
printer->Print(vars,
@ -245,8 +272,11 @@ void EnumGenerator::GenerateMethods(io::Printer* printer) {
}
printer->Print(vars,
"const $classname$ $parent$::$nested_name$_MIN;\n"
"const $classname$ $parent$::$nested_name$_MAX;\n"
"const int $parent$::$nested_name$_ARRAYSIZE;\n");
"const $classname$ $parent$::$nested_name$_MAX;\n");
if (generate_array_size_) {
printer->Print(vars,
"const int $parent$::$nested_name$_ARRAYSIZE;\n");
}
printer->Print("#endif // _MSC_VER\n");
}

@ -89,6 +89,8 @@ class EnumGenerator {
const EnumDescriptor* descriptor_;
string classname_;
Options options_;
// whether to generate the *_ARRAYSIZE constant.
bool generate_array_size_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumGenerator);
};

@ -51,7 +51,8 @@ void SetEnumVariables(const FieldDescriptor* descriptor,
SetCommonFieldVariables(descriptor, variables, options);
const EnumValueDescriptor* default_value = descriptor->default_value_enum();
(*variables)["type"] = ClassName(descriptor->enum_type(), true);
(*variables)["default"] = SimpleItoa(default_value->number());
(*variables)["default"] = Int32ToString(default_value->number());
(*variables)["full_name"] = descriptor->full_name();
}
} // namespace
@ -83,12 +84,14 @@ void EnumFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer) const {
printer->Print(variables_,
"inline $type$ $classname$::$name$() const {\n"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return static_cast< $type$ >($name$_);\n"
"}\n"
"inline void $classname$::set_$name$($type$ value) {\n"
" assert($type$_IsValid(value));\n"
" set_has_$name$();\n"
" $name$_ = value;\n"
" // @@protoc_insertion_point(field_set:$full_name$)\n"
"}\n");
}
@ -121,10 +124,15 @@ GenerateMergeFromCodedStream(io::Printer* printer) const {
" input, &value)));\n"
"if ($type$_IsValid(value)) {\n"
" set_$name$(static_cast< $type$ >(value));\n");
if (HasUnknownFields(descriptor_->file())) {
if (UseUnknownFieldSet(descriptor_->file())) {
printer->Print(variables_,
"} else {\n"
" mutable_unknown_fields()->AddVarint($number$, value);\n");
} else {
printer->Print(
"} else {\n"
" unknown_fields_stream.WriteVarint32(tag);\n"
" unknown_fields_stream.WriteVarint32(value);\n");
}
printer->Print(variables_,
"}\n");
@ -153,6 +161,52 @@ GenerateByteSize(io::Printer* printer) const {
// ===================================================================
EnumOneofFieldGenerator::
EnumOneofFieldGenerator(const FieldDescriptor* descriptor,
const Options& options)
: EnumFieldGenerator(descriptor, options) {
SetCommonOneofFieldVariables(descriptor, &variables_);
}
EnumOneofFieldGenerator::~EnumOneofFieldGenerator() {}
void EnumOneofFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer) const {
printer->Print(variables_,
"inline $type$ $classname$::$name$() const {\n"
" if (has_$name$()) {\n"
" return static_cast< $type$ >($oneof_prefix$$name$_);\n"
" }\n"
" return static_cast< $type$ >($default$);\n"
"}\n"
"inline void $classname$::set_$name$($type$ value) {\n"
" assert($type$_IsValid(value));\n"
" if (!has_$name$()) {\n"
" clear_$oneof_name$();\n"
" set_has_$name$();\n"
" }\n"
" $oneof_prefix$$name$_ = value;\n"
"}\n");
}
void EnumOneofFieldGenerator::
GenerateClearingCode(io::Printer* printer) const {
printer->Print(variables_, "$oneof_prefix$$name$_ = $default$;\n");
}
void EnumOneofFieldGenerator::
GenerateSwappingCode(io::Printer* printer) const {
// Don't print any swapping code. Swapping the union will swap this field.
}
void EnumOneofFieldGenerator::
GenerateConstructorCode(io::Printer* printer) const {
printer->Print(variables_,
" $classname$_default_oneof_instance_->$name$_ = $default$;\n");
}
// ===================================================================
RepeatedEnumFieldGenerator::
RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor,
const Options& options)
@ -166,7 +220,8 @@ void RepeatedEnumFieldGenerator::
GeneratePrivateMembers(io::Printer* printer) const {
printer->Print(variables_,
"::google::protobuf::RepeatedField<int> $name$_;\n");
if (descriptor_->options().packed() && HasGeneratedMethods(descriptor_->file())) {
if (descriptor_->options().packed()
&& HasGeneratedMethods(descriptor_->file())) {
printer->Print(variables_,
"mutable int _$name$_cached_byte_size_;\n");
}
@ -187,23 +242,28 @@ void RepeatedEnumFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer) const {
printer->Print(variables_,
"inline $type$ $classname$::$name$(int index) const {\n"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return static_cast< $type$ >($name$_.Get(index));\n"
"}\n"
"inline void $classname$::set_$name$(int index, $type$ value) {\n"
" assert($type$_IsValid(value));\n"
" $name$_.Set(index, value);\n"
" // @@protoc_insertion_point(field_set:$full_name$)\n"
"}\n"
"inline void $classname$::add_$name$($type$ value) {\n"
" assert($type$_IsValid(value));\n"
" $name$_.Add(value);\n"
" // @@protoc_insertion_point(field_add:$full_name$)\n"
"}\n");
printer->Print(variables_,
"inline const ::google::protobuf::RepeatedField<int>&\n"
"$classname$::$name$() const {\n"
" // @@protoc_insertion_point(field_list:$full_name$)\n"
" return $name$_;\n"
"}\n"
"inline ::google::protobuf::RepeatedField<int>*\n"
"$classname$::mutable_$name$() {\n"
" // @@protoc_insertion_point(field_mutable_list:$full_name$)\n"
" return &$name$_;\n"
"}\n");
}
@ -238,10 +298,15 @@ GenerateMergeFromCodedStream(io::Printer* printer) const {
" input, &value)));\n"
"if ($type$_IsValid(value)) {\n"
" add_$name$(static_cast< $type$ >(value));\n");
if (HasUnknownFields(descriptor_->file())) {
if (UseUnknownFieldSet(descriptor_->file())) {
printer->Print(variables_,
"} else {\n"
" mutable_unknown_fields()->AddVarint($number$, value);\n");
} else {
printer->Print(
"} else {\n"
" unknown_fields_stream.WriteVarint32(tag);\n"
" unknown_fields_stream.WriteVarint32(value);\n");
}
printer->Print("}\n");
}

@ -63,13 +63,30 @@ class EnumFieldGenerator : public FieldGenerator {
void GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const;
void GenerateByteSize(io::Printer* printer) const;
private:
protected:
const FieldDescriptor* descriptor_;
map<string, string> variables_;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumFieldGenerator);
};
class EnumOneofFieldGenerator : public EnumFieldGenerator {
public:
explicit EnumOneofFieldGenerator(const FieldDescriptor* descriptor,
const Options& options);
~EnumOneofFieldGenerator();
// implements FieldGenerator ---------------------------------------
void GenerateInlineAccessorDefinitions(io::Printer* printer) const;
void GenerateClearingCode(io::Printer* printer) const;
void GenerateSwappingCode(io::Printer* printer) const;
void GenerateConstructorCode(io::Printer* printer) const;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumOneofFieldGenerator);
};
class RepeatedEnumFieldGenerator : public FieldGenerator {
public:
explicit RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor,

@ -33,6 +33,8 @@
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/cpp/cpp_field.h>
#include <memory>
#include <google/protobuf/compiler/cpp/cpp_helpers.h>
#include <google/protobuf/compiler/cpp/cpp_primitive_field.h>
#include <google/protobuf/compiler/cpp/cpp_string_field.h>
@ -68,6 +70,12 @@ void SetCommonFieldVariables(const FieldDescriptor* descriptor,
(*variables)["cppget"] = "Get";
}
void SetCommonOneofFieldVariables(const FieldDescriptor* descriptor,
map<string, string>* variables) {
(*variables)["oneof_prefix"] = descriptor->containing_oneof()->name() + "_.";
(*variables)["oneof_name"] = descriptor->containing_oneof()->name();
}
FieldGenerator::~FieldGenerator() {}
void FieldGenerator::
@ -84,8 +92,9 @@ GenerateMergeFromCodedStreamWithPacking(io::Printer* printer) const {
FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor,
const Options& options)
: descriptor_(descriptor),
field_generators_(new scoped_ptr<FieldGenerator>[descriptor->field_count()]) {
: descriptor_(descriptor),
field_generators_(
new scoped_ptr<FieldGenerator>[descriptor->field_count()]) {
// Construct all the FieldGenerators.
for (int i = 0; i < descriptor->field_count(); i++) {
field_generators_[i].reset(MakeGenerator(descriptor->field(i), options));
@ -109,6 +118,21 @@ FieldGenerator* FieldGeneratorMap::MakeGenerator(const FieldDescriptor* field,
default:
return new RepeatedPrimitiveFieldGenerator(field, options);
}
} else if (field->containing_oneof()) {
switch (field->cpp_type()) {
case FieldDescriptor::CPPTYPE_MESSAGE:
return new MessageOneofFieldGenerator(field, options);
case FieldDescriptor::CPPTYPE_STRING:
switch (field->options().ctype()) {
default: // StringOneofFieldGenerator handles unknown ctypes.
case FieldOptions::STRING:
return new StringOneofFieldGenerator(field, options);
}
case FieldDescriptor::CPPTYPE_ENUM:
return new EnumOneofFieldGenerator(field, options);
default:
return new PrimitiveOneofFieldGenerator(field, options);
}
} else {
switch (field->cpp_type()) {
case FieldDescriptor::CPPTYPE_MESSAGE:

@ -36,9 +36,9 @@
#define GOOGLE_PROTOBUF_COMPILER_CPP_FIELD_H__
#include <map>
#include <memory>
#include <string>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/compiler/cpp/cpp_options.h>
@ -61,6 +61,9 @@ void SetCommonFieldVariables(const FieldDescriptor* descriptor,
map<string, string>* variables,
const Options& options);
void SetCommonOneofFieldVariables(const FieldDescriptor* descriptor,
map<string, string>* variables);
class FieldGenerator {
public:
FieldGenerator() {}
@ -71,6 +74,11 @@ class FieldGenerator {
// class.
virtual void GeneratePrivateMembers(io::Printer* printer) const = 0;
// Generate static default variable for this field. These are placed inside
// the message class. Most field types don't need this, so the default
// implementation is empty.
virtual void GenerateStaticMembers(io::Printer* printer) const {}
// Generate prototypes for all of the accessor functions related to this
// field. These are placed inside the class definition.
virtual void GenerateAccessorDeclarations(io::Printer* printer) const = 0;

@ -33,6 +33,9 @@
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/cpp/cpp_file.h>
#include <memory>
#include <set>
#include <google/protobuf/compiler/cpp/cpp_enum.h>
#include <google/protobuf/compiler/cpp/cpp_service.h>
#include <google/protobuf/compiler/cpp/cpp_extension.h>
@ -50,18 +53,17 @@ namespace cpp {
// ===================================================================
FileGenerator::FileGenerator(const FileDescriptor* file,
const Options& options)
: file_(file),
message_generators_(
new scoped_ptr<MessageGenerator>[file->message_type_count()]),
enum_generators_(
new scoped_ptr<EnumGenerator>[file->enum_type_count()]),
service_generators_(
new scoped_ptr<ServiceGenerator>[file->service_count()]),
extension_generators_(
new scoped_ptr<ExtensionGenerator>[file->extension_count()]),
options_(options) {
FileGenerator::FileGenerator(const FileDescriptor* file, const Options& options)
: file_(file),
message_generators_(
new scoped_ptr<MessageGenerator>[file->message_type_count()]),
enum_generators_(
new scoped_ptr<EnumGenerator>[file->enum_type_count()]),
service_generators_(
new scoped_ptr<ServiceGenerator>[file->service_count()]),
extension_generators_(
new scoped_ptr<ExtensionGenerator>[file->extension_count()]),
options_(options) {
for (int i = 0; i < file->message_type_count(); i++) {
message_generators_[i].reset(
@ -153,19 +155,28 @@ void FileGenerator::GenerateHeader(io::Printer* printer) {
"#include <google/protobuf/service.h>\n");
}
if (HasUnknownFields(file_) && file_->message_type_count() > 0) {
if (UseUnknownFieldSet(file_) && file_->message_type_count() > 0) {
printer->Print(
"#include <google/protobuf/unknown_field_set.h>\n");
}
set<string> public_import_names;
for (int i = 0; i < file_->public_dependency_count(); i++) {
public_import_names.insert(file_->public_dependency(i)->name());
}
for (int i = 0; i < file_->dependency_count(); i++) {
const string& name = file_->dependency(i)->name();
bool public_import = (public_import_names.count(name) != 0);
printer->Print(
"#include \"$dependency$.pb.h\"\n",
"dependency", StripProto(file_->dependency(i)->name()));
"#include \"$dependency$.pb.h\"$iwyu$\n",
"dependency", StripProto(name),
"iwyu", (public_import) ? " // IWYU pragma: export" : "");
}
printer->Print(
"// @@protoc_insertion_point(includes)\n");
@ -248,6 +259,7 @@ void FileGenerator::GenerateHeader(io::Printer* printer) {
printer->Print(kThickSeparator);
printer->Print("\n");
// Generate class inline methods.
for (int i = 0; i < file_->message_type_count(); i++) {
if (i > 0) {
@ -317,6 +329,12 @@ void FileGenerator::GenerateSource(io::Printer* printer) {
"filename", file_->name(),
"basename", StripProto(file_->name()));
// Unknown fields implementation in lite mode uses StringOutputStream
if (!UseUnknownFieldSet(file_) && file_->message_type_count() > 0) {
printer->Print(
"#include <google/protobuf/io/zero_copy_stream_impl_lite.h>\n");
}
if (HasDescriptorMethods(file_)) {
printer->Print(
"#include <google/protobuf/descriptor.h>\n"
@ -542,17 +560,12 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) {
for (int i = 0; i < file_->dependency_count(); i++) {
const FileDescriptor* dependency = file_->dependency(i);
// Print the namespace prefix for the dependency.
vector<string> dependency_package_parts;
SplitStringUsing(dependency->package(), ".", &dependency_package_parts);
printer->Print("::");
for (int j = 0; j < dependency_package_parts.size(); j++) {
printer->Print("$name$::",
"name", dependency_package_parts[j]);
}
string add_desc_name = QualifiedFileLevelSymbol(
dependency->package(), GlobalAddDescriptorsName(dependency->name()));
// Call its AddDescriptors function.
printer->Print(
"$name$();\n",
"name", GlobalAddDescriptorsName(dependency->name()));
"name", add_desc_name);
}
if (HasDescriptorMethods(file_)) {

@ -35,6 +35,7 @@
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_FILE_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_FILE_H__
#include <memory>
#include <string>
#include <vector>
#include <google/protobuf/stubs/common.h>
@ -85,7 +86,6 @@ class FileGenerator {
// E.g. if the package is foo.bar, package_parts_ is {"foo", "bar"}.
vector<string> package_parts_;
const Options options_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileGenerator);

@ -35,6 +35,7 @@
#include <google/protobuf/compiler/cpp/cpp_generator.h>
#include <vector>
#include <memory>
#include <utility>
#include <google/protobuf/compiler/cpp/cpp_file.h>
@ -102,7 +103,7 @@ bool CppGenerator::Generate(const FileDescriptor* file,
// Generate header.
{
scoped_ptr<io::ZeroCopyOutputStream> output(
generator_context->Open(basename + ".h"));
generator_context->Open(basename + ".h"));
io::Printer printer(output.get(), '$');
file_generator.GenerateHeader(&printer);
}
@ -110,7 +111,7 @@ bool CppGenerator::Generate(const FileDescriptor* file,
// Generate cc file.
{
scoped_ptr<io::ZeroCopyOutputStream> output(
generator_context->Open(basename + ".cc"));
generator_context->Open(basename + ".cc"));
io::Printer printer(output.get(), '$');
file_generator.GenerateSource(&printer);
}

@ -82,6 +82,22 @@ hash_set<string> MakeKeywordsMap() {
hash_set<string> kKeywords = MakeKeywordsMap();
// Returns whether the provided descriptor has an extension. This includes its
// nested types.
bool HasExtension(const Descriptor* descriptor) {
if (descriptor->extension_count() > 0) {
return true;
}
for (int i = 0; i < descriptor->nested_type_count(); ++i) {
if (HasExtension(descriptor->nested_type(i))) {
return true;
}
}
return false;
}
} // namespace
string UnderscoresToCamelCase(const string& input, bool cap_next_letter) {
string result;
// Note: I distrust ctype.h due to locales.
@ -107,22 +123,6 @@ string UnderscoresToCamelCase(const string& input, bool cap_next_letter) {
return result;
}
// Returns whether the provided descriptor has an extension. This includes its
// nested types.
bool HasExtension(const Descriptor* descriptor) {
if (descriptor->extension_count() > 0) {
return true;
}
for (int i = 0; i < descriptor->nested_type_count(); ++i) {
if (HasExtension(descriptor->nested_type(i))) {
return true;
}
}
return false;
}
} // namespace
const char kThickSeparator[] =
"// ===================================================================\n";
const char kThinSeparator[] =
@ -256,27 +256,35 @@ const char* DeclaredTypeMethodName(FieldDescriptor::Type type) {
return "";
}
string Int32ToString(int number) {
// gcc rejects the decimal form of kint32min.
if (number == kint32min) {
GOOGLE_COMPILE_ASSERT(kint32min == (~0x7fffffff), kint32min_value_error);
return "(~0x7fffffff)";
} else {
return SimpleItoa(number);
}
}
string Int64ToString(int64 number) {
// gcc rejects the decimal form of kint64min
if (number == kint64min) {
// Make sure we are in a 2's complement system.
GOOGLE_COMPILE_ASSERT(kint64min == GOOGLE_LONGLONG(-0x8000000000000000),
kint64min_value_error);
return "GOOGLE_LONGLONG(-0x8000000000000000)";
}
return "GOOGLE_LONGLONG(" + SimpleItoa(number) + ")";
}
string DefaultValue(const FieldDescriptor* field) {
switch (field->cpp_type()) {
case FieldDescriptor::CPPTYPE_INT32:
// gcc rejects the decimal form of kint32min and kint64min.
if (field->default_value_int32() == kint32min) {
// Make sure we are in a 2's complement system.
GOOGLE_COMPILE_ASSERT(kint32min == -0x80000000, kint32min_value_error);
return "-0x80000000";
}
return SimpleItoa(field->default_value_int32());
return Int32ToString(field->default_value_int32());
case FieldDescriptor::CPPTYPE_UINT32:
return SimpleItoa(field->default_value_uint32()) + "u";
case FieldDescriptor::CPPTYPE_INT64:
// See the comments for CPPTYPE_INT32.
if (field->default_value_int64() == kint64min) {
// Make sure we are in a 2's complement system.
GOOGLE_COMPILE_ASSERT(kint64min == GOOGLE_LONGLONG(-0x8000000000000000),
kint64min_value_error);
return "GOOGLE_LONGLONG(-0x8000000000000000)";
}
return "GOOGLE_LONGLONG(" + SimpleItoa(field->default_value_int64()) + ")";
return Int64ToString(field->default_value_int64());
case FieldDescriptor::CPPTYPE_UINT64:
return "GOOGLE_ULONGLONG(" + SimpleItoa(field->default_value_uint64())+ ")";
case FieldDescriptor::CPPTYPE_DOUBLE: {
@ -319,7 +327,7 @@ string DefaultValue(const FieldDescriptor* field) {
return strings::Substitute(
"static_cast< $0 >($1)",
ClassName(field->enum_type(), true),
field->default_value_enum()->number());
Int32ToString(field->default_value_enum()->number()));
case FieldDescriptor::CPPTYPE_STRING:
return "\"" + EscapeTrigraphs(
CEscape(field->default_value_string())) +
@ -366,11 +374,39 @@ string GlobalShutdownFileName(const string& filename) {
return "protobuf_ShutdownFile_" + FilenameIdentifier(filename);
}
// Return the qualified C++ name for a file level symbol.
string QualifiedFileLevelSymbol(const string& package, const string& name) {
if (package.empty()) {
return StrCat("::", name);
}
return StrCat("::", DotsToColons(package), "::", name);
}
// Escape C++ trigraphs by escaping question marks to \?
string EscapeTrigraphs(const string& to_escape) {
return StringReplace(to_escape, "?", "\\?", true);
}
// Escaped function name to eliminate naming conflict.
string SafeFunctionName(const Descriptor* descriptor,
const FieldDescriptor* field,
const string& prefix) {
// Do not use FieldName() since it will escape keywords.
string name = field->name();
LowerString(&name);
string function_name = prefix + name;
if (descriptor->FindFieldByName(function_name)) {
// Single underscore will also make it conflicting with the private data
// member. We use double underscore to escape function names.
function_name.append("__");
} else if (kKeywords.count(name) > 0) {
// If the field name is a keyword, we append the underscore back to keep it
// consistent with other function names.
function_name.append("_");
}
return function_name;
}
bool StaticInitializersForced(const FileDescriptor* file) {
if (HasDescriptorMethods(file) || file->extension_count() > 0) {
return true;
@ -432,6 +468,26 @@ bool HasEnumDefinitions(const FileDescriptor* file) {
return false;
}
bool IsStringOrMessage(const FieldDescriptor* field) {
switch (field->cpp_type()) {
case FieldDescriptor::CPPTYPE_INT32:
case FieldDescriptor::CPPTYPE_INT64:
case FieldDescriptor::CPPTYPE_UINT32:
case FieldDescriptor::CPPTYPE_UINT64:
case FieldDescriptor::CPPTYPE_DOUBLE:
case FieldDescriptor::CPPTYPE_FLOAT:
case FieldDescriptor::CPPTYPE_BOOL:
case FieldDescriptor::CPPTYPE_ENUM:
return false;
case FieldDescriptor::CPPTYPE_STRING:
case FieldDescriptor::CPPTYPE_MESSAGE:
return true;
}
GOOGLE_LOG(FATAL) << "Can't get here.";
return false;
}
} // namespace cpp
} // namespace compiler
} // namespace protobuf

@ -103,6 +103,12 @@ const char* PrimitiveTypeName(FieldDescriptor::CppType type);
// methods of WireFormat. For example, TYPE_INT32 becomes "Int32".
const char* DeclaredTypeMethodName(FieldDescriptor::Type type);
// Return the code that evaluates to the number when compiled.
string Int32ToString(int number);
// Return the code that evaluates to the number when compiled.
string Int64ToString(int64 number);
// Get code that evaluates to the field's default value.
string DefaultValue(const FieldDescriptor* field);
@ -115,14 +121,23 @@ string GlobalAddDescriptorsName(const string& filename);
// Return the name of the AssignDescriptors() function for a given file.
string GlobalAssignDescriptorsName(const string& filename);
// Return the qualified C++ name for a file level symbol.
string QualifiedFileLevelSymbol(const string& package, const string& name);
// Return the name of the ShutdownFile() function for a given file.
string GlobalShutdownFileName(const string& filename);
// Escape C++ trigraphs by escaping question marks to \?
string EscapeTrigraphs(const string& to_escape);
// Do message classes in this file keep track of unknown fields?
inline bool HasUnknownFields(const FileDescriptor* file) {
// Escaped function name to eliminate naming conflict.
string SafeFunctionName(const Descriptor* descriptor,
const FieldDescriptor* field,
const string& prefix);
// Do message classes in this file use UnknownFieldSet?
// Otherwise, messages will store unknown fields in a string
inline bool UseUnknownFieldSet(const FileDescriptor* file) {
return file->options().optimize_for() != FileOptions::LITE_RUNTIME;
}
@ -178,6 +193,11 @@ void PrintHandlingOptionalStaticInitializers(
const char* without_static_init);
// Returns true if the field's CPPTYPE is string or message.
bool IsStringOrMessage(const FieldDescriptor* field);
string UnderscoresToCamelCase(const string& input, bool cap_next_letter);
} // namespace cpp
} // namespace compiler
} // namespace protobuf

File diff suppressed because it is too large Load Diff

@ -35,8 +35,9 @@
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_H__
#include <memory>
#include <string>
#include <google/protobuf/stubs/common.h>
#include <vector>
#include <google/protobuf/compiler/cpp/cpp_field.h>
#include <google/protobuf/compiler/cpp/cpp_options.h>
@ -132,6 +133,7 @@ class MessageGenerator {
// Generate standard Message methods.
void GenerateClear(io::Printer* printer);
void GenerateOneofClear(io::Printer* printer);
void GenerateMergeFromCodedStream(io::Printer* printer);
void GenerateSerializeWithCachedSizes(io::Printer* printer);
void GenerateSerializeWithCachedSizesToArray(io::Printer* printer);
@ -156,9 +158,11 @@ class MessageGenerator {
string classname_;
Options options_;
FieldGeneratorMap field_generators_;
vector< vector<string> > runs_of_fields_; // that might be trivially cleared
scoped_array<scoped_ptr<MessageGenerator> > nested_generators_;
scoped_array<scoped_ptr<EnumGenerator> > enum_generators_;
scoped_array<scoped_ptr<ExtensionGenerator> > extension_generators_;
bool uses_string_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageGenerator);
};

@ -53,6 +53,12 @@ void SetMessageVariables(const FieldDescriptor* descriptor,
(HasFastArraySerialization(descriptor->message_type()->file()) ?
"MaybeToArray" :
"");
// NOTE: Escaped here to unblock proto1->proto2 migration.
// TODO(liujisi): Extend this to apply for other conflicting methods.
(*variables)["release_name"] =
SafeFunctionName(descriptor->containing_type(),
descriptor, "release_");
(*variables)["full_name"] = descriptor->full_name();
}
} // namespace
@ -78,14 +84,15 @@ GenerateAccessorDeclarations(io::Printer* printer) const {
printer->Print(variables_,
"inline const $type$& $name$() const$deprecation$;\n"
"inline $type$* mutable_$name$()$deprecation$;\n"
"inline $type$* release_$name$()$deprecation$;\n"
"inline $type$* $release_name$()$deprecation$;\n"
"inline void set_allocated_$name$($type$* $name$)$deprecation$;\n");
}
void MessageFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer) const {
printer->Print(variables_,
"inline const $type$& $classname$::$name$() const {\n");
"inline const $type$& $classname$::$name$() const {\n"
" // @@protoc_insertion_point(field_get:$full_name$)\n");
PrintHandlingOptionalStaticInitializers(
variables_, descriptor_->file(), printer,
@ -99,9 +106,10 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const {
"inline $type$* $classname$::mutable_$name$() {\n"
" set_has_$name$();\n"
" if ($name$_ == NULL) $name$_ = new $type$;\n"
" // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return $name$_;\n"
"}\n"
"inline $type$* $classname$::release_$name$() {\n"
"inline $type$* $classname$::$release_name$() {\n"
" clear_has_$name$();\n"
" $type$* temp = $name$_;\n"
" $name$_ = NULL;\n"
@ -115,6 +123,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const {
" } else {\n"
" clear_has_$name$();\n"
" }\n"
" // @@protoc_insertion_point(field_set_allocated:$full_name$)\n"
"}\n");
}
@ -178,6 +187,69 @@ GenerateByteSize(io::Printer* printer) const {
// ===================================================================
MessageOneofFieldGenerator::
MessageOneofFieldGenerator(const FieldDescriptor* descriptor,
const Options& options)
: MessageFieldGenerator(descriptor, options) {
SetCommonOneofFieldVariables(descriptor, &variables_);
}
MessageOneofFieldGenerator::~MessageOneofFieldGenerator() {}
void MessageOneofFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer) const {
printer->Print(variables_,
"inline const $type$& $classname$::$name$() const {\n"
" return has_$name$() ? *$oneof_prefix$$name$_\n"
" : $type$::default_instance();\n"
"}\n"
"inline $type$* $classname$::mutable_$name$() {\n"
" if (!has_$name$()) {\n"
" clear_$oneof_name$();\n"
" set_has_$name$();\n"
" $oneof_prefix$$name$_ = new $type$;\n"
" }\n"
" return $oneof_prefix$$name$_;\n"
"}\n"
"inline $type$* $classname$::$release_name$() {\n"
" if (has_$name$()) {\n"
" clear_has_$oneof_name$();\n"
" $type$* temp = $oneof_prefix$$name$_;\n"
" $oneof_prefix$$name$_ = NULL;\n"
" return temp;\n"
" } else {\n"
" return NULL;\n"
" }\n"
"}\n"
"inline void $classname$::set_allocated_$name$($type$* $name$) {\n"
" clear_$oneof_name$();\n"
" if ($name$) {\n"
" set_has_$name$();\n"
" $oneof_prefix$$name$_ = $name$;\n"
" }\n"
"}\n");
}
void MessageOneofFieldGenerator::
GenerateClearingCode(io::Printer* printer) const {
// if it is the active field, it cannot be NULL.
printer->Print(variables_,
"delete $oneof_prefix$$name$_;\n");
}
void MessageOneofFieldGenerator::
GenerateSwappingCode(io::Printer* printer) const {
// Don't print any swapping code. Swapping the union will swap this field.
}
void MessageOneofFieldGenerator::
GenerateConstructorCode(io::Printer* printer) const {
// Don't print any constructor code. The field is in a union. We allocate
// space only when this field is used.
}
// ===================================================================
RepeatedMessageFieldGenerator::
RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor,
const Options& options)
@ -210,21 +282,26 @@ void RepeatedMessageFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer) const {
printer->Print(variables_,
"inline const $type$& $classname$::$name$(int index) const {\n"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return $name$_.$cppget$(index);\n"
"}\n"
"inline $type$* $classname$::mutable_$name$(int index) {\n"
" // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return $name$_.Mutable(index);\n"
"}\n"
"inline $type$* $classname$::add_$name$() {\n"
" // @@protoc_insertion_point(field_add:$full_name$)\n"
" return $name$_.Add();\n"
"}\n");
printer->Print(variables_,
"inline const ::google::protobuf::RepeatedPtrField< $type$ >&\n"
"$classname$::$name$() const {\n"
" // @@protoc_insertion_point(field_list:$full_name$)\n"
" return $name$_;\n"
"}\n"
"inline ::google::protobuf::RepeatedPtrField< $type$ >*\n"
"$classname$::mutable_$name$() {\n"
" // @@protoc_insertion_point(field_mutable_list:$full_name$)\n"
" return &$name$_;\n"
"}\n");
}

@ -63,13 +63,30 @@ class MessageFieldGenerator : public FieldGenerator {
void GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const;
void GenerateByteSize(io::Printer* printer) const;
private:
protected:
const FieldDescriptor* descriptor_;
map<string, string> variables_;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFieldGenerator);
};
class MessageOneofFieldGenerator : public MessageFieldGenerator {
public:
explicit MessageOneofFieldGenerator(const FieldDescriptor* descriptor,
const Options& options);
~MessageOneofFieldGenerator();
// implements FieldGenerator ---------------------------------------
void GenerateInlineAccessorDefinitions(io::Printer* printer) const;
void GenerateClearingCode(io::Printer* printer) const;
void GenerateSwappingCode(io::Printer* printer) const;
void GenerateConstructorCode(io::Printer* printer) const;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageOneofFieldGenerator);
};
class RepeatedMessageFieldGenerator : public FieldGenerator {
public:
explicit RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor,

@ -34,6 +34,8 @@
// It seemed like parameterizing it would add more complexity than it is
// worth.
#include <memory>
#include <google/protobuf/compiler/cpp/cpp_generator.h>
#include <google/protobuf/compiler/command_line_interface.h>
#include <google/protobuf/io/zero_copy_stream.h>
@ -73,7 +75,7 @@ class TestGenerator : public CodeGenerator {
void TryInsert(const string& filename, const string& insertion_point,
GeneratorContext* context) const {
scoped_ptr<io::ZeroCopyOutputStream> output(
context->OpenForInsert(filename, insertion_point));
context->OpenForInsert(filename, insertion_point));
io::Printer printer(output.get(), '$');
printer.Print("// inserted $name$\n", "name", insertion_point);
}
@ -83,13 +85,13 @@ class TestGenerator : public CodeGenerator {
// not verify that they are correctly-placed; that would require actually
// compiling the output which is a bit more than I care to do for this test.
TEST(CppPluginTest, PluginTest) {
File::WriteStringToFileOrDie(
"syntax = \"proto2\";\n"
"package foo;\n"
"message Bar {\n"
" message Baz {}\n"
"}\n",
TestTempDir() + "/test.proto");
GOOGLE_CHECK_OK(File::SetContents(TestTempDir() + "/test.proto",
"syntax = \"proto2\";\n"
"package foo;\n"
"message Bar {\n"
" message Baz {}\n"
"}\n",
true));
google::protobuf::compiler::CommandLineInterface cli;
cli.SetInputsAreProtoPathRelative(true);

@ -93,6 +93,7 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor,
(*variables)["wire_format_field_type"] =
"::google::protobuf::internal::WireFormatLite::" + FieldDescriptorProto_Type_Name(
static_cast<FieldDescriptorProto_Type>(descriptor->type()));
(*variables)["full_name"] = descriptor->full_name();
}
} // namespace
@ -124,11 +125,13 @@ void PrimitiveFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer) const {
printer->Print(variables_,
"inline $type$ $classname$::$name$() const {\n"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return $name$_;\n"
"}\n"
"inline void $classname$::set_$name$($type$ value) {\n"
" set_has_$name$();\n"
" $name$_ = value;\n"
" // @@protoc_insertion_point(field_set:$full_name$)\n"
"}\n");
}
@ -191,6 +194,62 @@ GenerateByteSize(io::Printer* printer) const {
// ===================================================================
PrimitiveOneofFieldGenerator::
PrimitiveOneofFieldGenerator(const FieldDescriptor* descriptor,
const Options& options)
: PrimitiveFieldGenerator(descriptor, options) {
SetCommonOneofFieldVariables(descriptor, &variables_);
}
PrimitiveOneofFieldGenerator::~PrimitiveOneofFieldGenerator() {}
void PrimitiveOneofFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer) const {
printer->Print(variables_,
"inline $type$ $classname$::$name$() const {\n"
" if (has_$name$()) {\n"
" return $oneof_prefix$$name$_;\n"
" }\n"
" return $default$;\n"
"}\n"
"inline void $classname$::set_$name$($type$ value) {\n"
" if (!has_$name$()) {\n"
" clear_$oneof_name$();\n"
" set_has_$name$();\n"
" }\n"
" $oneof_prefix$$name$_ = value;\n"
"}\n");
}
void PrimitiveOneofFieldGenerator::
GenerateClearingCode(io::Printer* printer) const {
printer->Print(variables_, "$oneof_prefix$$name$_ = $default$;\n");
}
void PrimitiveOneofFieldGenerator::
GenerateSwappingCode(io::Printer* printer) const {
// Don't print any swapping code. Swapping the union will swap this field.
}
void PrimitiveOneofFieldGenerator::
GenerateConstructorCode(io::Printer* printer) const {
printer->Print(
variables_,
" $classname$_default_oneof_instance_->$name$_ = $default$;\n");
}
void PrimitiveOneofFieldGenerator::
GenerateMergeFromCodedStream(io::Printer* printer) const {
printer->Print(variables_,
"clear_$oneof_name$();\n"
"DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<\n"
" $type$, $wire_format_field_type$>(\n"
" input, &$oneof_prefix$$name$_)));\n"
"set_has_$name$();\n");
}
// ===================================================================
RepeatedPrimitiveFieldGenerator::
RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor,
const Options& options)
@ -235,21 +294,26 @@ void RepeatedPrimitiveFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer) const {
printer->Print(variables_,
"inline $type$ $classname$::$name$(int index) const {\n"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return $name$_.Get(index);\n"
"}\n"
"inline void $classname$::set_$name$(int index, $type$ value) {\n"
" $name$_.Set(index, value);\n"
" // @@protoc_insertion_point(field_set:$full_name$)\n"
"}\n"
"inline void $classname$::add_$name$($type$ value) {\n"
" $name$_.Add(value);\n"
" // @@protoc_insertion_point(field_add:$full_name$)\n"
"}\n");
printer->Print(variables_,
"inline const ::google::protobuf::RepeatedField< $type$ >&\n"
"$classname$::$name$() const {\n"
" // @@protoc_insertion_point(field_list:$full_name$)\n"
" return $name$_;\n"
"}\n"
"inline ::google::protobuf::RepeatedField< $type$ >*\n"
"$classname$::mutable_$name$() {\n"
" // @@protoc_insertion_point(field_mutable_list:$full_name$)\n"
" return &$name$_;\n"
"}\n");
}

@ -63,13 +63,31 @@ class PrimitiveFieldGenerator : public FieldGenerator {
void GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const;
void GenerateByteSize(io::Printer* printer) const;
private:
protected:
const FieldDescriptor* descriptor_;
map<string, string> variables_;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PrimitiveFieldGenerator);
};
class PrimitiveOneofFieldGenerator : public PrimitiveFieldGenerator {
public:
explicit PrimitiveOneofFieldGenerator(const FieldDescriptor* descriptor,
const Options& options);
~PrimitiveOneofFieldGenerator();
// implements FieldGenerator ---------------------------------------
void GenerateInlineAccessorDefinitions(io::Printer* printer) const;
void GenerateClearingCode(io::Printer* printer) const;
void GenerateSwappingCode(io::Printer* printer) const;
void GenerateConstructorCode(io::Printer* printer) const;
void GenerateMergeFromCodedStream(io::Printer* printer) const;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PrimitiveOneofFieldGenerator);
};
class RepeatedPrimitiveFieldGenerator : public FieldGenerator {
public:
explicit RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor,

@ -37,7 +37,6 @@
#include <map>
#include <string>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/compiler/cpp/cpp_options.h>
#include <google/protobuf/descriptor.h>

@ -53,10 +53,16 @@ void SetStringVariables(const FieldDescriptor* descriptor,
(*variables)["default_length"] =
SimpleItoa(descriptor->default_value_string().length());
(*variables)["default_variable"] = descriptor->default_value_string().empty()
? "&::google::protobuf::internal::GetEmptyString()"
? "&::google::protobuf::internal::GetEmptyStringAlreadyInited()"
: "_default_" + FieldName(descriptor) + "_";
(*variables)["pointer_type"] =
descriptor->type() == FieldDescriptor::TYPE_BYTES ? "void" : "char";
// NOTE: Escaped here to unblock proto1->proto2 migration.
// TODO(liujisi): Extend this to apply for other conflicting methods.
(*variables)["release_name"] =
SafeFunctionName(descriptor->containing_type(),
descriptor, "release_");
(*variables)["full_name"] = descriptor->full_name();
}
} // namespace
@ -75,6 +81,10 @@ StringFieldGenerator::~StringFieldGenerator() {}
void StringFieldGenerator::
GeneratePrivateMembers(io::Printer* printer) const {
printer->Print(variables_, "::std::string* $name$_;\n");
}
void StringFieldGenerator::
GenerateStaticMembers(io::Printer* printer) const {
if (!descriptor_->default_value_string().empty()) {
printer->Print(variables_, "static ::std::string* $default_variable$;\n");
}
@ -113,7 +123,7 @@ GenerateAccessorDeclarations(io::Printer* printer) const {
"inline void set_$name$(const $pointer_type$* value, size_t size)"
"$deprecation$;\n"
"inline ::std::string* mutable_$name$()$deprecation$;\n"
"inline ::std::string* release_$name$()$deprecation$;\n"
"inline ::std::string* $release_name$()$deprecation$;\n"
"inline void set_allocated_$name$(::std::string* $name$)$deprecation$;\n");
@ -128,6 +138,7 @@ void StringFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer) const {
printer->Print(variables_,
"inline const ::std::string& $classname$::$name$() const {\n"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return *$name$_;\n"
"}\n"
"inline void $classname$::set_$name$(const ::std::string& value) {\n"
@ -136,6 +147,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const {
" $name$_ = new ::std::string;\n"
" }\n"
" $name$_->assign(value);\n"
" // @@protoc_insertion_point(field_set:$full_name$)\n"
"}\n"
"inline void $classname$::set_$name$(const char* value) {\n"
" set_has_$name$();\n"
@ -143,6 +155,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const {
" $name$_ = new ::std::string;\n"
" }\n"
" $name$_->assign(value);\n"
" // @@protoc_insertion_point(field_set_char:$full_name$)\n"
"}\n"
"inline "
"void $classname$::set_$name$(const $pointer_type$* value, size_t size) {\n"
@ -151,6 +164,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const {
" $name$_ = new ::std::string;\n"
" }\n"
" $name$_->assign(reinterpret_cast<const char*>(value), size);\n"
" // @@protoc_insertion_point(field_set_pointer:$full_name$)\n"
"}\n"
"inline ::std::string* $classname$::mutable_$name$() {\n"
" set_has_$name$();\n"
@ -164,9 +178,10 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const {
}
printer->Print(variables_,
" }\n"
" // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return $name$_;\n"
"}\n"
"inline ::std::string* $classname$::release_$name$() {\n"
"inline ::std::string* $classname$::$release_name$() {\n"
" clear_has_$name$();\n"
" if ($name$_ == $default_variable$) {\n"
" return NULL;\n"
@ -187,6 +202,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const {
" clear_has_$name$();\n"
" $name$_ = const_cast< ::std::string*>($default_variable$);\n"
" }\n"
" // @@protoc_insertion_point(field_set_allocated:$full_name$)\n"
"}\n");
}
@ -263,9 +279,10 @@ GenerateMergeFromCodedStream(io::Printer* printer) const {
if (HasUtf8Verification(descriptor_->file()) &&
descriptor_->type() == FieldDescriptor::TYPE_STRING) {
printer->Print(variables_,
"::google::protobuf::internal::WireFormat::VerifyUTF8String(\n"
"::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n"
" this->$name$().data(), this->$name$().length(),\n"
" ::google::protobuf::internal::WireFormat::PARSE);\n");
" ::google::protobuf::internal::WireFormat::PARSE,\n"
" \"$name$\");\n");
}
}
@ -274,12 +291,13 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const {
if (HasUtf8Verification(descriptor_->file()) &&
descriptor_->type() == FieldDescriptor::TYPE_STRING) {
printer->Print(variables_,
"::google::protobuf::internal::WireFormat::VerifyUTF8String(\n"
"::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n"
" this->$name$().data(), this->$name$().length(),\n"
" ::google::protobuf::internal::WireFormat::SERIALIZE);\n");
" ::google::protobuf::internal::WireFormat::SERIALIZE,\n"
" \"$name$\");\n");
}
printer->Print(variables_,
"::google::protobuf::internal::WireFormatLite::Write$declared_type$(\n"
"::google::protobuf::internal::WireFormatLite::Write$declared_type$MaybeAliased(\n"
" $number$, this->$name$(), output);\n");
}
@ -288,9 +306,10 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const {
if (HasUtf8Verification(descriptor_->file()) &&
descriptor_->type() == FieldDescriptor::TYPE_STRING) {
printer->Print(variables_,
"::google::protobuf::internal::WireFormat::VerifyUTF8String(\n"
"::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n"
" this->$name$().data(), this->$name$().length(),\n"
" ::google::protobuf::internal::WireFormat::SERIALIZE);\n");
" ::google::protobuf::internal::WireFormat::SERIALIZE,\n"
" \"$name$\");\n");
}
printer->Print(variables_,
"target =\n"
@ -308,6 +327,125 @@ GenerateByteSize(io::Printer* printer) const {
// ===================================================================
StringOneofFieldGenerator::
StringOneofFieldGenerator(const FieldDescriptor* descriptor,
const Options& options)
: StringFieldGenerator(descriptor, options) {
SetCommonOneofFieldVariables(descriptor, &variables_);
}
StringOneofFieldGenerator::~StringOneofFieldGenerator() {}
void StringOneofFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer) const {
printer->Print(variables_,
"inline const ::std::string& $classname$::$name$() const {\n"
" if (has_$name$()) {\n"
" return *$oneof_prefix$$name$_;\n"
" }\n");
if (descriptor_->default_value_string().empty()) {
printer->Print(variables_,
" return ::google::protobuf::internal::GetEmptyStringAlreadyInited();\n");
} else {
printer->Print(variables_,
" return *$default_variable$;\n");
}
printer->Print(variables_,
"}\n"
"inline void $classname$::set_$name$(const ::std::string& value) {\n"
" if (!has_$name$()) {\n"
" clear_$oneof_name$();\n"
" set_has_$name$();\n"
" $oneof_prefix$$name$_ = new ::std::string;\n"
" }\n"
" $oneof_prefix$$name$_->assign(value);\n"
"}\n"
"inline void $classname$::set_$name$(const char* value) {\n"
" if (!has_$name$()) {\n"
" clear_$oneof_name$();\n"
" set_has_$name$();\n"
" $oneof_prefix$$name$_ = new ::std::string;\n"
" }\n"
" $oneof_prefix$$name$_->assign(value);\n"
"}\n"
"inline "
"void $classname$::set_$name$(const $pointer_type$* value, size_t size) {\n"
" if (!has_$name$()) {\n"
" clear_$oneof_name$();\n"
" set_has_$name$();\n"
" $oneof_prefix$$name$_ = new ::std::string;\n"
" }\n"
" $oneof_prefix$$name$_->assign(\n"
" reinterpret_cast<const char*>(value), size);\n"
"}\n"
"inline ::std::string* $classname$::mutable_$name$() {\n"
" if (!has_$name$()) {\n"
" clear_$oneof_name$();\n"
" set_has_$name$();\n");
if (descriptor_->default_value_string().empty()) {
printer->Print(variables_,
" $oneof_prefix$$name$_ = new ::std::string;\n");
} else {
printer->Print(variables_,
" $oneof_prefix$$name$_ = new ::std::string(*$default_variable$);\n");
}
printer->Print(variables_,
" }\n"
" return $oneof_prefix$$name$_;\n"
"}\n"
"inline ::std::string* $classname$::$release_name$() {\n"
" if (has_$name$()) {\n"
" clear_has_$oneof_name$();\n"
" ::std::string* temp = $oneof_prefix$$name$_;\n"
" $oneof_prefix$$name$_ = NULL;\n"
" return temp;\n"
" } else {\n"
" return NULL;\n"
" }\n"
"}\n"
"inline void $classname$::set_allocated_$name$(::std::string* $name$) {\n"
" clear_$oneof_name$();\n"
" if ($name$) {\n"
" set_has_$name$();\n"
" $oneof_prefix$$name$_ = $name$;\n"
" }\n"
"}\n");
}
void StringOneofFieldGenerator::
GenerateClearingCode(io::Printer* printer) const {
printer->Print(variables_,
"delete $oneof_prefix$$name$_;\n");
}
void StringOneofFieldGenerator::
GenerateSwappingCode(io::Printer* printer) const {
// Don't print any swapping code. Swapping the union will swap this field.
}
void StringOneofFieldGenerator::
GenerateConstructorCode(io::Printer* printer) const {
if (!descriptor_->default_value_string().empty()) {
printer->Print(variables_,
" $classname$_default_oneof_instance_->$name$_ = "
"$classname$::$default_variable$;\n");
} else {
printer->Print(variables_,
" $classname$_default_oneof_instance_->$name$_ = "
"$default_variable$;\n");
}
}
void StringOneofFieldGenerator::
GenerateDestructorCode(io::Printer* printer) const {
printer->Print(variables_,
"if (has_$name$()) {\n"
" delete $oneof_prefix$$name$_;\n"
"}\n");
}
// ===================================================================
RepeatedStringFieldGenerator::
RepeatedStringFieldGenerator(const FieldDescriptor* descriptor,
const Options& options)
@ -365,43 +503,53 @@ void RepeatedStringFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer) const {
printer->Print(variables_,
"inline const ::std::string& $classname$::$name$(int index) const {\n"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return $name$_.$cppget$(index);\n"
"}\n"
"inline ::std::string* $classname$::mutable_$name$(int index) {\n"
" // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return $name$_.Mutable(index);\n"
"}\n"
"inline void $classname$::set_$name$(int index, const ::std::string& value) {\n"
" // @@protoc_insertion_point(field_set:$full_name$)\n"
" $name$_.Mutable(index)->assign(value);\n"
"}\n"
"inline void $classname$::set_$name$(int index, const char* value) {\n"
" $name$_.Mutable(index)->assign(value);\n"
" // @@protoc_insertion_point(field_set_char:$full_name$)\n"
"}\n"
"inline void "
"$classname$::set_$name$"
"(int index, const $pointer_type$* value, size_t size) {\n"
" $name$_.Mutable(index)->assign(\n"
" reinterpret_cast<const char*>(value), size);\n"
" // @@protoc_insertion_point(field_set_pointer:$full_name$)\n"
"}\n"
"inline ::std::string* $classname$::add_$name$() {\n"
" return $name$_.Add();\n"
"}\n"
"inline void $classname$::add_$name$(const ::std::string& value) {\n"
" $name$_.Add()->assign(value);\n"
" // @@protoc_insertion_point(field_add:$full_name$)\n"
"}\n"
"inline void $classname$::add_$name$(const char* value) {\n"
" $name$_.Add()->assign(value);\n"
" // @@protoc_insertion_point(field_add_char:$full_name$)\n"
"}\n"
"inline void "
"$classname$::add_$name$(const $pointer_type$* value, size_t size) {\n"
" $name$_.Add()->assign(reinterpret_cast<const char*>(value), size);\n"
" // @@protoc_insertion_point(field_add_pointer:$full_name$)\n"
"}\n");
printer->Print(variables_,
"inline const ::google::protobuf::RepeatedPtrField< ::std::string>&\n"
"$classname$::$name$() const {\n"
" // @@protoc_insertion_point(field_list:$full_name$)\n"
" return $name$_;\n"
"}\n"
"inline ::google::protobuf::RepeatedPtrField< ::std::string>*\n"
"$classname$::mutable_$name$() {\n"
" // @@protoc_insertion_point(field_mutable_list:$full_name$)\n"
" return &$name$_;\n"
"}\n");
}
@ -434,10 +582,11 @@ GenerateMergeFromCodedStream(io::Printer* printer) const {
if (HasUtf8Verification(descriptor_->file()) &&
descriptor_->type() == FieldDescriptor::TYPE_STRING) {
printer->Print(variables_,
"::google::protobuf::internal::WireFormat::VerifyUTF8String(\n"
"::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n"
" this->$name$(this->$name$_size() - 1).data(),\n"
" this->$name$(this->$name$_size() - 1).length(),\n"
" ::google::protobuf::internal::WireFormat::PARSE);\n");
" ::google::protobuf::internal::WireFormat::PARSE,\n"
" \"$name$\");\n");
}
}
@ -448,9 +597,10 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const {
if (HasUtf8Verification(descriptor_->file()) &&
descriptor_->type() == FieldDescriptor::TYPE_STRING) {
printer->Print(variables_,
"::google::protobuf::internal::WireFormat::VerifyUTF8String(\n"
"::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n"
" this->$name$(i).data(), this->$name$(i).length(),\n"
" ::google::protobuf::internal::WireFormat::SERIALIZE);\n");
" ::google::protobuf::internal::WireFormat::SERIALIZE,\n"
" \"$name$\");\n");
}
printer->Print(variables_,
" ::google::protobuf::internal::WireFormatLite::Write$declared_type$(\n"
@ -465,9 +615,10 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const {
if (HasUtf8Verification(descriptor_->file()) &&
descriptor_->type() == FieldDescriptor::TYPE_STRING) {
printer->Print(variables_,
" ::google::protobuf::internal::WireFormat::VerifyUTF8String(\n"
" ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n"
" this->$name$(i).data(), this->$name$(i).length(),\n"
" ::google::protobuf::internal::WireFormat::SERIALIZE);\n");
" ::google::protobuf::internal::WireFormat::SERIALIZE,\n"
" \"$name$\");\n");
}
printer->Print(variables_,
" target = ::google::protobuf::internal::WireFormatLite::\n"

@ -52,6 +52,7 @@ class StringFieldGenerator : public FieldGenerator {
// implements FieldGenerator ---------------------------------------
void GeneratePrivateMembers(io::Printer* printer) const;
void GenerateStaticMembers(io::Printer* printer) const;
void GenerateAccessorDeclarations(io::Printer* printer) const;
void GenerateInlineAccessorDefinitions(io::Printer* printer) const;
void GenerateNonInlineAccessorDefinitions(io::Printer* printer) const;
@ -67,13 +68,31 @@ class StringFieldGenerator : public FieldGenerator {
void GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const;
void GenerateByteSize(io::Printer* printer) const;
private:
protected:
const FieldDescriptor* descriptor_;
map<string, string> variables_;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringFieldGenerator);
};
class StringOneofFieldGenerator : public StringFieldGenerator {
public:
explicit StringOneofFieldGenerator(const FieldDescriptor* descriptor,
const Options& options);
~StringOneofFieldGenerator();
// implements FieldGenerator ---------------------------------------
void GenerateInlineAccessorDefinitions(io::Printer* printer) const;
void GenerateClearingCode(io::Printer* printer) const;
void GenerateSwappingCode(io::Printer* printer) const;
void GenerateConstructorCode(io::Printer* printer) const;
void GenerateDestructorCode(io::Printer* printer) const;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringOneofFieldGenerator);
};
class RepeatedStringFieldGenerator : public FieldGenerator {
public:
explicit RepeatedStringFieldGenerator(const FieldDescriptor* descriptor,

@ -98,6 +98,7 @@ message TestConflictingSymbolNames {
// Some keywords.
optional uint32 int = 30;
optional uint32 friend = 31;
optional uint32 class = 37;
// The generator used to #define a macro called "DO" inside the .cc file.
message DO {}
@ -107,6 +108,14 @@ message TestConflictingSymbolNames {
optional int32 field_type = 33;
optional bool is_packed = 34;
// test conflicting release_$name$. "length" and "do" field in this message
// must remain string or message fields to make the test valid.
optional string release_length = 35;
// A more extreme case, the field name "do" here is a keyword, which will be
// escaped to "do_" already. Test there is no conflict even with escaped field
// names.
optional DO release_do = 36;
extensions 1000 to max;
}

@ -46,6 +46,7 @@
#include <google/protobuf/compiler/cpp/cpp_unittest.h>
#include <memory>
#include <vector>
#include <google/protobuf/unittest.pb.h>
@ -53,6 +54,7 @@
#include <google/protobuf/unittest_embed_optimize_for.pb.h>
#include <google/protobuf/unittest_no_generic_services.pb.h>
#include <google/protobuf/test_util.h>
#include <google/protobuf/compiler/cpp/cpp_helpers.h>
#include <google/protobuf/compiler/cpp/cpp_test_bad_identifiers.pb.h>
#include <google/protobuf/compiler/importer.h>
#include <google/protobuf/io/coded_stream.h>
@ -148,6 +150,19 @@ TEST(GeneratedMessageTest, Defaults) {
&message.optional_import_message());
}
TEST(GeneratedMessageTest, Int32StringConversion) {
EXPECT_EQ("971", Int32ToString(971));
EXPECT_EQ("(~0x7fffffff)", Int32ToString(kint32min));
EXPECT_EQ("2147483647", Int32ToString(kint32max));
}
TEST(GeneratedMessageTest, Int64StringConversion) {
EXPECT_EQ("GOOGLE_LONGLONG(971)", Int64ToString(971));
EXPECT_EQ("GOOGLE_LONGLONG(-2147483648)", Int64ToString(kint32min));
EXPECT_EQ("GOOGLE_LONGLONG(-0x8000000000000000)", Int64ToString(kint64min));
EXPECT_EQ("GOOGLE_LONGLONG(9223372036854775807)", Int64ToString(kint64max));
}
TEST(GeneratedMessageTest, FloatingPointDefaults) {
const unittest::TestExtremeDefaultValues& extreme_default =
unittest::TestExtremeDefaultValues::default_instance();
@ -233,11 +248,10 @@ TEST(GeneratedMessageTest, ReleaseString) {
message.set_default_string("blah");
EXPECT_TRUE(message.has_default_string());
string* str = message.release_default_string();
scoped_ptr<string> str(message.release_default_string());
EXPECT_FALSE(message.has_default_string());
ASSERT_TRUE(str != NULL);
EXPECT_EQ("blah", *str);
delete str;
EXPECT_EQ(NULL, message.release_default_string());
EXPECT_FALSE(message.has_default_string());
@ -253,12 +267,11 @@ TEST(GeneratedMessageTest, ReleaseMessage) {
EXPECT_FALSE(message.has_optional_nested_message());
message.mutable_optional_nested_message()->set_bb(1);
unittest::TestAllTypes::NestedMessage* nest =
message.release_optional_nested_message();
scoped_ptr<unittest::TestAllTypes::NestedMessage> nest(
message.release_optional_nested_message());
EXPECT_FALSE(message.has_optional_nested_message());
ASSERT_TRUE(nest != NULL);
EXPECT_EQ(1, nest->bb());
delete nest;
EXPECT_EQ(NULL, message.release_optional_nested_message());
EXPECT_FALSE(message.has_optional_nested_message());
@ -381,6 +394,7 @@ TEST(GeneratedMessageTest, StringCharStarLength) {
EXPECT_EQ("wx", message.repeated_string(0));
}
TEST(GeneratedMessageTest, CopyFrom) {
unittest::TestAllTypes message1, message2;
@ -393,6 +407,7 @@ TEST(GeneratedMessageTest, CopyFrom) {
TestUtil::ExpectAllFieldsSet(message2);
}
TEST(GeneratedMessageTest, SwapWithEmpty) {
unittest::TestAllTypes message1, message2;
TestUtil::SetAllFields(&message1);
@ -763,6 +778,9 @@ TEST(GeneratedMessageTest, TestConflictingSymbolNames) {
message.set_friend_(5);
EXPECT_EQ(5, message.friend_());
message.set_class_(6);
EXPECT_EQ(6, message.class_());
// Instantiate extension template functions to test conflicting template
// parameter names.
typedef protobuf_unittest::TestConflictingSymbolNamesExtension ExtensionMessage;
@ -840,6 +858,40 @@ TEST(GeneratedMessageTest, TestSpaceUsed) {
message1.SpaceUsed());
}
TEST(GeneratedMessageTest, TestOneofSpaceUsed) {
unittest::TestOneof2 message1;
EXPECT_LE(sizeof(unittest::TestOneof2), message1.SpaceUsed());
const int empty_message_size = message1.SpaceUsed();
// Setting primitive types shouldn't affect the space used.
message1.set_foo_int(123);
message1.set_bar_int(12345);
EXPECT_EQ(empty_message_size, message1.SpaceUsed());
// Setting a string in oneof to a small value should only increase SpaceUsed()
// by the size of a string object.
message1.set_foo_string("abc");
EXPECT_LE(empty_message_size + sizeof(string), message1.SpaceUsed());
// Setting a string in oneof to a value larger than the string object itself
// should increase SpaceUsed(), because it cannot store the value internally.
message1.set_foo_string(string(sizeof(string) + 1, 'x'));
int min_expected_increase = message1.foo_string().capacity() +
sizeof(string);
EXPECT_LE(empty_message_size + min_expected_increase,
message1.SpaceUsed());
// Setting a message in oneof should delete the other fields and increase the
// size by the size of the nested message type. NestedMessage is simple enough
// that it is equal to sizeof(NestedMessage)
message1.mutable_foo_message();
ASSERT_EQ(sizeof(unittest::TestOneof2::NestedMessage),
message1.foo_message().SpaceUsed());
EXPECT_EQ(empty_message_size +
sizeof(unittest::TestOneof2::NestedMessage),
message1.SpaceUsed());
}
#endif // !PROTOBUF_TEST_NO_DESCRIPTORS
@ -887,6 +939,9 @@ TEST(GeneratedEnumTest, EnumValuesAsSwitchCases) {
case unittest::TestAllTypes::BAZ:
i = 3;
break;
case unittest::TestAllTypes::NEG:
i = -1;
break;
// no default case: We want to make sure the compiler recognizes that
// all cases are covered. (GCC warns if you do not cover all cases of
// an enum in a switch.)
@ -915,7 +970,7 @@ TEST(GeneratedEnumTest, IsValidValue) {
}
TEST(GeneratedEnumTest, MinAndMax) {
EXPECT_EQ(unittest::TestAllTypes::FOO,
EXPECT_EQ(unittest::TestAllTypes::NEG,
unittest::TestAllTypes::NestedEnum_MIN);
EXPECT_EQ(unittest::TestAllTypes::BAZ,
unittest::TestAllTypes::NestedEnum_MAX);
@ -989,6 +1044,20 @@ TEST(GeneratedEnumTest, GetEnumDescriptor) {
GetEnumDescriptor<unittest::TestSparseEnum>());
}
enum NonProtoEnum {
kFoo = 1,
};
TEST(GeneratedEnumTest, IsProtoEnumTypeTrait) {
EXPECT_TRUE(is_proto_enum<unittest::TestAllTypes::NestedEnum>::value);
EXPECT_TRUE(is_proto_enum<unittest::ForeignEnum>::value);
EXPECT_TRUE(is_proto_enum<unittest::TestEnumWithDupValue>::value);
EXPECT_TRUE(is_proto_enum<unittest::TestSparseEnum>::value);
EXPECT_FALSE(is_proto_enum<int>::value);
EXPECT_FALSE(is_proto_enum<NonProtoEnum>::value);
}
#endif // PROTOBUF_TEST_NO_DESCRIPTORS
// ===================================================================
@ -1288,6 +1357,657 @@ TEST_F(GeneratedServiceTest, NotImplemented) {
EXPECT_TRUE(controller.called_);
}
// ===================================================================
class OneofTest : public testing::Test {
protected:
virtual void SetUp() {
}
void ExpectEnumCasesWork(const unittest::TestOneof2 &message) {
switch (message.foo_case()) {
case unittest::TestOneof2::kFooInt:
EXPECT_TRUE(message.has_foo_int());
break;
case unittest::TestOneof2::kFooString:
EXPECT_TRUE(message.has_foo_string());
break;
case unittest::TestOneof2::kFooBytes:
EXPECT_TRUE(message.has_foo_bytes());
break;
case unittest::TestOneof2::kFooEnum:
EXPECT_TRUE(message.has_foo_enum());
break;
case unittest::TestOneof2::kFooMessage:
EXPECT_TRUE(message.has_foo_message());
break;
case unittest::TestOneof2::kFoogroup:
EXPECT_TRUE(message.has_foogroup());
break;
case unittest::TestOneof2::FOO_NOT_SET:
break;
}
}
};
TEST_F(OneofTest, SettingOneFieldClearsOthers) {
unittest::TestOneof2 message;
message.set_foo_int(123);
EXPECT_TRUE(message.has_foo_int());
TestUtil::ExpectAtMostOneFieldSetInOneof(message);
message.set_foo_string("foo");
EXPECT_TRUE(message.has_foo_string());
TestUtil::ExpectAtMostOneFieldSetInOneof(message);
message.set_foo_bytes("qux");
EXPECT_TRUE(message.has_foo_bytes());
TestUtil::ExpectAtMostOneFieldSetInOneof(message);
message.set_foo_enum(unittest::TestOneof2::FOO);
EXPECT_TRUE(message.has_foo_enum());
TestUtil::ExpectAtMostOneFieldSetInOneof(message);
message.mutable_foo_message()->set_qux_int(234);
EXPECT_TRUE(message.has_foo_message());
TestUtil::ExpectAtMostOneFieldSetInOneof(message);
message.mutable_foogroup()->set_a(345);
EXPECT_TRUE(message.has_foogroup());
TestUtil::ExpectAtMostOneFieldSetInOneof(message);
// we repeat this because we didn't test if this properly clears other fields
// at the beginning.
message.set_foo_int(123);
EXPECT_TRUE(message.has_foo_int());
TestUtil::ExpectAtMostOneFieldSetInOneof(message);
}
TEST_F(OneofTest, EnumCases) {
unittest::TestOneof2 message;
message.set_foo_int(123);
ExpectEnumCasesWork(message);
message.set_foo_string("foo");
ExpectEnumCasesWork(message);
message.set_foo_bytes("qux");
ExpectEnumCasesWork(message);
message.set_foo_enum(unittest::TestOneof2::FOO);
ExpectEnumCasesWork(message);
message.mutable_foo_message()->set_qux_int(234);
ExpectEnumCasesWork(message);
message.mutable_foogroup()->set_a(345);
ExpectEnumCasesWork(message);
}
TEST_F(OneofTest, PrimitiveType) {
unittest::TestOneof2 message;
// Unset field returns default value
EXPECT_EQ(message.foo_int(), 0);
message.set_foo_int(123);
EXPECT_TRUE(message.has_foo_int());
EXPECT_EQ(message.foo_int(), 123);
message.clear_foo_int();
EXPECT_FALSE(message.has_foo_int());
}
TEST_F(OneofTest, EnumType) {
unittest::TestOneof2 message;
// Unset field returns default value
EXPECT_EQ(message.foo_enum(), 1);
message.set_foo_enum(unittest::TestOneof2::FOO);
EXPECT_TRUE(message.has_foo_enum());
EXPECT_EQ(message.foo_enum(), unittest::TestOneof2::FOO);
message.clear_foo_enum();
EXPECT_FALSE(message.has_foo_enum());
}
TEST_F(OneofTest, SetString) {
// Check that setting a string field in various ways works
unittest::TestOneof2 message;
// Unset field returns default value
EXPECT_EQ(message.foo_string(), "");
message.set_foo_string("foo");
EXPECT_TRUE(message.has_foo_string());
EXPECT_EQ(message.foo_string(), "foo");
message.clear_foo_string();
EXPECT_FALSE(message.has_foo_string());
message.set_foo_string(string("bar"));
EXPECT_TRUE(message.has_foo_string());
EXPECT_EQ(message.foo_string(), "bar");
message.clear_foo_string();
EXPECT_FALSE(message.has_foo_string());
message.set_foo_string("qux", 3);
EXPECT_TRUE(message.has_foo_string());
EXPECT_EQ(message.foo_string(), "qux");
message.clear_foo_string();
EXPECT_FALSE(message.has_foo_string());
message.mutable_foo_string()->assign("quux");
EXPECT_TRUE(message.has_foo_string());
EXPECT_EQ(message.foo_string(), "quux");
message.clear_foo_string();
EXPECT_FALSE(message.has_foo_string());
message.set_foo_string("corge");
EXPECT_TRUE(message.has_foo_string());
EXPECT_EQ(message.foo_string(), "corge");
message.clear_foo_string();
EXPECT_FALSE(message.has_foo_string());
}
TEST_F(OneofTest, ReleaseString) {
// Check that release_foo() starts out NULL, and gives us a value
// that we can delete after it's been set.
unittest::TestOneof2 message;
EXPECT_EQ(NULL, message.release_foo_string());
EXPECT_FALSE(message.has_foo_string());
message.set_foo_string("blah");
EXPECT_TRUE(message.has_foo_string());
scoped_ptr<string> str(message.release_foo_string());
EXPECT_FALSE(message.has_foo_string());
ASSERT_TRUE(str != NULL);
EXPECT_EQ("blah", *str);
EXPECT_EQ(NULL, message.release_foo_string());
EXPECT_FALSE(message.has_foo_string());
}
TEST_F(OneofTest, SetAllocatedString) {
// Check that set_allocated_foo() works for strings.
unittest::TestOneof2 message;
EXPECT_FALSE(message.has_foo_string());
const string kHello("hello");
message.set_foo_string(kHello);
EXPECT_TRUE(message.has_foo_string());
message.set_allocated_foo_string(NULL);
EXPECT_FALSE(message.has_foo_string());
EXPECT_EQ("", message.foo_string());
message.set_allocated_foo_string(new string(kHello));
EXPECT_TRUE(message.has_foo_string());
EXPECT_EQ(kHello, message.foo_string());
}
TEST_F(OneofTest, SetMessage) {
// Check that setting a message field works
unittest::TestOneof2 message;
// Unset field returns default instance
EXPECT_EQ(&message.foo_message(),
&unittest::TestOneof2_NestedMessage::default_instance());
EXPECT_EQ(message.foo_message().qux_int(), 0);
message.mutable_foo_message()->set_qux_int(234);
EXPECT_TRUE(message.has_foo_message());
EXPECT_EQ(message.foo_message().qux_int(), 234);
message.clear_foo_message();
EXPECT_FALSE(message.has_foo_message());
}
TEST_F(OneofTest, ReleaseMessage) {
// Check that release_foo() starts out NULL, and gives us a value
// that we can delete after it's been set.
unittest::TestOneof2 message;
EXPECT_EQ(NULL, message.release_foo_message());
EXPECT_FALSE(message.has_foo_message());
message.mutable_foo_message()->set_qux_int(1);
EXPECT_TRUE(message.has_foo_message());
scoped_ptr<unittest::TestOneof2_NestedMessage> mes(
message.release_foo_message());
EXPECT_FALSE(message.has_foo_message());
ASSERT_TRUE(mes != NULL);
EXPECT_EQ(1, mes->qux_int());
EXPECT_EQ(NULL, message.release_foo_message());
EXPECT_FALSE(message.has_foo_message());
}
TEST_F(OneofTest, SetAllocatedMessage) {
// Check that set_allocated_foo() works for messages.
unittest::TestOneof2 message;
EXPECT_FALSE(message.has_foo_message());
message.mutable_foo_message()->set_qux_int(1);
EXPECT_TRUE(message.has_foo_message());
message.set_allocated_foo_message(NULL);
EXPECT_FALSE(message.has_foo_message());
EXPECT_EQ(&message.foo_message(),
&unittest::TestOneof2_NestedMessage::default_instance());
message.mutable_foo_message()->set_qux_int(1);
unittest::TestOneof2_NestedMessage* mes = message.release_foo_message();
ASSERT_TRUE(mes != NULL);
EXPECT_FALSE(message.has_foo_message());
message.set_allocated_foo_message(mes);
EXPECT_TRUE(message.has_foo_message());
EXPECT_EQ(1, message.foo_message().qux_int());
}
TEST_F(OneofTest, Clear) {
unittest::TestOneof2 message;
message.set_foo_int(1);
EXPECT_TRUE(message.has_foo_int());
message.clear_foo_int();
EXPECT_FALSE(message.has_foo_int());
}
TEST_F(OneofTest, Defaults) {
unittest::TestOneof2 message;
EXPECT_FALSE(message.has_foo_int());
EXPECT_EQ(message.foo_int(), 0);
EXPECT_FALSE(message.has_foo_string());
EXPECT_EQ(message.foo_string(), "");
EXPECT_FALSE(message.has_foo_bytes());
EXPECT_EQ(message.foo_bytes(), "");
EXPECT_FALSE(message.has_foo_enum());
EXPECT_EQ(message.foo_enum(), 1);
EXPECT_FALSE(message.has_foo_message());
EXPECT_EQ(message.foo_message().qux_int(), 0);
EXPECT_FALSE(message.has_foogroup());
EXPECT_EQ(message.foogroup().a(), 0);
EXPECT_FALSE(message.has_bar_int());
EXPECT_EQ(message.bar_int(), 5);
EXPECT_FALSE(message.has_bar_string());
EXPECT_EQ(message.bar_string(), "STRING");
EXPECT_FALSE(message.has_bar_bytes());
EXPECT_EQ(message.bar_bytes(), "BYTES");
EXPECT_FALSE(message.has_bar_enum());
EXPECT_EQ(message.bar_enum(), 2);
}
TEST_F(OneofTest, SwapWithEmpty) {
unittest::TestOneof2 message1, message2;
message1.set_foo_string("FOO");
EXPECT_TRUE(message1.has_foo_string());
message1.Swap(&message2);
EXPECT_FALSE(message1.has_foo_string());
EXPECT_TRUE(message2.has_foo_string());
EXPECT_EQ(message2.foo_string(), "FOO");
}
TEST_F(OneofTest, SwapWithSelf) {
unittest::TestOneof2 message;
message.set_foo_string("FOO");
EXPECT_TRUE(message.has_foo_string());
message.Swap(&message);
EXPECT_TRUE(message.has_foo_string());
EXPECT_EQ(message.foo_string(), "FOO");
}
TEST_F(OneofTest, SwapBothHasFields) {
unittest::TestOneof2 message1, message2;
message1.set_foo_string("FOO");
EXPECT_TRUE(message1.has_foo_string());
message2.mutable_foo_message()->set_qux_int(1);
EXPECT_TRUE(message2.has_foo_message());
message1.Swap(&message2);
EXPECT_FALSE(message1.has_foo_string());
EXPECT_FALSE(message2.has_foo_message());
EXPECT_TRUE(message1.has_foo_message());
EXPECT_EQ(message1.foo_message().qux_int(), 1);
EXPECT_TRUE(message2.has_foo_string());
EXPECT_EQ(message2.foo_string(), "FOO");
}
TEST_F(OneofTest, CopyContructor) {
unittest::TestOneof2 message1;
message1.set_foo_bytes("FOO");
unittest::TestOneof2 message2(message1);
EXPECT_TRUE(message2.has_foo_bytes());
EXPECT_EQ(message2.foo_bytes(), "FOO");
}
TEST_F(OneofTest, CopyFrom) {
unittest::TestOneof2 message1, message2;
message1.set_foo_enum(unittest::TestOneof2::BAR);
EXPECT_TRUE(message1.has_foo_enum());
message2.CopyFrom(message1);
EXPECT_TRUE(message2.has_foo_enum());
EXPECT_EQ(message2.foo_enum(), unittest::TestOneof2::BAR);
// Copying from self should be a no-op.
message2.CopyFrom(message2);
EXPECT_TRUE(message2.has_foo_enum());
EXPECT_EQ(message2.foo_enum(), unittest::TestOneof2::BAR);
}
TEST_F(OneofTest, CopyAssignmentOperator) {
unittest::TestOneof2 message1;
message1.mutable_foo_message()->set_qux_int(123);
EXPECT_TRUE(message1.has_foo_message());
unittest::TestOneof2 message2;
message2 = message1;
EXPECT_EQ(message2.foo_message().qux_int(), 123);
// Make sure that self-assignment does something sane.
message2 = message2;
EXPECT_EQ(message2.foo_message().qux_int(), 123);
}
TEST_F(OneofTest, UpcastCopyFrom) {
// Test the CopyFrom method that takes in the generic const Message&
// parameter.
unittest::TestOneof2 message1, message2;
message1.mutable_foogroup()->set_a(123);
EXPECT_TRUE(message1.has_foogroup());
const Message* source = implicit_cast<const Message*>(&message1);
message2.CopyFrom(*source);
EXPECT_TRUE(message2.has_foogroup());
EXPECT_EQ(message2.foogroup().a(), 123);
}
// Test the generated SerializeWithCachedSizesToArray(),
// This indirectly tests MergePartialFromCodedStream()
// We have to test each field type separately because we cannot set them at the
// same time
TEST_F(OneofTest, SerializationToArray) {
// Primitive type
{
unittest::TestOneof2 message1, message2;
string data;
message1.set_foo_int(123);
int size = message1.ByteSize();
data.resize(size);
uint8* start = reinterpret_cast<uint8*>(string_as_array(&data));
uint8* end = message1.SerializeWithCachedSizesToArray(start);
EXPECT_EQ(size, end - start);
EXPECT_TRUE(message2.ParseFromString(data));
EXPECT_EQ(message2.foo_int(), 123);
}
// String
{
unittest::TestOneof2 message1, message2;
string data;
message1.set_foo_string("foo");
int size = message1.ByteSize();
data.resize(size);
uint8* start = reinterpret_cast<uint8*>(string_as_array(&data));
uint8* end = message1.SerializeWithCachedSizesToArray(start);
EXPECT_EQ(size, end - start);
EXPECT_TRUE(message2.ParseFromString(data));
EXPECT_EQ(message2.foo_string(), "foo");
}
// Bytes
{
unittest::TestOneof2 message1, message2;
string data;
message1.set_foo_bytes("qux");
int size = message1.ByteSize();
data.resize(size);
uint8* start = reinterpret_cast<uint8*>(string_as_array(&data));
uint8* end = message1.SerializeWithCachedSizesToArray(start);
EXPECT_EQ(size, end - start);
EXPECT_TRUE(message2.ParseFromString(data));
EXPECT_EQ(message2.foo_bytes(), "qux");
}
// Enum
{
unittest::TestOneof2 message1, message2;
string data;
message1.set_foo_enum(unittest::TestOneof2::FOO);
int size = message1.ByteSize();
data.resize(size);
uint8* start = reinterpret_cast<uint8*>(string_as_array(&data));
uint8* end = message1.SerializeWithCachedSizesToArray(start);
EXPECT_EQ(size, end - start);
EXPECT_TRUE(message2.ParseFromString(data));
EXPECT_EQ(message2.foo_enum(), unittest::TestOneof2::FOO);
}
// Message
{
unittest::TestOneof2 message1, message2;
string data;
message1.mutable_foo_message()->set_qux_int(234);
int size = message1.ByteSize();
data.resize(size);
uint8* start = reinterpret_cast<uint8*>(string_as_array(&data));
uint8* end = message1.SerializeWithCachedSizesToArray(start);
EXPECT_EQ(size, end - start);
EXPECT_TRUE(message2.ParseFromString(data));
EXPECT_EQ(message2.foo_message().qux_int(), 234);
}
// Group
{
unittest::TestOneof2 message1, message2;
string data;
message1.mutable_foogroup()->set_a(345);
int size = message1.ByteSize();
data.resize(size);
uint8* start = reinterpret_cast<uint8*>(string_as_array(&data));
uint8* end = message1.SerializeWithCachedSizesToArray(start);
EXPECT_EQ(size, end - start);
EXPECT_TRUE(message2.ParseFromString(data));
EXPECT_EQ(message2.foogroup().a(), 345);
}
}
// Test the generated SerializeWithCachedSizes() by forcing the buffer to write
// one byte at a time.
// This indirectly tests MergePartialFromCodedStream()
// We have to test each field type separately because we cannot set them at the
// same time
TEST_F(OneofTest, SerializationToStream) {
// Primitive type
{
unittest::TestOneof2 message1, message2;
string data;
message1.set_foo_int(123);
int size = message1.ByteSize();
data.resize(size);
{
// Allow the output stream to buffer only one byte at a time.
io::ArrayOutputStream array_stream(string_as_array(&data), size, 1);
io::CodedOutputStream output_stream(&array_stream);
message1.SerializeWithCachedSizes(&output_stream);
EXPECT_FALSE(output_stream.HadError());
EXPECT_EQ(size, output_stream.ByteCount());
}
EXPECT_TRUE(message2.ParseFromString(data));
EXPECT_EQ(message2.foo_int(), 123);
}
// String
{
unittest::TestOneof2 message1, message2;
string data;
message1.set_foo_string("foo");
int size = message1.ByteSize();
data.resize(size);
{
// Allow the output stream to buffer only one byte at a time.
io::ArrayOutputStream array_stream(string_as_array(&data), size, 1);
io::CodedOutputStream output_stream(&array_stream);
message1.SerializeWithCachedSizes(&output_stream);
EXPECT_FALSE(output_stream.HadError());
EXPECT_EQ(size, output_stream.ByteCount());
}
EXPECT_TRUE(message2.ParseFromString(data));
EXPECT_EQ(message2.foo_string(), "foo");
}
// Bytes
{
unittest::TestOneof2 message1, message2;
string data;
message1.set_foo_bytes("qux");
int size = message1.ByteSize();
data.resize(size);
{
// Allow the output stream to buffer only one byte at a time.
io::ArrayOutputStream array_stream(string_as_array(&data), size, 1);
io::CodedOutputStream output_stream(&array_stream);
message1.SerializeWithCachedSizes(&output_stream);
EXPECT_FALSE(output_stream.HadError());
EXPECT_EQ(size, output_stream.ByteCount());
}
EXPECT_TRUE(message2.ParseFromString(data));
EXPECT_EQ(message2.foo_bytes(), "qux");
}
// Enum
{
unittest::TestOneof2 message1, message2;
string data;
message1.set_foo_enum(unittest::TestOneof2::FOO);
int size = message1.ByteSize();
data.resize(size);
{
// Allow the output stream to buffer only one byte at a time.
io::ArrayOutputStream array_stream(string_as_array(&data), size, 1);
io::CodedOutputStream output_stream(&array_stream);
message1.SerializeWithCachedSizes(&output_stream);
EXPECT_FALSE(output_stream.HadError());
EXPECT_EQ(size, output_stream.ByteCount());
}
EXPECT_TRUE(message2.ParseFromString(data));
EXPECT_EQ(message2.foo_enum(), unittest::TestOneof2::FOO);
}
// Message
{
unittest::TestOneof2 message1, message2;
string data;
message1.mutable_foo_message()->set_qux_int(234);
int size = message1.ByteSize();
data.resize(size);
{
// Allow the output stream to buffer only one byte at a time.
io::ArrayOutputStream array_stream(string_as_array(&data), size, 1);
io::CodedOutputStream output_stream(&array_stream);
message1.SerializeWithCachedSizes(&output_stream);
EXPECT_FALSE(output_stream.HadError());
EXPECT_EQ(size, output_stream.ByteCount());
}
EXPECT_TRUE(message2.ParseFromString(data));
EXPECT_EQ(message2.foo_message().qux_int(), 234);
}
// Group
{
unittest::TestOneof2 message1, message2;
string data;
message1.mutable_foogroup()->set_a(345);
int size = message1.ByteSize();
data.resize(size);
{
// Allow the output stream to buffer only one byte at a time.
io::ArrayOutputStream array_stream(string_as_array(&data), size, 1);
io::CodedOutputStream output_stream(&array_stream);
message1.SerializeWithCachedSizes(&output_stream);
EXPECT_FALSE(output_stream.HadError());
EXPECT_EQ(size, output_stream.ByteCount());
}
EXPECT_TRUE(message2.ParseFromString(data));
EXPECT_EQ(message2.foogroup().a(), 345);
}
}
TEST_F(OneofTest, MergeFrom) {
unittest::TestOneof2 message1, message2;
message1.set_foo_int(123);
message2.MergeFrom(message1);
TestUtil::ExpectAtMostOneFieldSetInOneof(message2);
EXPECT_TRUE(message2.has_foo_int());
EXPECT_EQ(message2.foo_int(), 123);
message1.set_foo_string("foo");
message2.MergeFrom(message1);
TestUtil::ExpectAtMostOneFieldSetInOneof(message2);
EXPECT_TRUE(message2.has_foo_string());
EXPECT_EQ(message2.foo_string(), "foo");
message1.set_foo_bytes("qux");
message2.MergeFrom(message1);
TestUtil::ExpectAtMostOneFieldSetInOneof(message2);
EXPECT_TRUE(message2.has_foo_bytes());
EXPECT_EQ(message2.foo_bytes(), "qux");
message1.set_foo_enum(unittest::TestOneof2::FOO);
message2.MergeFrom(message1);
TestUtil::ExpectAtMostOneFieldSetInOneof(message2);
EXPECT_TRUE(message2.has_foo_enum());
EXPECT_EQ(message2.foo_enum(), unittest::TestOneof2::FOO);
message1.mutable_foo_message()->set_qux_int(234);
message2.MergeFrom(message1);
TestUtil::ExpectAtMostOneFieldSetInOneof(message2);
EXPECT_TRUE(message2.has_foo_message());
EXPECT_EQ(message2.foo_message().qux_int(), 234);
message1.mutable_foogroup()->set_a(345);
message2.MergeFrom(message1);
TestUtil::ExpectAtMostOneFieldSetInOneof(message2);
EXPECT_TRUE(message2.has_foogroup());
EXPECT_EQ(message2.foogroup().a(), 345);
}
} // namespace cpp_unittest
} // namespace cpp
} // namespace compiler

@ -43,6 +43,7 @@
#include <errno.h>
#include <algorithm>
#include <memory>
#include <google/protobuf/compiler/importer.h>
@ -124,7 +125,8 @@ bool SourceTreeDescriptorDatabase::FindFileByName(
scoped_ptr<io::ZeroCopyInputStream> input(source_tree_->Open(filename));
if (input == NULL) {
if (error_collector_ != NULL) {
error_collector_->AddError(filename, -1, 0, "File not found.");
error_collector_->AddError(filename, -1, 0,
source_tree_->GetLastErrorMessage());
}
return false;
}
@ -186,6 +188,7 @@ Importer::Importer(SourceTree* source_tree,
MultiFileErrorCollector* error_collector)
: database_(source_tree),
pool_(&database_, database_.GetValidationErrorCollector()) {
pool_.EnforceWeakDependencies(true);
database_.RecordErrorsTo(error_collector);
}
@ -195,10 +198,22 @@ const FileDescriptor* Importer::Import(const string& filename) {
return pool_.FindFileByName(filename);
}
void Importer::AddUnusedImportTrackFile(const string& file_name) {
pool_.AddUnusedImportTrackFile(file_name);
}
void Importer::ClearUnusedImportTrackFiles() {
pool_.ClearUnusedImportTrackFiles();
}
// ===================================================================
SourceTree::~SourceTree() {}
string SourceTree::GetLastErrorMessage() {
return "File not found.";
}
DiskSourceTree::DiskSourceTree() {}
DiskSourceTree::~DiskSourceTree() {}
@ -239,9 +254,9 @@ static string CanonicalizePath(string path) {
}
#endif
vector<string> parts;
vector<string> canonical_parts;
SplitStringUsing(path, "/", &parts); // Note: Removes empty parts.
vector<string> parts = Split(
path, "/", true); // Note: Removes empty parts.
for (int i = 0; i < parts.size(); i++) {
if (parts[i] == ".") {
// Ignore.
@ -249,7 +264,7 @@ static string CanonicalizePath(string path) {
canonical_parts.push_back(parts[i]);
}
}
string result = JoinStrings(canonical_parts, "/");
string result = Join(canonical_parts, "/");
if (!path.empty() && path[0] == '/') {
// Restore leading slash.
result = '/' + result;
@ -395,8 +410,8 @@ DiskSourceTree::DiskFileToVirtualFile(
bool DiskSourceTree::VirtualFileToDiskFile(const string& virtual_file,
string* disk_file) {
scoped_ptr<io::ZeroCopyInputStream> stream(OpenVirtualFile(virtual_file,
disk_file));
scoped_ptr<io::ZeroCopyInputStream> stream(
OpenVirtualFile(virtual_file, disk_file));
return stream != NULL;
}
@ -404,6 +419,10 @@ io::ZeroCopyInputStream* DiskSourceTree::Open(const string& filename) {
return OpenVirtualFile(filename, NULL);
}
string DiskSourceTree::GetLastErrorMessage() {
return last_error_message_;
}
io::ZeroCopyInputStream* DiskSourceTree::OpenVirtualFile(
const string& virtual_file,
string* disk_file) {
@ -412,6 +431,8 @@ io::ZeroCopyInputStream* DiskSourceTree::OpenVirtualFile(
// We do not allow importing of paths containing things like ".." or
// consecutive slashes since the compiler expects files to be uniquely
// identified by file name.
last_error_message_ = "Backslashes, consecutive slashes, \".\", or \"..\" "
"are not allowed in the virtual path";
return NULL;
}
@ -429,13 +450,13 @@ io::ZeroCopyInputStream* DiskSourceTree::OpenVirtualFile(
if (errno == EACCES) {
// The file exists but is not readable.
// TODO(kenton): Find a way to report this more nicely.
GOOGLE_LOG(WARNING) << "Read access is denied for file: " << temp_disk_file;
last_error_message_ = "Read access is denied for file: " +
temp_disk_file;
return NULL;
}
}
}
last_error_message_ = "File not found.";
return NULL;
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save