Integrated internal changes from Google

This includes all internal changes from around May 20 to now.
pull/1732/merge
Adam Cozzette 9 years ago
parent c18aa7795a
commit d64a2d9941
  1. 2
      Makefile.am
  2. 93
      java/core/src/main/java/com/google/protobuf/AbstractMessage.java
  3. 34
      java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java
  4. 10
      java/core/src/main/java/com/google/protobuf/BooleanArrayList.java
  5. 113
      java/core/src/main/java/com/google/protobuf/CodedOutputStream.java
  6. 12
      java/core/src/main/java/com/google/protobuf/Descriptors.java
  7. 10
      java/core/src/main/java/com/google/protobuf/DoubleArrayList.java
  8. 11
      java/core/src/main/java/com/google/protobuf/ExtensionRegistry.java
  9. 95
      java/core/src/main/java/com/google/protobuf/ExtensionRegistryFactory.java
  10. 57
      java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java
  11. 10
      java/core/src/main/java/com/google/protobuf/FieldSet.java
  12. 11
      java/core/src/main/java/com/google/protobuf/FloatArrayList.java
  13. 200
      java/core/src/main/java/com/google/protobuf/GeneratedMessage.java
  14. 76
      java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java
  15. 11
      java/core/src/main/java/com/google/protobuf/IntArrayList.java
  16. 13
      java/core/src/main/java/com/google/protobuf/LongArrayList.java
  17. 157
      java/core/src/main/java/com/google/protobuf/MapEntry.java
  18. 285
      java/core/src/main/java/com/google/protobuf/MapEntryLite.java
  19. 339
      java/core/src/main/java/com/google/protobuf/MapField.java
  20. 403
      java/core/src/main/java/com/google/protobuf/MapFieldLite.java
  21. 3
      java/core/src/main/java/com/google/protobuf/MessageReflection.java
  22. 708
      java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilderV3.java
  23. 241
      java/core/src/main/java/com/google/protobuf/SingleFieldBuilderV3.java
  24. 91
      java/core/src/main/java/com/google/protobuf/TextFormat.java
  25. 4
      java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java
  26. 17
      java/core/src/main/java/com/google/protobuf/UnsafeByteOperations.java
  27. 210
      java/core/src/main/java/com/google/protobuf/UnsafeUtil.java
  28. 248
      java/core/src/main/java/com/google/protobuf/Utf8.java
  29. 76
      java/core/src/test/java/com/google/protobuf/BooleanArrayListTest.java
  30. 8
      java/core/src/test/java/com/google/protobuf/DescriptorsTest.java
  31. 35
      java/core/src/test/java/com/google/protobuf/DoubleArrayListTest.java
  32. 245
      java/core/src/test/java/com/google/protobuf/ExtensionRegistryFactoryTest.java
  33. 20
      java/core/src/test/java/com/google/protobuf/FieldPresenceTest.java
  34. 33
      java/core/src/test/java/com/google/protobuf/FloatArrayListTest.java
  35. 31
      java/core/src/test/java/com/google/protobuf/IntArrayListTest.java
  36. 17
      java/core/src/test/java/com/google/protobuf/LazyMessageLiteTest.java
  37. 27
      java/core/src/test/java/com/google/protobuf/LiteTest.java
  38. 67
      java/core/src/test/java/com/google/protobuf/LongArrayListTest.java
  39. 659
      java/core/src/test/java/com/google/protobuf/MapForProto2LiteTest.java
  40. 538
      java/core/src/test/java/com/google/protobuf/MapForProto2Test.java
  41. 576
      java/core/src/test/java/com/google/protobuf/MapTest.java
  42. 190
      java/core/src/test/java/com/google/protobuf/RepeatedFieldBuilderV3Test.java
  43. 155
      java/core/src/test/java/com/google/protobuf/SingleFieldBuilderV3Test.java
  44. 5
      java/core/src/test/java/com/google/protobuf/TestUtil.java
  45. 7
      java/core/src/test/java/com/google/protobuf/TextFormatTest.java
  46. 2
      java/core/src/test/proto/com/google/protobuf/field_presence_test.proto
  47. 11
      java/core/src/test/proto/com/google/protobuf/map_for_proto2_lite_test.proto
  48. 11
      java/core/src/test/proto/com/google/protobuf/map_for_proto2_test.proto
  49. 12
      java/core/src/test/proto/com/google/protobuf/map_test.proto
  50. 256
      java/util/src/main/java/com/google/protobuf/util/Durations.java
  51. 48
      java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java
  52. 64
      java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java
  53. 427
      java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
  54. 359
      java/util/src/main/java/com/google/protobuf/util/TimeUtil.java
  55. 349
      java/util/src/main/java/com/google/protobuf/util/Timestamps.java
  56. 87
      java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java
  57. 285
      java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
  58. 49
      java/util/src/test/java/com/google/protobuf/util/TimeUtilTest.java
  59. 1
      java/util/src/test/proto/com/google/protobuf/util/json_test.proto
  60. 4
      js/binary/constants.js
  61. 3
      js/binary/decoder_test.js
  62. 25
      js/binary/reader_test.js
  63. 36
      js/binary/writer.js
  64. 97
      js/message.js
  65. 33
      js/message_test.js
  66. 1
      js/test.proto
  67. 29
      js/testbinary.proto
  68. 42
      python/google/protobuf/descriptor.py
  69. 65
      python/google/protobuf/descriptor_pool.py
  70. 6
      python/google/protobuf/internal/containers.py
  71. 18
      python/google/protobuf/internal/descriptor_pool_test.py
  72. 41
      python/google/protobuf/internal/descriptor_test.py
  73. 43
      python/google/protobuf/internal/file_options_test.proto
  74. 13
      python/google/protobuf/internal/json_format_test.py
  75. 4
      python/google/protobuf/internal/message_test.py
  76. 582
      python/google/protobuf/internal/text_format_test.py
  77. 272
      python/google/protobuf/json_format.py
  78. 277
      python/google/protobuf/pyext/descriptor.cc
  79. 6
      python/google/protobuf/pyext/descriptor.h
  80. 142
      python/google/protobuf/pyext/descriptor_containers.cc
  81. 8
      python/google/protobuf/pyext/descriptor_containers.h
  82. 38
      python/google/protobuf/pyext/descriptor_pool.cc
  83. 1
      python/google/protobuf/pyext/map_container.cc
  84. 66
      python/google/protobuf/pyext/message.cc
  85. 6
      python/google/protobuf/pyext/message.h
  86. 88
      python/google/protobuf/pyext/message_module.cc
  87. 601
      python/google/protobuf/text_format.py
  88. 2
      python/setup.py
  89. 4
      src/google/protobuf/any.pb.cc
  90. 8
      src/google/protobuf/any.pb.h
  91. 16
      src/google/protobuf/any.proto
  92. 32
      src/google/protobuf/api.pb.cc
  93. 24
      src/google/protobuf/api.pb.h
  94. 7
      src/google/protobuf/arena.cc
  95. 2
      src/google/protobuf/arena.h
  96. 1
      src/google/protobuf/arena_unittest.cc
  97. 13
      src/google/protobuf/compiler/command_line_interface.cc
  98. 2
      src/google/protobuf/compiler/cpp/cpp_enum_field.cc
  99. 55
      src/google/protobuf/compiler/cpp/cpp_map_field.cc
  100. 39
      src/google/protobuf/compiler/cpp/cpp_message.cc
  101. Some files were not shown because too many files have changed in this diff Show More

@ -209,6 +209,7 @@ java_EXTRA_DIST=
java/core/src/main/java/com/google/protobuf/Extension.java \ java/core/src/main/java/com/google/protobuf/Extension.java \
java/core/src/main/java/com/google/protobuf/ExtensionLite.java \ java/core/src/main/java/com/google/protobuf/ExtensionLite.java \
java/core/src/main/java/com/google/protobuf/ExtensionRegistry.java \ java/core/src/main/java/com/google/protobuf/ExtensionRegistry.java \
java/core/src/main/java/com/google/protobuf/ExtensionRegistryFactory.java \
java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java \ java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java \
java/core/src/main/java/com/google/protobuf/FieldSet.java \ java/core/src/main/java/com/google/protobuf/FieldSet.java \
java/core/src/main/java/com/google/protobuf/FloatArrayList.java \ java/core/src/main/java/com/google/protobuf/FloatArrayList.java \
@ -273,6 +274,7 @@ java_EXTRA_DIST=
java/core/src/test/java/com/google/protobuf/DoubleArrayListTest.java \ java/core/src/test/java/com/google/protobuf/DoubleArrayListTest.java \
java/core/src/test/java/com/google/protobuf/DynamicMessageTest.java \ java/core/src/test/java/com/google/protobuf/DynamicMessageTest.java \
java/core/src/test/java/com/google/protobuf/EnumTest.java \ java/core/src/test/java/com/google/protobuf/EnumTest.java \
java/core/src/test/java/com/google/protobuf/ExtensionRegistryFactoryTest.java \
java/core/src/test/java/com/google/protobuf/FieldPresenceTest.java \ java/core/src/test/java/com/google/protobuf/FieldPresenceTest.java \
java/core/src/test/java/com/google/protobuf/FloatArrayListTest.java \ java/core/src/test/java/com/google/protobuf/FloatArrayListTest.java \
java/core/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java \ java/core/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java \

@ -60,6 +60,34 @@ public abstract class AbstractMessage
return MessageReflection.isInitialized(this); return MessageReflection.isInitialized(this);
} }
/**
* Interface for the parent of a Builder that allows the builder to
* communicate invalidations back to the parent for use when using nested
* builders.
*/
protected interface BuilderParent {
/**
* A builder becomes dirty whenever a field is modified -- including fields
* in nested builders -- and becomes clean when build() is called. Thus,
* when a builder becomes dirty, all its parents become dirty as well, and
* when it becomes clean, all its children become clean. The dirtiness
* state is used to invalidate certain cached values.
* <br>
* To this end, a builder calls markDirty() on its parent whenever it
* transitions from clean to dirty. The parent must propagate this call to
* its own parent, unless it was already dirty, in which case the
* grandparent must necessarily already be dirty as well. The parent can
* only transition back to "clean" after calling build() on all children.
*/
void markDirty();
}
/** Create a nested builder. */
protected Message.Builder newBuilderForType(BuilderParent parent) {
throw new UnsupportedOperationException("Nested builder is not supported for this type.");
}
@Override @Override
public List<String> findInitializationErrors() { public List<String> findInitializationErrors() {
@ -460,6 +488,31 @@ public abstract class AbstractMessage
MessageReflection.findMissingFields(message)); MessageReflection.findMissingFields(message));
} }
/**
* Used to support nested builders and called to mark this builder as clean.
* Clean builders will propagate the {@link BuildParent#markDirty()} event
* to their parent builders, while dirty builders will not, as their parents
* should be dirty already.
*
* NOTE: Implementations that don't support nested builders don't need to
* override this method.
*/
void markClean() {
throw new IllegalStateException("Should be overriden by subclasses.");
}
/**
* Used to support nested builders and called when this nested builder is
* no longer used by its parent builder and should release the reference
* to its parent builder.
*
* NOTE: Implementations that don't support nested builders don't need to
* override this method.
*/
void dispose() {
throw new IllegalStateException("Should be overriden by subclasses.");
}
// =============================================================== // ===============================================================
// The following definitions seem to be required in order to make javac // The following definitions seem to be required in order to make javac
// not produce weird errors like: // not produce weird errors like:
@ -550,4 +603,44 @@ public abstract class AbstractMessage
return super.mergeDelimitedFrom(input, extensionRegistry); return super.mergeDelimitedFrom(input, extensionRegistry);
} }
} }
/**
* @deprecated from v3.0.0-beta-3+, for compatiblity with v2.5.0 and v2.6.1
* generated code.
*/
@Deprecated
protected static int hashLong(long n) {
return (int) (n ^ (n >>> 32));
}
//
/**
* @deprecated from v3.0.0-beta-3+, for compatiblity with v2.5.0 and v2.6.1
* generated code.
*/
@Deprecated
protected static int hashBoolean(boolean b) {
return b ? 1231 : 1237;
}
//
/**
* @deprecated from v3.0.0-beta-3+, for compatiblity with v2.5.0 and v2.6.1
* generated code.
*/
@Deprecated
protected static int hashEnum(EnumLite e) {
return e.getNumber();
}
//
/**
* @deprecated from v3.0.0-beta-3+, for compatiblity with v2.5.0 and v2.6.1
* generated code.
*/
@Deprecated
protected static int hashEnumList(List<? extends EnumLite> list) {
int hash = 1;
for (EnumLite e : list) {
hash = 31 * hash + hashEnum(e);
}
return hash;
}
} }

@ -57,9 +57,7 @@ public abstract class AbstractMessageLite<
writeTo(out.getCodedOutput()); writeTo(out.getCodedOutput());
return out.build(); return out.build();
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException( throw new RuntimeException(getSerializingExceptionMessage("ByteString"), e);
"Serializing to a ByteString threw an IOException (should " +
"never happen).", e);
} }
} }
@ -72,9 +70,7 @@ public abstract class AbstractMessageLite<
output.checkNoSpaceLeft(); output.checkNoSpaceLeft();
return result; return result;
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException( throw new RuntimeException(getSerializingExceptionMessage("byte array"), e);
"Serializing to a byte array threw an IOException " +
"(should never happen).", e);
} }
} }
@ -109,6 +105,11 @@ public abstract class AbstractMessageLite<
return new UninitializedMessageException(this); return new UninitializedMessageException(this);
} }
private String getSerializingExceptionMessage(String target) {
return "Serializing " + getClass().getName() + " to a " + target
+ " threw an IOException (should never happen).";
}
protected static void checkByteStringIsUtf8(ByteString byteString) protected static void checkByteStringIsUtf8(ByteString byteString)
throws IllegalArgumentException { throws IllegalArgumentException {
if (!byteString.isValidUtf8()) { if (!byteString.isValidUtf8()) {
@ -156,9 +157,7 @@ public abstract class AbstractMessageLite<
} catch (InvalidProtocolBufferException e) { } catch (InvalidProtocolBufferException e) {
throw e; throw e;
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException( throw new RuntimeException(getReadingExceptionMessage("ByteString"), e);
"Reading from a ByteString threw an IOException (should " +
"never happen).", e);
} }
} }
@ -174,9 +173,7 @@ public abstract class AbstractMessageLite<
} catch (InvalidProtocolBufferException e) { } catch (InvalidProtocolBufferException e) {
throw e; throw e;
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException( throw new RuntimeException(getReadingExceptionMessage("ByteString"), e);
"Reading from a ByteString threw an IOException (should " +
"never happen).", e);
} }
} }
@ -197,9 +194,7 @@ public abstract class AbstractMessageLite<
} catch (InvalidProtocolBufferException e) { } catch (InvalidProtocolBufferException e) {
throw e; throw e;
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException( throw new RuntimeException(getReadingExceptionMessage("byte array"), e);
"Reading from a byte array threw an IOException (should " +
"never happen).", e);
} }
} }
@ -225,9 +220,7 @@ public abstract class AbstractMessageLite<
} catch (InvalidProtocolBufferException e) { } catch (InvalidProtocolBufferException e) {
throw e; throw e;
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException( throw new RuntimeException(getReadingExceptionMessage("byte array"), e);
"Reading from a byte array threw an IOException (should " +
"never happen).", e);
} }
} }
@ -335,6 +328,11 @@ public abstract class AbstractMessageLite<
protected abstract BuilderType internalMergeFrom(MessageType message); protected abstract BuilderType internalMergeFrom(MessageType message);
private String getReadingExceptionMessage(String target) {
return "Reading " + getClass().getName() + " from a " + target
+ " threw an IOException (should never happen).";
}
/** /**
* Construct an UninitializedMessageException reporting missing fields in * Construct an UninitializedMessageException reporting missing fields in
* the given message. * the given message.

@ -42,7 +42,8 @@ import java.util.RandomAccess;
* @author dweis@google.com (Daniel Weis) * @author dweis@google.com (Daniel Weis)
*/ */
final class BooleanArrayList final class BooleanArrayList
extends AbstractProtobufList<Boolean> implements BooleanList, RandomAccess { extends AbstractProtobufList<Boolean>
implements BooleanList, RandomAccess {
private static final BooleanArrayList EMPTY_LIST = new BooleanArrayList(); private static final BooleanArrayList EMPTY_LIST = new BooleanArrayList();
static { static {
@ -72,10 +73,11 @@ final class BooleanArrayList
} }
/** /**
* Constructs a new mutable {@code BooleanArrayList}. * Constructs a new mutable {@code BooleanArrayList}
* containing the same elements as {@code other}.
*/ */
private BooleanArrayList(boolean[] array, int size) { private BooleanArrayList(boolean[] other, int size) {
this.array = array; array = other;
this.size = size; this.size = size;
} }

@ -36,12 +36,9 @@ import com.google.protobuf.Utf8.UnpairedSurrogateException;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.lang.reflect.Field;
import java.nio.BufferOverflowException; import java.nio.BufferOverflowException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -59,9 +56,8 @@ import java.util.logging.Logger;
*/ */
public abstract class CodedOutputStream extends ByteOutput { public abstract class CodedOutputStream extends ByteOutput {
private static final Logger logger = Logger.getLogger(CodedOutputStream.class.getName()); private static final Logger logger = Logger.getLogger(CodedOutputStream.class.getName());
private static final sun.misc.Unsafe UNSAFE = getUnsafe(); private static final boolean HAS_UNSAFE_ARRAY_OPERATIONS = UnsafeUtil.hasUnsafeArrayOperations();
private static final boolean HAS_UNSAFE_ARRAY_OPERATIONS = supportsUnsafeArrayOperations(); private static final long ARRAY_BASE_OFFSET = UnsafeUtil.getArrayBaseOffset();
private static final long ARRAY_BASE_OFFSET = byteArrayBaseOffset();
private static final int FIXED_32_SIZE = 4; private static final int FIXED_32_SIZE = 4;
private static final int FIXED_64_SIZE = 8; private static final int FIXED_64_SIZE = 8;
@ -869,7 +865,7 @@ public abstract class CodedOutputStream extends ByteOutput {
return computeLengthDelimitedFieldSize(value.getSerializedSize()); return computeLengthDelimitedFieldSize(value.getSerializedSize());
} }
private static int computeLengthDelimitedFieldSize(int fieldLength) { static int computeLengthDelimitedFieldSize(int fieldLength) {
return computeUInt32SizeNoTag(fieldLength) + fieldLength; return computeUInt32SizeNoTag(fieldLength) + fieldLength;
} }
@ -948,6 +944,10 @@ public abstract class CodedOutputStream extends ByteOutput {
OutOfSpaceException(Throwable cause) { OutOfSpaceException(Throwable cause) {
super(MESSAGE, cause); super(MESSAGE, cause);
} }
OutOfSpaceException(String explanationMessage, Throwable cause) {
super(MESSAGE + ": " + explanationMessage, cause);
}
} }
/** /**
@ -1250,8 +1250,8 @@ public abstract class CodedOutputStream extends ByteOutput {
try { try {
buffer[position++] = value; buffer[position++] = value;
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
throw new OutOfSpaceException(new IndexOutOfBoundsException( throw new OutOfSpaceException(
String.format("Pos: %d, limit: %d, len: %d", position, limit, 1))); String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e);
} }
} }
@ -1271,11 +1271,11 @@ public abstract class CodedOutputStream extends ByteOutput {
long pos = ARRAY_BASE_OFFSET + position; long pos = ARRAY_BASE_OFFSET + position;
while (true) { while (true) {
if ((value & ~0x7F) == 0) { if ((value & ~0x7F) == 0) {
UNSAFE.putByte(buffer, pos++, (byte) value); UnsafeUtil.putByte(buffer, pos++, (byte) value);
position++; position++;
return; return;
} else { } else {
UNSAFE.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80)); UnsafeUtil.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80));
position++; position++;
value >>>= 7; value >>>= 7;
} }
@ -1293,8 +1293,7 @@ public abstract class CodedOutputStream extends ByteOutput {
} }
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
throw new OutOfSpaceException( throw new OutOfSpaceException(
new IndexOutOfBoundsException( String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e);
String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)));
} }
} }
} }
@ -1308,8 +1307,7 @@ public abstract class CodedOutputStream extends ByteOutput {
buffer[position++] = (byte) ((value >> 24) & 0xFF); buffer[position++] = (byte) ((value >> 24) & 0xFF);
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
throw new OutOfSpaceException( throw new OutOfSpaceException(
new IndexOutOfBoundsException( String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e);
String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)));
} }
} }
@ -1319,11 +1317,11 @@ public abstract class CodedOutputStream extends ByteOutput {
long pos = ARRAY_BASE_OFFSET + position; long pos = ARRAY_BASE_OFFSET + position;
while (true) { while (true) {
if ((value & ~0x7FL) == 0) { if ((value & ~0x7FL) == 0) {
UNSAFE.putByte(buffer, pos++, (byte) value); UnsafeUtil.putByte(buffer, pos++, (byte) value);
position++; position++;
return; return;
} else { } else {
UNSAFE.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80)); UnsafeUtil.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80));
position++; position++;
value >>>= 7; value >>>= 7;
} }
@ -1341,8 +1339,7 @@ public abstract class CodedOutputStream extends ByteOutput {
} }
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
throw new OutOfSpaceException( throw new OutOfSpaceException(
new IndexOutOfBoundsException( String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e);
String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)));
} }
} }
} }
@ -1360,8 +1357,7 @@ public abstract class CodedOutputStream extends ByteOutput {
buffer[position++] = (byte) ((int) (value >> 56) & 0xFF); buffer[position++] = (byte) ((int) (value >> 56) & 0xFF);
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
throw new OutOfSpaceException( throw new OutOfSpaceException(
new IndexOutOfBoundsException( String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e);
String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)));
} }
} }
@ -1372,8 +1368,7 @@ public abstract class CodedOutputStream extends ByteOutput {
position += length; position += length;
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
throw new OutOfSpaceException( throw new OutOfSpaceException(
new IndexOutOfBoundsException( String.format("Pos: %d, limit: %d, len: %d", position, limit, length), e);
String.format("Pos: %d, limit: %d, len: %d", position, limit, length)));
} }
} }
@ -1390,8 +1385,7 @@ public abstract class CodedOutputStream extends ByteOutput {
position += length; position += length;
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
throw new OutOfSpaceException( throw new OutOfSpaceException(
new IndexOutOfBoundsException( String.format("Pos: %d, limit: %d, len: %d", position, limit, length), e);
String.format("Pos: %d, limit: %d, len: %d", position, limit, length)));
} }
} }
@ -1855,10 +1849,10 @@ public abstract class CodedOutputStream extends ByteOutput {
long pos = originalPos; long pos = originalPos;
while (true) { while (true) {
if ((value & ~0x7F) == 0) { if ((value & ~0x7F) == 0) {
UNSAFE.putByte(buffer, pos++, (byte) value); UnsafeUtil.putByte(buffer, pos++, (byte) value);
break; break;
} else { } else {
UNSAFE.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80)); UnsafeUtil.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80));
value >>>= 7; value >>>= 7;
} }
} }
@ -1890,10 +1884,10 @@ public abstract class CodedOutputStream extends ByteOutput {
long pos = originalPos; long pos = originalPos;
while (true) { while (true) {
if ((value & ~0x7FL) == 0) { if ((value & ~0x7FL) == 0) {
UNSAFE.putByte(buffer, pos++, (byte) value); UnsafeUtil.putByte(buffer, pos++, (byte) value);
break; break;
} else { } else {
UNSAFE.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80)); UnsafeUtil.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80));
value >>>= 7; value >>>= 7;
} }
} }
@ -2600,65 +2594,4 @@ public abstract class CodedOutputStream extends ByteOutput {
position = 0; position = 0;
} }
} }
/**
* Gets the {@code sun.misc.Unsafe} instance, or {@code null} if not available on this
* platform.
*/
private static sun.misc.Unsafe getUnsafe() {
sun.misc.Unsafe unsafe = null;
try {
unsafe = AccessController.doPrivileged(new PrivilegedExceptionAction<sun.misc.Unsafe>() {
@Override
public sun.misc.Unsafe run() throws Exception {
Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class;
for (Field f : k.getDeclaredFields()) {
f.setAccessible(true);
Object x = f.get(null);
if (k.isInstance(x)) {
return k.cast(x);
}
}
// The sun.misc.Unsafe field does not exist.
return null;
}
});
} catch (Throwable e) {
// Catching Throwable here due to the fact that Google AppEngine raises NoClassDefFoundError
// for Unsafe.
}
logger.log(Level.FINEST, "sun.misc.Unsafe: {}",
unsafe != null ? "available" : "unavailable");
return unsafe;
}
/**
* Indicates whether or not unsafe array operations are supported on this platform.
*/
// TODO(nathanmittler): Add support for Android's MemoryBlock.
private static boolean supportsUnsafeArrayOperations() {
boolean supported = false;
if (UNSAFE != null) {
try {
UNSAFE.getClass().getMethod("arrayBaseOffset", Class.class);
UNSAFE.getClass().getMethod("putByte", Object.class, long.class, byte.class);
supported = true;
} catch (Throwable e) {
// Do nothing.
}
}
logger.log(Level.FINEST, "Unsafe array operations: {}",
supported ? "available" : "unavailable");
return supported;
}
/**
* Get the base offset for byte arrays, or {@code -1} if {@code sun.misc.Unsafe} is not
* available.
*/
private static <T> int byteArrayBaseOffset() {
return HAS_UNSAFE_ARRAY_OPERATIONS ? UNSAFE.arrayBaseOffset(byte[].class) : -1;
}
} }

@ -871,6 +871,10 @@ public final class Descriptors {
nestedTypes[i].setProto(proto.getNestedType(i)); nestedTypes[i].setProto(proto.getNestedType(i));
} }
for (int i = 0; i < oneofs.length; i++) {
oneofs[i].setProto(proto.getOneofDecl(i));
}
for (int i = 0; i < enumTypes.length; i++) { for (int i = 0; i < enumTypes.length; i++) {
enumTypes[i].setProto(proto.getEnumType(i)); enumTypes[i].setProto(proto.getEnumType(i));
} }
@ -2513,6 +2517,10 @@ public final class Descriptors {
public int getFieldCount() { return fieldCount; } public int getFieldCount() { return fieldCount; }
public OneofOptions getOptions() {
return proto.getOptions();
}
/** Get a list of this message type's fields. */ /** Get a list of this message type's fields. */
public List<FieldDescriptor> getFields() { public List<FieldDescriptor> getFields() {
return Collections.unmodifiableList(Arrays.asList(fields)); return Collections.unmodifiableList(Arrays.asList(fields));
@ -2522,6 +2530,10 @@ public final class Descriptors {
return fields[index]; return fields[index];
} }
private void setProto(final OneofDescriptorProto proto) {
this.proto = proto;
}
private OneofDescriptor(final OneofDescriptorProto proto, private OneofDescriptor(final OneofDescriptorProto proto,
final FileDescriptor file, final FileDescriptor file,
final Descriptor parent, final Descriptor parent,

@ -42,7 +42,8 @@ import java.util.RandomAccess;
* @author dweis@google.com (Daniel Weis) * @author dweis@google.com (Daniel Weis)
*/ */
final class DoubleArrayList final class DoubleArrayList
extends AbstractProtobufList<Double> implements DoubleList, RandomAccess { extends AbstractProtobufList<Double>
implements DoubleList, RandomAccess {
private static final DoubleArrayList EMPTY_LIST = new DoubleArrayList(); private static final DoubleArrayList EMPTY_LIST = new DoubleArrayList();
static { static {
@ -72,10 +73,11 @@ final class DoubleArrayList
} }
/** /**
* Constructs a new mutable {@code DoubleArrayList} containing the same elements as {@code other}. * Constructs a new mutable {@code DoubleArrayList}
* containing the same elements as {@code other}.
*/ */
private DoubleArrayList(double[] array, int size) { private DoubleArrayList(double[] other, int size) {
this.array = array; array = other;
this.size = size; this.size = size;
} }

@ -101,7 +101,7 @@ public class ExtensionRegistry extends ExtensionRegistryLite {
/** Get the unmodifiable singleton empty instance. */ /** Get the unmodifiable singleton empty instance. */
public static ExtensionRegistry getEmptyRegistry() { public static ExtensionRegistry getEmptyRegistry() {
return EMPTY; return EMPTY_REGISTRY;
} }
@ -243,6 +243,11 @@ public class ExtensionRegistry extends ExtensionRegistryLite {
add(newExtensionInfo(extension), extension.getExtensionType()); add(newExtensionInfo(extension), extension.getExtensionType());
} }
/** Add an extension from a generated file to the registry. */
public void add(final GeneratedMessage.GeneratedExtension<?, ?> extension) {
add((Extension<?, ?>) extension);
}
static ExtensionInfo newExtensionInfo(final Extension<?, ?> extension) { static ExtensionInfo newExtensionInfo(final Extension<?, ?> extension) {
if (extension.getDescriptor().getJavaType() == if (extension.getDescriptor().getJavaType() ==
FieldDescriptor.JavaType.MESSAGE) { FieldDescriptor.JavaType.MESSAGE) {
@ -311,7 +316,7 @@ public class ExtensionRegistry extends ExtensionRegistryLite {
private final Map<DescriptorIntPair, ExtensionInfo> mutableExtensionsByNumber; private final Map<DescriptorIntPair, ExtensionInfo> mutableExtensionsByNumber;
ExtensionRegistry(boolean empty) { ExtensionRegistry(boolean empty) {
super(ExtensionRegistryLite.getEmptyRegistry()); super(EMPTY_REGISTRY_LITE);
this.immutableExtensionsByName = this.immutableExtensionsByName =
Collections.<String, ExtensionInfo>emptyMap(); Collections.<String, ExtensionInfo>emptyMap();
this.mutableExtensionsByName = this.mutableExtensionsByName =
@ -321,7 +326,7 @@ public class ExtensionRegistry extends ExtensionRegistryLite {
this.mutableExtensionsByNumber = this.mutableExtensionsByNumber =
Collections.<DescriptorIntPair, ExtensionInfo>emptyMap(); Collections.<DescriptorIntPair, ExtensionInfo>emptyMap();
} }
private static final ExtensionRegistry EMPTY = new ExtensionRegistry(true); static final ExtensionRegistry EMPTY_REGISTRY = new ExtensionRegistry(true);
private void add( private void add(
final ExtensionInfo extension, final ExtensionInfo extension,

@ -0,0 +1,95 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import static com.google.protobuf.ExtensionRegistryLite.EMPTY_REGISTRY_LITE;
/**
* A factory object to create instances of {@link ExtensionRegistryLite}.
*
* <p>
* This factory detects (via reflection) if the full (non-Lite) protocol buffer libraries
* are available, and if so, the instances returned are actually {@link ExtensionRegistry}.
*/
final class ExtensionRegistryFactory {
static final String FULL_REGISTRY_CLASS_NAME = "com.google.protobuf.ExtensionRegistry";
/* Visible for Testing
@Nullable */
static final Class<?> EXTENSION_REGISTRY_CLASS = reflectExtensionRegistry();
/* @Nullable */
static Class<?> reflectExtensionRegistry() {
try {
return Class.forName(FULL_REGISTRY_CLASS_NAME);
} catch (ClassNotFoundException e) {
// The exception allocation is potentially expensive on Android (where it can be triggered
// many times at start up). Is there a way to ameliorate this?
return null;
}
}
/** Construct a new, empty instance. */
public static ExtensionRegistryLite create() {
if (EXTENSION_REGISTRY_CLASS != null) {
try {
return invokeSubclassFactory("newInstance");
} catch (Exception e) {
// return a Lite registry.
}
}
return new ExtensionRegistryLite();
}
/** Get the unmodifiable singleton empty instance. */
public static ExtensionRegistryLite createEmpty() {
if (EXTENSION_REGISTRY_CLASS != null) {
try {
return invokeSubclassFactory("getEmptyRegistry");
} catch (Exception e) {
// return a Lite registry.
}
}
return EMPTY_REGISTRY_LITE;
}
static boolean isFullRegistry(ExtensionRegistryLite registry) {
return EXTENSION_REGISTRY_CLASS != null
&& EXTENSION_REGISTRY_CLASS.isAssignableFrom(registry.getClass());
}
private static final ExtensionRegistryLite invokeSubclassFactory(String methodName)
throws Exception {
return (ExtensionRegistryLite) EXTENSION_REGISTRY_CLASS
.getMethod(methodName).invoke(null);
}
}

@ -79,6 +79,22 @@ public class ExtensionRegistryLite {
// applications. Need to support this feature on smaller granularity. // applications. Need to support this feature on smaller granularity.
private static volatile boolean eagerlyParseMessageSets = false; private static volatile boolean eagerlyParseMessageSets = false;
// Visible for testing.
static final String EXTENSION_CLASS_NAME = "com.google.protobuf.Extension";
/* @Nullable */
static Class<?> resolveExtensionClass() {
try {
return Class.forName(EXTENSION_CLASS_NAME);
} catch (ClassNotFoundException e) {
// See comment in ExtensionRegistryFactory on the potential expense of this.
return null;
}
}
/* @Nullable */
private static final Class<?> extensionClass = resolveExtensionClass();
public static boolean isEagerlyParseMessageSets() { public static boolean isEagerlyParseMessageSets() {
return eagerlyParseMessageSets; return eagerlyParseMessageSets;
} }
@ -87,14 +103,22 @@ public class ExtensionRegistryLite {
eagerlyParseMessageSets = isEagerlyParse; eagerlyParseMessageSets = isEagerlyParse;
} }
/** Construct a new, empty instance. */ /**
* Construct a new, empty instance.
*
* <p>
* This may be an {@code ExtensionRegistry} if the full (non-Lite) proto libraries are available.
*/
public static ExtensionRegistryLite newInstance() { public static ExtensionRegistryLite newInstance() {
return new ExtensionRegistryLite(); return ExtensionRegistryFactory.create();
} }
/** Get the unmodifiable singleton empty instance. */ /**
* Get the unmodifiable singleton empty instance of either ExtensionRegistryLite or
* {@code ExtensionRegistry} (if the full (non-Lite) proto libraries are available).
*/
public static ExtensionRegistryLite getEmptyRegistry() { public static ExtensionRegistryLite getEmptyRegistry() {
return EMPTY; return ExtensionRegistryFactory.createEmpty();
} }
/** Returns an unmodifiable view of the registry. */ /** Returns an unmodifiable view of the registry. */
@ -128,6 +152,23 @@ public class ExtensionRegistryLite {
extension); extension);
} }
/**
* Add an extension from a lite generated file to the registry only if it is
* a non-lite extension i.e. {@link GeneratedMessageLite.GeneratedExtension}. */
public final void add(ExtensionLite<?, ?> extension) {
if (GeneratedMessageLite.GeneratedExtension.class.isAssignableFrom(extension.getClass())) {
add((GeneratedMessageLite.GeneratedExtension<?, ?>) extension);
}
if (ExtensionRegistryFactory.isFullRegistry(this)) {
try {
this.getClass().getMethod("add", extensionClass).invoke(this, extension);
} catch (Exception e) {
throw new IllegalArgumentException(
String.format("Could not invoke ExtensionRegistry#add for %s", extension), e);
}
}
}
// ================================================================= // =================================================================
// Private stuff. // Private stuff.
@ -139,9 +180,11 @@ public class ExtensionRegistryLite {
new HashMap<ObjectIntPair, new HashMap<ObjectIntPair,
GeneratedMessageLite.GeneratedExtension<?, ?>>(); GeneratedMessageLite.GeneratedExtension<?, ?>>();
} }
static final ExtensionRegistryLite EMPTY_REGISTRY_LITE =
new ExtensionRegistryLite(true);
ExtensionRegistryLite(ExtensionRegistryLite other) { ExtensionRegistryLite(ExtensionRegistryLite other) {
if (other == EMPTY) { if (other == EMPTY_REGISTRY_LITE) {
this.extensionsByNumber = Collections.emptyMap(); this.extensionsByNumber = Collections.emptyMap();
} else { } else {
this.extensionsByNumber = this.extensionsByNumber =
@ -153,11 +196,9 @@ public class ExtensionRegistryLite {
GeneratedMessageLite.GeneratedExtension<?, ?>> GeneratedMessageLite.GeneratedExtension<?, ?>>
extensionsByNumber; extensionsByNumber;
private ExtensionRegistryLite(boolean empty) { ExtensionRegistryLite(boolean empty) {
this.extensionsByNumber = Collections.emptyMap(); this.extensionsByNumber = Collections.emptyMap();
} }
private static final ExtensionRegistryLite EMPTY =
new ExtensionRegistryLite(true);
/** A (Object, int) pair, used as a map key. */ /** A (Object, int) pair, used as a map key. */
private static final class ObjectIntPair { private static final class ObjectIntPair {

@ -132,7 +132,7 @@ final class FieldSet<FieldDescriptorType extends
} }
FieldSet<?> other = (FieldSet<?>) o; FieldSet<?> other = (FieldSet<?>) o;
return other.fields.equals(other.fields); return fields.equals(other.fields);
} }
@Override @Override
@ -638,7 +638,8 @@ final class FieldSet<FieldDescriptorType extends
* {@link Message#getField(Descriptors.FieldDescriptor)} for * {@link Message#getField(Descriptors.FieldDescriptor)} for
* this field. * this field.
*/ */
private static void writeElement(final CodedOutputStream output, static void writeElement(
final CodedOutputStream output,
final WireFormat.FieldType type, final WireFormat.FieldType type,
final int number, final int number,
final Object value) throws IOException { final Object value) throws IOException {
@ -804,9 +805,8 @@ final class FieldSet<FieldDescriptorType extends
* {@link Message#getField(Descriptors.FieldDescriptor)} for * {@link Message#getField(Descriptors.FieldDescriptor)} for
* this field. * this field.
*/ */
private static int computeElementSize( static int computeElementSize(
final WireFormat.FieldType type, final WireFormat.FieldType type, final int number, final Object value) {
final int number, final Object value) {
int tagSize = CodedOutputStream.computeTagSize(number); int tagSize = CodedOutputStream.computeTagSize(number);
if (type == WireFormat.FieldType.GROUP) { if (type == WireFormat.FieldType.GROUP) {
// Only count the end group tag for proto2 messages as for proto1 the end // Only count the end group tag for proto2 messages as for proto1 the end

@ -41,7 +41,9 @@ import java.util.RandomAccess;
* *
* @author dweis@google.com (Daniel Weis) * @author dweis@google.com (Daniel Weis)
*/ */
final class FloatArrayList extends AbstractProtobufList<Float> implements FloatList, RandomAccess { final class FloatArrayList
extends AbstractProtobufList<Float>
implements FloatList, RandomAccess {
private static final FloatArrayList EMPTY_LIST = new FloatArrayList(); private static final FloatArrayList EMPTY_LIST = new FloatArrayList();
static { static {
@ -71,10 +73,11 @@ final class FloatArrayList extends AbstractProtobufList<Float> implements FloatL
} }
/** /**
* Constructs a new mutable {@code FloatArrayList} containing the same elements as {@code other}. * Constructs a new mutable {@code FloatArrayList}
* containing the same elements as {@code other}.
*/ */
private FloatArrayList(float[] array, int size) { private FloatArrayList(float[] other, int size) {
this.array = array; array = other;
this.size = size; this.size = size;
} }

@ -355,31 +355,30 @@ public abstract class GeneratedMessage extends AbstractMessage
// Noop for messages without extensions. // Noop for messages without extensions.
} }
protected abstract Message.Builder newBuilderForType(BuilderParent parent);
/** /**
* Interface for the parent of a Builder that allows the builder to * TODO(xiaofeng): remove this after b/29368482 is fixed. We need to move this
* communicate invalidations back to the parent for use when using nested * interface to AbstractMessage in order to versioning GeneratedMessage but
* builders. * this move breaks binary compatibility for AppEngine. After AppEngine is
* fixed we can exlude this from google3.
*/ */
protected interface BuilderParent { protected interface BuilderParent extends AbstractMessage.BuilderParent {}
/** /**
* A builder becomes dirty whenever a field is modified -- including fields * TODO(xiaofeng): remove this together with GeneratedMessage.BuilderParent.
* in nested builders -- and becomes clean when build() is called. Thus,
* when a builder becomes dirty, all its parents become dirty as well, and
* when it becomes clean, all its children become clean. The dirtiness
* state is used to invalidate certain cached values.
* <br>
* To this end, a builder calls markAsDirty() on its parent whenever it
* transitions from clean to dirty. The parent must propagate this call to
* its own parent, unless it was already dirty, in which case the
* grandparent must necessarily already be dirty as well. The parent can
* only transition back to "clean" after calling build() on all children.
*/ */
void markDirty(); protected abstract Message.Builder newBuilderForType(BuilderParent parent);
@Override
protected Message.Builder newBuilderForType(final AbstractMessage.BuilderParent parent) {
return newBuilderForType(new BuilderParent() {
@Override
public void markDirty() {
parent.markDirty();
}
});
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public abstract static class Builder <BuilderType extends Builder<BuilderType>> public abstract static class Builder <BuilderType extends Builder<BuilderType>>
extends AbstractMessage.Builder<BuilderType> { extends AbstractMessage.Builder<BuilderType> {
@ -403,6 +402,7 @@ public abstract class GeneratedMessage extends AbstractMessage
this.builderParent = builderParent; this.builderParent = builderParent;
} }
@Override
void dispose() { void dispose() {
builderParent = null; builderParent = null;
} }
@ -420,6 +420,7 @@ public abstract class GeneratedMessage extends AbstractMessage
* Called by the subclass or a builder to notify us that a message was * Called by the subclass or a builder to notify us that a message was
* built and may be cached and therefore invalidations are needed. * built and may be cached and therefore invalidations are needed.
*/ */
@Override
protected void markClean() { protected void markClean() {
this.isClean = true; this.isClean = true;
} }
@ -755,6 +756,33 @@ public abstract class GeneratedMessage extends AbstractMessage
<Type> Type getExtension( <Type> Type getExtension(
ExtensionLite<MessageType, List<Type>> extension, ExtensionLite<MessageType, List<Type>> extension,
int index); int index);
/** Check if a singular extension is present. */
<Type> boolean hasExtension(
Extension<MessageType, Type> extension);
/** Check if a singular extension is present. */
<Type> boolean hasExtension(
GeneratedExtension<MessageType, Type> extension);
/** Get the number of elements in a repeated extension. */
<Type> int getExtensionCount(
Extension<MessageType, List<Type>> extension);
/** Get the number of elements in a repeated extension. */
<Type> int getExtensionCount(
GeneratedExtension<MessageType, List<Type>> extension);
/** Get the value of an extension. */
<Type> Type getExtension(
Extension<MessageType, Type> extension);
/** Get the value of an extension. */
<Type> Type getExtension(
GeneratedExtension<MessageType, Type> extension);
/** Get one element of a repeated extension. */
<Type> Type getExtension(
Extension<MessageType, List<Type>> extension,
int index);
/** Get one element of a repeated extension. */
<Type> Type getExtension(
GeneratedExtension<MessageType, List<Type>> extension,
int index);
} }
/** /**
@ -881,6 +909,53 @@ public abstract class GeneratedMessage extends AbstractMessage
extensions.getRepeatedField(descriptor, index)); extensions.getRepeatedField(descriptor, index));
} }
/** Check if a singular extension is present. */
@Override
public final <Type> boolean hasExtension(final Extension<MessageType, Type> extension) {
return hasExtension((ExtensionLite<MessageType, Type>) extension);
}
/** Check if a singular extension is present. */
@Override
public final <Type> boolean hasExtension(
final GeneratedExtension<MessageType, Type> extension) {
return hasExtension((ExtensionLite<MessageType, Type>) extension);
}
/** Get the number of elements in a repeated extension. */
@Override
public final <Type> int getExtensionCount(
final Extension<MessageType, List<Type>> extension) {
return getExtensionCount((ExtensionLite<MessageType, List<Type>>) extension);
}
/** Get the number of elements in a repeated extension. */
@Override
public final <Type> int getExtensionCount(
final GeneratedExtension<MessageType, List<Type>> extension) {
return getExtensionCount((ExtensionLite<MessageType, List<Type>>) extension);
}
/** Get the value of an extension. */
@Override
public final <Type> Type getExtension(final Extension<MessageType, Type> extension) {
return getExtension((ExtensionLite<MessageType, Type>) extension);
}
/** Get the value of an extension. */
@Override
public final <Type> Type getExtension(
final GeneratedExtension<MessageType, Type> extension) {
return getExtension((ExtensionLite<MessageType, Type>) extension);
}
/** Get one element of a repeated extension. */
@Override
public final <Type> Type getExtension(
final Extension<MessageType, List<Type>> extension, final int index) {
return getExtension((ExtensionLite<MessageType, List<Type>>) extension, index);
}
/** Get one element of a repeated extension. */
@Override
public final <Type> Type getExtension(
final GeneratedExtension<MessageType, List<Type>> extension, final int index) {
return getExtension((ExtensionLite<MessageType, List<Type>>) extension, index);
}
/** Called by subclasses to check if all extensions are initialized. */ /** Called by subclasses to check if all extensions are initialized. */
protected boolean extensionsAreInitialized() { protected boolean extensionsAreInitialized() {
return extensions.isInitialized(); return extensions.isInitialized();
@ -1269,6 +1344,95 @@ public abstract class GeneratedMessage extends AbstractMessage
return (BuilderType) this; return (BuilderType) this;
} }
/** Check if a singular extension is present. */
@Override
public final <Type> boolean hasExtension(final Extension<MessageType, Type> extension) {
return hasExtension((ExtensionLite<MessageType, Type>) extension);
}
/** Check if a singular extension is present. */
@Override
public final <Type> boolean hasExtension(
final GeneratedExtension<MessageType, Type> extension) {
return hasExtension((ExtensionLite<MessageType, Type>) extension);
}
/** Get the number of elements in a repeated extension. */
@Override
public final <Type> int getExtensionCount(
final Extension<MessageType, List<Type>> extension) {
return getExtensionCount((ExtensionLite<MessageType, List<Type>>) extension);
}
/** Get the number of elements in a repeated extension. */
@Override
public final <Type> int getExtensionCount(
final GeneratedExtension<MessageType, List<Type>> extension) {
return getExtensionCount((ExtensionLite<MessageType, List<Type>>) extension);
}
/** Get the value of an extension. */
@Override
public final <Type> Type getExtension(final Extension<MessageType, Type> extension) {
return getExtension((ExtensionLite<MessageType, Type>) extension);
}
/** Get the value of an extension. */
@Override
public final <Type> Type getExtension(
final GeneratedExtension<MessageType, Type> extension) {
return getExtension((ExtensionLite<MessageType, Type>) extension);
}
/** Get the value of an extension. */
@Override
public final <Type> Type getExtension(
final Extension<MessageType, List<Type>> extension, final int index) {
return getExtension((ExtensionLite<MessageType, List<Type>>) extension, index);
}
/** Get the value of an extension. */
@Override
public final <Type> Type getExtension(
final GeneratedExtension<MessageType, List<Type>> extension, final int index) {
return getExtension((ExtensionLite<MessageType, List<Type>>) extension, index);
}
/** Set the value of an extension. */
public final <Type> BuilderType setExtension(
final Extension<MessageType, Type> extension, final Type value) {
return setExtension((ExtensionLite<MessageType, Type>) extension, value);
}
/** Set the value of an extension. */
public final <Type> BuilderType setExtension(
final GeneratedExtension<MessageType, Type> extension, final Type value) {
return setExtension((ExtensionLite<MessageType, Type>) extension, value);
}
/** Set the value of one element of a repeated extension. */
public final <Type> BuilderType setExtension(
final Extension<MessageType, List<Type>> extension,
final int index, final Type value) {
return setExtension((ExtensionLite<MessageType, List<Type>>) extension, index, value);
}
/** Set the value of one element of a repeated extension. */
public final <Type> BuilderType setExtension(
final GeneratedExtension<MessageType, List<Type>> extension,
final int index, final Type value) {
return setExtension((ExtensionLite<MessageType, List<Type>>) extension, index, value);
}
/** Append a value to a repeated extension. */
public final <Type> BuilderType addExtension(
final Extension<MessageType, List<Type>> extension, final Type value) {
return addExtension((ExtensionLite<MessageType, List<Type>>) extension, value);
}
/** Append a value to a repeated extension. */
public final <Type> BuilderType addExtension(
final GeneratedExtension<MessageType, List<Type>> extension, final Type value) {
return addExtension((ExtensionLite<MessageType, List<Type>>) extension, value);
}
/** Clear an extension. */
public final <Type> BuilderType clearExtension(
final Extension<MessageType, ?> extension) {
return clearExtension((ExtensionLite<MessageType, ?>) extension);
}
/** Clear an extension. */
public final <Type> BuilderType clearExtension(
final GeneratedExtension<MessageType, ?> extension) {
return clearExtension((ExtensionLite<MessageType, ?>) extension);
}
/** Called by subclasses to check if all extensions are initialized. */ /** Called by subclasses to check if all extensions are initialized. */
protected boolean extensionsAreInitialized() { protected boolean extensionsAreInitialized() {
return extensions.isInitialized(); return extensions.isInitialized();

@ -1191,7 +1191,7 @@ public abstract class GeneratedMessageLite<
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
throw new RuntimeException("Unable to find proto buffer class: " + messageClassName, e); throw new RuntimeException("Unable to find proto buffer class: " + messageClassName, e);
} catch (NoSuchFieldException e) { } catch (NoSuchFieldException e) {
throw new RuntimeException("Unable to find DEFAULT_INSTANCE in " + messageClassName, e); return readResolveFallback();
} catch (SecurityException e) { } catch (SecurityException e) {
throw new RuntimeException("Unable to call DEFAULT_INSTANCE in " + messageClassName, e); throw new RuntimeException("Unable to call DEFAULT_INSTANCE in " + messageClassName, e);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
@ -1200,6 +1200,33 @@ public abstract class GeneratedMessageLite<
throw new RuntimeException("Unable to understand proto buffer", e); throw new RuntimeException("Unable to understand proto buffer", e);
} }
} }
/**
* @deprecated from v3.0.0-beta-3+, for compatibility with v2.5.0 and v2.6.1 generated code.
*/
@Deprecated
private Object readResolveFallback() throws ObjectStreamException {
try {
Class<?> messageClass = Class.forName(messageClassName);
java.lang.reflect.Field defaultInstanceField =
messageClass.getDeclaredField("defaultInstance");
defaultInstanceField.setAccessible(true);
MessageLite defaultInstance = (MessageLite) defaultInstanceField.get(null);
return defaultInstance.newBuilderForType()
.mergeFrom(asBytes)
.buildPartial();
} catch (ClassNotFoundException e) {
throw new RuntimeException("Unable to find proto buffer class: " + messageClassName, e);
} catch (NoSuchFieldException e) {
throw new RuntimeException("Unable to find defaultInstance in " + messageClassName, e);
} catch (SecurityException e) {
throw new RuntimeException("Unable to call defaultInstance in " + messageClassName, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to call parsePartialFrom", e);
} catch (InvalidProtocolBufferException e) {
throw new RuntimeException("Unable to understand proto buffer", e);
}
}
} }
/** /**
@ -1535,8 +1562,7 @@ public abstract class GeneratedMessageLite<
* Message fields use null sentinals. * Message fields use null sentinals.
*/ */
<T extends MessageLite> T visitMessage(T mine, T other); <T extends MessageLite> T visitMessage(T mine, T other);
LazyFieldLite visitLazyMessage( LazyFieldLite visitLazyMessage(LazyFieldLite mine, LazyFieldLite other);
boolean minePresent, LazyFieldLite mine, boolean otherPresent, LazyFieldLite other);
<T> ProtobufList<T> visitList(ProtobufList<T> mine, ProtobufList<T> other); <T> ProtobufList<T> visitList(ProtobufList<T> mine, ProtobufList<T> other);
BooleanList visitBooleanList(BooleanList mine, BooleanList other); BooleanList visitBooleanList(BooleanList mine, BooleanList other);
@ -1719,10 +1745,14 @@ public abstract class GeneratedMessageLite<
@Override @Override
public LazyFieldLite visitLazyMessage( public LazyFieldLite visitLazyMessage(
boolean minePresent, LazyFieldLite mine, boolean otherPresent, LazyFieldLite other) { LazyFieldLite mine, LazyFieldLite other) {
if (!minePresent && !otherPresent) { if (mine == null && other == null) {
return mine; return null;
} else if (minePresent && otherPresent && mine.equals(other)) { }
if (mine == null || other == null) {
throw NOT_EQUALS;
}
if (mine.equals(other)) {
return mine; return mine;
} }
throw NOT_EQUALS; throw NOT_EQUALS;
@ -1939,9 +1969,14 @@ public abstract class GeneratedMessageLite<
} }
@Override @Override
public LazyFieldLite visitLazyMessage( public LazyFieldLite visitLazyMessage(LazyFieldLite mine, LazyFieldLite other) {
boolean minePresent, LazyFieldLite mine, boolean otherPresent, LazyFieldLite other) { final int protoHash;
hashCode = (53 * hashCode) + mine.hashCode(); if (mine != null) {
protoHash = mine.hashCode();
} else {
protoHash = 37;
}
hashCode = (53 * hashCode) + protoHash;
return mine; return mine;
} }
@ -2089,13 +2124,10 @@ public abstract class GeneratedMessageLite<
@Override @Override
public Object visitOneofLazyMessage(boolean minePresent, Object mine, Object other) { public Object visitOneofLazyMessage(boolean minePresent, Object mine, Object other) {
if (minePresent) { LazyFieldLite lazy = minePresent ? (LazyFieldLite) mine : new LazyFieldLite();
LazyFieldLite lazy = (LazyFieldLite) mine;
lazy.merge((LazyFieldLite) other); lazy.merge((LazyFieldLite) other);
return lazy; return lazy;
} }
return other;
}
@Override @Override
public Object visitOneofMessage(boolean minePresent, Object mine, Object other) { public Object visitOneofMessage(boolean minePresent, Object mine, Object other) {
@ -2121,12 +2153,13 @@ public abstract class GeneratedMessageLite<
} }
@Override @Override
public LazyFieldLite visitLazyMessage( public LazyFieldLite visitLazyMessage(LazyFieldLite mine, LazyFieldLite other) {
boolean minePresent, LazyFieldLite mine, boolean otherPresent, LazyFieldLite other) { if (other != null) {
// LazyFieldLite's are never null so we can just copy across. Necessary to avoid leakage if (mine == null) {
// from builder into immutable message. mine = new LazyFieldLite();
// TODO(dweis): Change to null sentinels? }
mine.merge(other); mine.merge(other);
}
return mine; return mine;
} }
@ -2235,7 +2268,12 @@ public abstract class GeneratedMessageLite<
@Override @Override
public <K, V> MapFieldLite<K, V> visitMap(MapFieldLite<K, V> mine, MapFieldLite<K, V> other) { public <K, V> MapFieldLite<K, V> visitMap(MapFieldLite<K, V> mine, MapFieldLite<K, V> other) {
if (!other.isEmpty()) {
if (!mine.isMutable()) {
mine = mine.mutableCopy();
}
mine.mergeFrom(other); mine.mergeFrom(other);
}
return mine; return mine;
} }
} }

@ -41,7 +41,9 @@ import java.util.RandomAccess;
* *
* @author dweis@google.com (Daniel Weis) * @author dweis@google.com (Daniel Weis)
*/ */
final class IntArrayList extends AbstractProtobufList<Integer> implements IntList, RandomAccess { final class IntArrayList
extends AbstractProtobufList<Integer>
implements IntList, RandomAccess {
private static final IntArrayList EMPTY_LIST = new IntArrayList(); private static final IntArrayList EMPTY_LIST = new IntArrayList();
static { static {
@ -71,10 +73,11 @@ final class IntArrayList extends AbstractProtobufList<Integer> implements IntLis
} }
/** /**
* Constructs a new mutable {@code IntArrayList} containing the same elements as {@code other}. * Constructs a new mutable {@code IntArrayList}
* containing the same elements as {@code other}.
*/ */
private IntArrayList(int[] array, int size) { private IntArrayList(int[] other, int size) {
this.array = array; array = other;
this.size = size; this.size = size;
} }

@ -41,7 +41,9 @@ import java.util.RandomAccess;
* *
* @author dweis@google.com (Daniel Weis) * @author dweis@google.com (Daniel Weis)
*/ */
final class LongArrayList extends AbstractProtobufList<Long> implements LongList, RandomAccess { final class LongArrayList
extends AbstractProtobufList<Long>
implements LongList, RandomAccess {
private static final LongArrayList EMPTY_LIST = new LongArrayList(); private static final LongArrayList EMPTY_LIST = new LongArrayList();
static { static {
@ -71,10 +73,11 @@ final class LongArrayList extends AbstractProtobufList<Long> implements LongList
} }
/** /**
* Constructs a new mutable {@code LongArrayList} containing the same elements as {@code other}. * Constructs a new mutable {@code LongArrayList}
* containing the same elements as {@code other}.
*/ */
private LongArrayList(long[] array, int size) { private LongArrayList(long[] other, int size) {
this.array = array; array = other;
this.size = size; this.size = size;
} }
@ -83,7 +86,7 @@ final class LongArrayList extends AbstractProtobufList<Long> implements LongList
if (this == o) { if (this == o) {
return true; return true;
} }
if (!(o instanceof IntArrayList)) { if (!(o instanceof LongArrayList)) {
return super.equals(o); return super.equals(o);
} }
LongArrayList other = (LongArrayList) o; LongArrayList other = (LongArrayList) o;

@ -49,48 +49,68 @@ import java.util.TreeMap;
* Protobuf internal. Users shouldn't use this class. * Protobuf internal. Users shouldn't use this class.
*/ */
public final class MapEntry<K, V> extends AbstractMessage { public final class MapEntry<K, V> extends AbstractMessage {
private static class Metadata<K, V> {
private static final class Metadata<K, V> extends MapEntryLite.Metadata<K, V> {
public final Descriptor descriptor; public final Descriptor descriptor;
public final MapEntry<K, V> defaultInstance; public final Parser<MapEntry<K, V>> parser;
public final AbstractParser<MapEntry<K, V>> parser;
public Metadata( public Metadata(
final Descriptor descriptor, final MapEntry<K, V> defaultInstance) { Descriptor descriptor,
MapEntry<K, V> defaultInstance,
WireFormat.FieldType keyType,
WireFormat.FieldType valueType) {
super(keyType, defaultInstance.key, valueType, defaultInstance.value);
this.descriptor = descriptor; this.descriptor = descriptor;
this.defaultInstance = defaultInstance;
final Metadata<K, V> thisMetadata = this;
this.parser = new AbstractParser<MapEntry<K, V>>() { this.parser = new AbstractParser<MapEntry<K, V>>() {
private final Parser<MapEntryLite<K, V>> dataParser =
defaultInstance.data.getParserForType();
@Override @Override
public MapEntry<K, V> parsePartialFrom( public MapEntry<K, V> parsePartialFrom(
CodedInputStream input, ExtensionRegistryLite extensionRegistry) CodedInputStream input, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException { throws InvalidProtocolBufferException {
MapEntryLite<K, V> data = return new MapEntry<K, V>(Metadata.this, input, extensionRegistry);
dataParser.parsePartialFrom(input, extensionRegistry);
return new MapEntry<K, V>(thisMetadata, data);
} }
}; };
} }
} }
private final K key;
private final V value;
private final Metadata<K, V> metadata; private final Metadata<K, V> metadata;
private final MapEntryLite<K, V> data;
/** Create a default MapEntry instance. */ /** Create a default MapEntry instance. */
private MapEntry(Descriptor descriptor, private MapEntry(
Descriptor descriptor,
WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType keyType, K defaultKey,
WireFormat.FieldType valueType, V defaultValue) { WireFormat.FieldType valueType, V defaultValue) {
this.data = MapEntryLite.newDefaultInstance( this.key = defaultKey;
keyType, defaultKey, valueType, defaultValue); this.value = defaultValue;
this.metadata = new Metadata<K, V>(descriptor, this); this.metadata = new Metadata<K, V>(descriptor, this, keyType, valueType);
}
/** Create a MapEntry with the provided key and value. */
private MapEntry(Metadata metadata, K key, V value) {
this.key = key;
this.value = value;
this.metadata = metadata;
} }
/** Create a new MapEntry message. */ /** Parsing constructor. */
private MapEntry(Metadata<K, V> metadata, MapEntryLite<K, V> data) { private MapEntry(
Metadata<K, V> metadata,
CodedInputStream input,
ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
try {
this.metadata = metadata; this.metadata = metadata;
this.data = data; Map.Entry<K, V> entry = MapEntryLite.parseEntry(input, metadata, extensionRegistry);
this.key = entry.getKey();
this.value = entry.getValue();
} catch (InvalidProtocolBufferException e) {
throw e.setUnfinishedMessage(this);
} catch (IOException e) {
throw new InvalidProtocolBufferException(e.getMessage()).setUnfinishedMessage(this);
}
} }
/** /**
@ -108,26 +128,34 @@ public final class MapEntry<K, V> extends AbstractMessage {
} }
public K getKey() { public K getKey() {
return data.getKey(); return key;
} }
public V getValue() { public V getValue() {
return data.getValue(); return value;
} }
private volatile int cachedSerializedSize = -1;
@Override @Override
public int getSerializedSize() { public int getSerializedSize() {
return data.getSerializedSize(); if (cachedSerializedSize != -1) {
return cachedSerializedSize;
}
int size = MapEntryLite.computeSerializedSize(metadata, key, value);
cachedSerializedSize = size;
return size;
} }
@Override @Override
public void writeTo(CodedOutputStream output) throws IOException { public void writeTo(CodedOutputStream output) throws IOException {
data.writeTo(output); MapEntryLite.writeTo(output, metadata, key, value);
} }
@Override @Override
public boolean isInitialized() { public boolean isInitialized() {
return data.isInitialized(); return isInitialized(metadata, value);
} }
@Override @Override
@ -142,12 +170,12 @@ public final class MapEntry<K, V> extends AbstractMessage {
@Override @Override
public Builder<K, V> toBuilder() { public Builder<K, V> toBuilder() {
return new Builder<K, V>(metadata, data); return new Builder<K, V>(metadata, key, value);
} }
@Override @Override
public MapEntry<K, V> getDefaultInstanceForType() { public MapEntry<K, V> getDefaultInstanceForType() {
return metadata.defaultInstance; return new MapEntry<K, V>(metadata, metadata.defaultKey, metadata.defaultValue);
} }
@Override @Override
@ -157,8 +185,7 @@ public final class MapEntry<K, V> extends AbstractMessage {
@Override @Override
public Map<FieldDescriptor, Object> getAllFields() { public Map<FieldDescriptor, Object> getAllFields() {
final TreeMap<FieldDescriptor, Object> result = TreeMap<FieldDescriptor, Object> result = new TreeMap<FieldDescriptor, Object>();
new TreeMap<FieldDescriptor, Object>();
for (final FieldDescriptor field : metadata.descriptor.getFields()) { for (final FieldDescriptor field : metadata.descriptor.getFields()) {
if (hasField(field)) { if (hasField(field)) {
result.put(field, getField(field)); result.put(field, getField(field));
@ -217,56 +244,44 @@ public final class MapEntry<K, V> extends AbstractMessage {
public static class Builder<K, V> public static class Builder<K, V>
extends AbstractMessage.Builder<Builder<K, V>> { extends AbstractMessage.Builder<Builder<K, V>> {
private final Metadata<K, V> metadata; private final Metadata<K, V> metadata;
private MapEntryLite<K, V> data; private K key;
private MapEntryLite.Builder<K, V> dataBuilder; private V value;
private Builder(Metadata<K, V> metadata) { private Builder(Metadata<K, V> metadata) {
this.metadata = metadata; this(metadata, metadata.defaultKey, metadata.defaultValue);
this.data = metadata.defaultInstance.data;
this.dataBuilder = null;
} }
private Builder(Metadata<K, V> metadata, MapEntryLite<K, V> data) { private Builder(Metadata<K, V> metadata, K key, V value) {
this.metadata = metadata; this.metadata = metadata;
this.data = data; this.key = key;
this.dataBuilder = null; this.value = value;
} }
public K getKey() { public K getKey() {
return dataBuilder == null ? data.getKey() : dataBuilder.getKey(); return key;
} }
public V getValue() { public V getValue() {
return dataBuilder == null ? data.getValue() : dataBuilder.getValue(); return value;
}
private void ensureMutable() {
if (dataBuilder == null) {
dataBuilder = data.toBuilder();
}
} }
public Builder<K, V> setKey(K key) { public Builder<K, V> setKey(K key) {
ensureMutable(); this.key = key;
dataBuilder.setKey(key);
return this; return this;
} }
public Builder<K, V> clearKey() { public Builder<K, V> clearKey() {
ensureMutable(); this.key = metadata.defaultKey;
dataBuilder.clearKey();
return this; return this;
} }
public Builder<K, V> setValue(V value) { public Builder<K, V> setValue(V value) {
ensureMutable(); this.value = value;
dataBuilder.setValue(value);
return this; return this;
} }
public Builder<K, V> clearValue() { public Builder<K, V> clearValue() {
ensureMutable(); this.value = metadata.defaultValue;
dataBuilder.clearValue();
return this; return this;
} }
@ -281,11 +296,7 @@ public final class MapEntry<K, V> extends AbstractMessage {
@Override @Override
public MapEntry<K, V> buildPartial() { public MapEntry<K, V> buildPartial() {
if (dataBuilder != null) { return new MapEntry<K, V>(metadata, key, value);
data = dataBuilder.buildPartial();
dataBuilder = null;
}
return new MapEntry<K, V>(metadata, data);
} }
@Override @Override
@ -302,8 +313,7 @@ public final class MapEntry<K, V> extends AbstractMessage {
} }
@Override @Override
public com.google.protobuf.Message.Builder newBuilderForField( public Message.Builder newBuilderForField(FieldDescriptor field) {
FieldDescriptor field) {
checkFieldDescriptor(field);; checkFieldDescriptor(field);;
// This method should be called for message fields and in a MapEntry // This method should be called for message fields and in a MapEntry
// message only the value field can possibly be a message field. // message only the value field can possibly be a message field.
@ -312,7 +322,7 @@ public final class MapEntry<K, V> extends AbstractMessage {
throw new RuntimeException( throw new RuntimeException(
"\"" + field.getFullName() + "\" is not a message value field."); "\"" + field.getFullName() + "\" is not a message value field.");
} }
return ((Message) data.getValue()).newBuilderForType(); return ((Message) value).newBuilderForType();
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -362,22 +372,17 @@ public final class MapEntry<K, V> extends AbstractMessage {
@Override @Override
public MapEntry<K, V> getDefaultInstanceForType() { public MapEntry<K, V> getDefaultInstanceForType() {
return metadata.defaultInstance; return new MapEntry<K, V>(metadata, metadata.defaultKey, metadata.defaultValue);
} }
@Override @Override
public boolean isInitialized() { public boolean isInitialized() {
if (dataBuilder != null) { return MapEntry.isInitialized(metadata, value);
return dataBuilder.isInitialized();
} else {
return data.isInitialized();
}
} }
@Override @Override
public Map<FieldDescriptor, Object> getAllFields() { public Map<FieldDescriptor, Object> getAllFields() {
final TreeMap<FieldDescriptor, Object> result = final TreeMap<FieldDescriptor, Object> result = new TreeMap<FieldDescriptor, Object>();
new TreeMap<FieldDescriptor, Object>();
for (final FieldDescriptor field : metadata.descriptor.getFields()) { for (final FieldDescriptor field : metadata.descriptor.getFields()) {
if (hasField(field)) { if (hasField(field)) {
result.put(field, getField(field)); result.put(field, getField(field));
@ -398,8 +403,7 @@ public final class MapEntry<K, V> extends AbstractMessage {
Object result = field.getNumber() == 1 ? getKey() : getValue(); Object result = field.getNumber() == 1 ? getKey() : getValue();
// Convert enums to EnumValueDescriptor. // Convert enums to EnumValueDescriptor.
if (field.getType() == FieldDescriptor.Type.ENUM) { if (field.getType() == FieldDescriptor.Type.ENUM) {
result = field.getEnumType().findValueByNumberCreatingIfUnknown( result = field.getEnumType().findValueByNumberCreatingIfUnknown((Integer) result);
(java.lang.Integer) result);
} }
return result; return result;
} }
@ -423,11 +427,14 @@ public final class MapEntry<K, V> extends AbstractMessage {
@Override @Override
public Builder<K, V> clone() { public Builder<K, V> clone() {
if (dataBuilder == null) { return new Builder(metadata, key, value);
return new Builder<K, V>(metadata, data);
} else {
return new Builder<K, V>(metadata, dataBuilder.build());
} }
} }
private static <V> boolean isInitialized(Metadata metadata, V value) {
if (metadata.valueType.getJavaType() == WireFormat.JavaType.MESSAGE) {
return ((MessageLite) value).isInitialized();
}
return true;
} }
} }

@ -31,6 +31,8 @@
package com.google.protobuf; package com.google.protobuf;
import java.io.IOException; import java.io.IOException;
import java.util.AbstractMap;
import java.util.Map;
/** /**
* Implements the lite version of map entry messages. * Implements the lite version of map entry messages.
@ -41,29 +43,21 @@ import java.io.IOException;
* *
* Protobuf internal. Users shouldn't use. * Protobuf internal. Users shouldn't use.
*/ */
public class MapEntryLite<K, V> public class MapEntryLite<K, V> {
extends AbstractMessageLite<MapEntryLite<K, V>, MapEntryLite.Builder<K, V>> {
private static class Metadata<K, V> { static class Metadata<K, V> {
public final MapEntryLite<K, V> defaultInstance;
public final WireFormat.FieldType keyType; public final WireFormat.FieldType keyType;
public final K defaultKey;
public final WireFormat.FieldType valueType; public final WireFormat.FieldType valueType;
public final Parser<MapEntryLite<K, V>> parser; public final V defaultValue;
public Metadata( public Metadata(
MapEntryLite<K, V> defaultInstance, WireFormat.FieldType keyType, K defaultKey,
WireFormat.FieldType keyType, WireFormat.FieldType valueType, V defaultValue) {
WireFormat.FieldType valueType) {
this.defaultInstance = defaultInstance;
this.keyType = keyType; this.keyType = keyType;
this.defaultKey = defaultKey;
this.valueType = valueType; this.valueType = valueType;
final Metadata<K, V> finalThis = this; this.defaultValue = defaultValue;
this.parser = new AbstractParser<MapEntryLite<K, V>>() {
@Override
public MapEntryLite<K, V> parsePartialFrom(
CodedInputStream input, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
return new MapEntryLite<K, V>(finalThis, input, extensionRegistry);
}
};
} }
} }
@ -78,7 +72,7 @@ public class MapEntryLite<K, V>
private MapEntryLite( private MapEntryLite(
WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType keyType, K defaultKey,
WireFormat.FieldType valueType, V defaultValue) { WireFormat.FieldType valueType, V defaultValue) {
this.metadata = new Metadata<K, V>(this, keyType, valueType); this.metadata = new Metadata<K, V>(keyType, defaultKey, valueType, defaultValue);
this.key = defaultKey; this.key = defaultKey;
this.value = defaultValue; this.value = defaultValue;
} }
@ -113,79 +107,19 @@ public class MapEntryLite<K, V>
keyType, defaultKey, valueType, defaultValue); keyType, defaultKey, valueType, defaultValue);
} }
@Override static <K, V> void writeTo(CodedOutputStream output, Metadata<K, V> metadata, K key, V value)
public void writeTo(CodedOutputStream output) throws IOException { throws IOException {
writeField(KEY_FIELD_NUMBER, metadata.keyType, key, output); FieldSet.writeElement(output, metadata.keyType, KEY_FIELD_NUMBER, key);
writeField(VALUE_FIELD_NUMBER, metadata.valueType, value, output); FieldSet.writeElement(output, metadata.valueType, VALUE_FIELD_NUMBER, value);
}
private void writeField(
int number, WireFormat.FieldType type, Object value,
CodedOutputStream output) throws IOException {
output.writeTag(number, type.getWireType());
FieldSet.writeElementNoTag(output, type, value);
}
private volatile int cachedSerializedSize = -1;
@Override
public int getSerializedSize() {
if (cachedSerializedSize != -1) {
return cachedSerializedSize;
}
int size = 0;
size += getFieldSize(KEY_FIELD_NUMBER, metadata.keyType, key);
size += getFieldSize(VALUE_FIELD_NUMBER, metadata.valueType, value);
cachedSerializedSize = size;
return size;
}
private int getFieldSize(
int number, WireFormat.FieldType type, Object value) {
return CodedOutputStream.computeTagSize(number)
+ FieldSet.computeElementSizeNoTag(type, value);
} }
/** Parsing constructor. */ static <K, V> int computeSerializedSize(Metadata<K, V> metadata, K key, V value) {
private MapEntryLite( return FieldSet.computeElementSize(metadata.keyType, KEY_FIELD_NUMBER, key)
Metadata<K, V> metadata, + FieldSet.computeElementSize(metadata.valueType, VALUE_FIELD_NUMBER, value);
CodedInputStream input,
ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
try {
K key = metadata.defaultInstance.key;
V value = metadata.defaultInstance.value;
while (true) {
int tag = input.readTag();
if (tag == 0) {
break;
}
if (tag == WireFormat.makeTag(
KEY_FIELD_NUMBER, metadata.keyType.getWireType())) {
key = mergeField(
input, extensionRegistry, metadata.keyType, key);
} else if (tag == WireFormat.makeTag(
VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) {
value = mergeField(
input, extensionRegistry, metadata.valueType, value);
} else {
if (!input.skipField(tag)) {
break;
}
}
}
this.metadata = metadata;
this.key = key;
this.value = value;
} catch (InvalidProtocolBufferException e) {
throw e.setUnfinishedMessage(this);
} catch (IOException e) {
throw new InvalidProtocolBufferException(e.getMessage())
.setUnfinishedMessage(this);
}
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private <T> T mergeField( static <T> T parseField(
CodedInputStream input, ExtensionRegistryLite extensionRegistry, CodedInputStream input, ExtensionRegistryLite extensionRegistry,
WireFormat.FieldType type, T value) throws IOException { WireFormat.FieldType type, T value) throws IOException {
switch (type) { switch (type) {
@ -202,136 +136,91 @@ public class MapEntryLite<K, V>
} }
} }
@Override
public Parser<MapEntryLite<K, V>> getParserForType() {
return metadata.parser;
}
@Override
public Builder<K, V> newBuilderForType() {
return new Builder<K, V>(metadata);
}
@Override
public Builder<K, V> toBuilder() {
return new Builder<K, V>(metadata, key, value);
}
@Override
public MapEntryLite<K, V> getDefaultInstanceForType() {
return metadata.defaultInstance;
}
@Override
public boolean isInitialized() {
if (metadata.valueType.getJavaType() == WireFormat.JavaType.MESSAGE) {
return ((MessageLite) value).isInitialized();
}
return true;
}
/** /**
* Builder used to create {@link MapEntryLite} messages. * Serializes the provided key and value as though they were wrapped by a {@link MapEntryLite}
* to the output stream. This helper method avoids allocation of a {@link MapEntryLite}
* built with a key and value and is called from generated code directly.
*/ */
public static class Builder<K, V> public void serializeTo(CodedOutputStream output, int fieldNumber, K key, V value)
extends AbstractMessageLite.Builder<MapEntryLite<K, V>, Builder<K, V>> { throws IOException {
private final Metadata<K, V> metadata; output.writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
private K key; output.writeUInt32NoTag(computeSerializedSize(metadata, key, value));
private V value; writeTo(output, metadata, key, value);
private Builder(Metadata<K, V> metadata) {
this.metadata = metadata;
this.key = metadata.defaultInstance.key;
this.value = metadata.defaultInstance.value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public Builder<K, V> setKey(K key) {
this.key = key;
return this;
} }
public Builder<K, V> setValue(V value) { /**
this.value = value; * Computes the message size for the provided key and value as though they were wrapped
return this; * by a {@link MapEntryLite}. This helper method avoids allocation of a {@link MapEntryLite}
* built with a key and value and is called from generated code directly.
*/
public int computeMessageSize(int fieldNumber, K key, V value) {
return CodedOutputStream.computeTagSize(fieldNumber)
+ CodedOutputStream.computeLengthDelimitedFieldSize(
computeSerializedSize(metadata, key, value));
} }
public Builder<K, V> clearKey() { /**
this.key = metadata.defaultInstance.key; * Parses an entry off of the input as a {@link Map.Entry}. This helper requires an allocation
return this; * so using {@link #parseInto} is preferred if possible.
*/
public Map.Entry<K, V> parseEntry(ByteString bytes, ExtensionRegistryLite extensionRegistry)
throws IOException {
return parseEntry(bytes.newCodedInput(), metadata, extensionRegistry);
} }
public Builder<K, V> clearValue() { static <K, V> Map.Entry<K, V> parseEntry(
this.value = metadata.defaultInstance.value; CodedInputStream input, Metadata<K, V> metadata, ExtensionRegistryLite extensionRegistry)
return this; throws IOException{
K key = metadata.defaultKey;
V value = metadata.defaultValue;
while (true) {
int tag = input.readTag();
if (tag == 0) {
break;
} }
if (tag == WireFormat.makeTag(KEY_FIELD_NUMBER, metadata.keyType.getWireType())) {
@Override key = parseField(input, extensionRegistry, metadata.keyType, key);
public Builder<K, V> clear() { } else if (tag == WireFormat.makeTag(VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) {
this.key = metadata.defaultInstance.key; value = parseField(input, extensionRegistry, metadata.valueType, value);
this.value = metadata.defaultInstance.value; } else {
return this; if (!input.skipField(tag)) {
break;
} }
@Override
public MapEntryLite<K, V> build() {
MapEntryLite<K, V> result = buildPartial();
if (!result.isInitialized()) {
throw newUninitializedMessageException(result);
} }
return result;
} }
return new AbstractMap.SimpleImmutableEntry<K, V>(key, value);
@Override
public MapEntryLite<K, V> buildPartial() {
return new MapEntryLite<K, V>(metadata, key, value);
} }
@Override /**
public MessageLite getDefaultInstanceForType() { * Parses an entry off of the input into the map. This helper avoids allocaton of a
return metadata.defaultInstance; * {@link MapEntryLite} by parsing directly into the provided {@link MapFieldLite}.
} */
public void parseInto(
MapFieldLite<K, V> map, CodedInputStream input, ExtensionRegistryLite extensionRegistry)
throws IOException {
int length = input.readRawVarint32();
final int oldLimit = input.pushLimit(length);
K key = metadata.defaultKey;
V value = metadata.defaultValue;
@Override while (true) {
public boolean isInitialized() { int tag = input.readTag();
if (metadata.valueType.getJavaType() == WireFormat.JavaType.MESSAGE) { if (tag == 0) {
return ((MessageLite) value).isInitialized(); break;
}
return true;
} }
if (tag == WireFormat.makeTag(KEY_FIELD_NUMBER, metadata.keyType.getWireType())) {
private Builder(Metadata<K, V> metadata, K key, V value) { key = parseField(input, extensionRegistry, metadata.keyType, key);
this.metadata = metadata; } else if (tag == WireFormat.makeTag(VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) {
this.key = key; value = parseField(input, extensionRegistry, metadata.valueType, value);
this.value = value; } else {
if (!input.skipField(tag)) {
break;
} }
@Override
public Builder<K, V> clone() {
return new Builder<K, V>(metadata, key, value);
} }
@Override
public Builder<K, V> mergeFrom(
CodedInputStream input, ExtensionRegistryLite extensionRegistry)
throws IOException {
MapEntryLite<K, V> entry =
new MapEntryLite<K, V>(metadata, input, extensionRegistry);
this.key = entry.key;
this.value = entry.value;
return this;
} }
@Override input.checkLastTagWas(0);
protected Builder<K, V> internalMergeFrom(MapEntryLite<K, V> message) { input.popLimit(oldLimit);
throw new UnsupportedOperationException(); map.put(key, value);
}
} }
} }

@ -30,13 +30,14 @@
package com.google.protobuf; package com.google.protobuf;
import com.google.protobuf.MapFieldLite.MutatabilityAwareMap;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
/** /**
* Internal representation of map fields in generated messages. * Internal representation of map fields in generated messages.
@ -286,4 +287,338 @@ public class MapField<K, V> implements MutabilityOracle {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
} }
/**
* An internal map that checks for mutability before delegating.
*/
private static class MutatabilityAwareMap<K, V> implements Map<K, V> {
private final MutabilityOracle mutabilityOracle;
private final Map<K, V> delegate;
MutatabilityAwareMap(MutabilityOracle mutabilityOracle, Map<K, V> delegate) {
this.mutabilityOracle = mutabilityOracle;
this.delegate = delegate;
}
@Override
public int size() {
return delegate.size();
}
@Override
public boolean isEmpty() {
return delegate.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return delegate.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return delegate.containsValue(value);
}
@Override
public V get(Object key) {
return delegate.get(key);
}
@Override
public V put(K key, V value) {
mutabilityOracle.ensureMutable();
return delegate.put(key, value);
}
@Override
public V remove(Object key) {
mutabilityOracle.ensureMutable();
return delegate.remove(key);
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
mutabilityOracle.ensureMutable();
delegate.putAll(m);
}
@Override
public void clear() {
mutabilityOracle.ensureMutable();
delegate.clear();
}
@Override
public Set<K> keySet() {
return new MutatabilityAwareSet<K>(mutabilityOracle, delegate.keySet());
}
@Override
public Collection<V> values() {
return new MutatabilityAwareCollection<V>(mutabilityOracle, delegate.values());
}
@Override
public Set<java.util.Map.Entry<K, V>> entrySet() {
return new MutatabilityAwareSet<Entry<K, V>>(mutabilityOracle, delegate.entrySet());
}
@Override
public boolean equals(Object o) {
return delegate.equals(o);
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public String toString() {
return delegate.toString();
}
/**
* An internal collection that checks for mutability before delegating.
*/
private static class MutatabilityAwareCollection<E> implements Collection<E> {
private final MutabilityOracle mutabilityOracle;
private final Collection<E> delegate;
MutatabilityAwareCollection(MutabilityOracle mutabilityOracle, Collection<E> delegate) {
this.mutabilityOracle = mutabilityOracle;
this.delegate = delegate;
}
@Override
public int size() {
return delegate.size();
}
@Override
public boolean isEmpty() {
return delegate.isEmpty();
}
@Override
public boolean contains(Object o) {
return delegate.contains(o);
}
@Override
public Iterator<E> iterator() {
return new MutatabilityAwareIterator<E>(mutabilityOracle, delegate.iterator());
}
@Override
public Object[] toArray() {
return delegate.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return delegate.toArray(a);
}
@Override
public boolean add(E e) {
// Unsupported operation in the delegate.
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object o) {
mutabilityOracle.ensureMutable();
return delegate.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return delegate.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends E> c) {
// Unsupported operation in the delegate.
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(Collection<?> c) {
mutabilityOracle.ensureMutable();
return delegate.removeAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
mutabilityOracle.ensureMutable();
return delegate.retainAll(c);
}
@Override
public void clear() {
mutabilityOracle.ensureMutable();
delegate.clear();
}
@Override
public boolean equals(Object o) {
return delegate.equals(o);
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public String toString() {
return delegate.toString();
}
}
/**
* An internal set that checks for mutability before delegating.
*/
private static class MutatabilityAwareSet<E> implements Set<E> {
private final MutabilityOracle mutabilityOracle;
private final Set<E> delegate;
MutatabilityAwareSet(MutabilityOracle mutabilityOracle, Set<E> delegate) {
this.mutabilityOracle = mutabilityOracle;
this.delegate = delegate;
}
@Override
public int size() {
return delegate.size();
}
@Override
public boolean isEmpty() {
return delegate.isEmpty();
}
@Override
public boolean contains(Object o) {
return delegate.contains(o);
}
@Override
public Iterator<E> iterator() {
return new MutatabilityAwareIterator<E>(mutabilityOracle, delegate.iterator());
}
@Override
public Object[] toArray() {
return delegate.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return delegate.toArray(a);
}
@Override
public boolean add(E e) {
mutabilityOracle.ensureMutable();
return delegate.add(e);
}
@Override
public boolean remove(Object o) {
mutabilityOracle.ensureMutable();
return delegate.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return delegate.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends E> c) {
mutabilityOracle.ensureMutable();
return delegate.addAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
mutabilityOracle.ensureMutable();
return delegate.retainAll(c);
}
@Override
public boolean removeAll(Collection<?> c) {
mutabilityOracle.ensureMutable();
return delegate.removeAll(c);
}
@Override
public void clear() {
mutabilityOracle.ensureMutable();
delegate.clear();
}
@Override
public boolean equals(Object o) {
return delegate.equals(o);
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public String toString() {
return delegate.toString();
}
}
/**
* An internal iterator that checks for mutability before delegating.
*/
private static class MutatabilityAwareIterator<E> implements Iterator<E> {
private final MutabilityOracle mutabilityOracle;
private final Iterator<E> delegate;
MutatabilityAwareIterator(MutabilityOracle mutabilityOracle, Iterator<E> delegate) {
this.mutabilityOracle = mutabilityOracle;
this.delegate = delegate;
}
@Override
public boolean hasNext() {
return delegate.hasNext();
}
@Override
public E next() {
return delegate.next();
}
@Override
public void remove() {
mutabilityOracle.ensureMutable();
delegate.remove();
}
@Override
public boolean equals(Object obj) {
return delegate.equals(obj);
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public String toString() {
return delegate.toString();
}
}
}
} }

@ -33,9 +33,7 @@ package com.google.protobuf;
import com.google.protobuf.Internal.EnumLite; import com.google.protobuf.Internal.EnumLite;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -46,18 +44,21 @@ import java.util.Set;
* This class is a protobuf implementation detail. Users shouldn't use this * This class is a protobuf implementation detail. Users shouldn't use this
* class directly. * class directly.
*/ */
public final class MapFieldLite<K, V> implements MutabilityOracle { public final class MapFieldLite<K, V> extends LinkedHashMap<K, V> {
private MutatabilityAwareMap<K, V> mapData;
private boolean isMutable; private boolean isMutable;
private MapFieldLite() {
this.isMutable = true;
}
private MapFieldLite(Map<K, V> mapData) { private MapFieldLite(Map<K, V> mapData) {
this.mapData = new MutatabilityAwareMap<K, V>(this, mapData); super(mapData);
this.isMutable = true; this.isMutable = true;
} }
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
private static final MapFieldLite EMPTY_MAP_FIELD = private static final MapFieldLite EMPTY_MAP_FIELD = new MapFieldLite(Collections.emptyMap());
new MapFieldLite(Collections.emptyMap());
static { static {
EMPTY_MAP_FIELD.makeImmutable(); EMPTY_MAP_FIELD.makeImmutable();
} }
@ -68,27 +69,40 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
return (MapFieldLite<K, V>) EMPTY_MAP_FIELD; return (MapFieldLite<K, V>) EMPTY_MAP_FIELD;
} }
/** Creates a new MapFieldLite instance. */ public void mergeFrom(MapFieldLite<K, V> other) {
public static <K, V> MapFieldLite<K, V> newMapField() { ensureMutable();
return new MapFieldLite<K, V>(new LinkedHashMap<K, V>()); if (!other.isEmpty()) {
putAll(other);
}
} }
/** Gets the content of this MapField as a read-only Map. */ @SuppressWarnings({"unchecked", "cast"})
public Map<K, V> getMap() { @Override public Set<Map.Entry<K, V>> entrySet() {
return Collections.unmodifiableMap(mapData); return isEmpty() ? Collections.<Map.Entry<K, V>>emptySet() : super.entrySet();
} }
/** Gets a mutable Map view of this MapField. */ @Override public void clear() {
public Map<K, V> getMutableMap() { ensureMutable();
return mapData; clear();
} }
public void mergeFrom(MapFieldLite<K, V> other) { @Override public V put(K key, V value) {
mapData.putAll(copy(other.mapData)); ensureMutable();
return super.put(key, value);
}
public V put(Map.Entry<K, V> entry) {
return put(entry.getKey(), entry.getValue());
} }
public void clear() { @Override public void putAll(Map<? extends K, ? extends V> m) {
mapData.clear(); ensureMutable();
super.putAll(m);
}
@Override public V remove(Object key) {
ensureMutable();
return super.remove(key);
} }
private static boolean equals(Object a, Object b) { private static boolean equals(Object a, Object b) {
@ -127,11 +141,7 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public boolean equals(Object object) { public boolean equals(Object object) {
if (!(object instanceof MapFieldLite)) { return (object instanceof Map) && equals(this, (Map<K, V>) object);
return false;
}
MapFieldLite<K, V> other = (MapFieldLite<K, V>) object;
return equals(mapData, other.mapData);
} }
private static int calculateHashCodeForObject(Object a) { private static int calculateHashCodeForObject(Object a) {
@ -161,7 +171,7 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
@Override @Override
public int hashCode() { public int hashCode() {
return calculateHashCodeForMap(mapData); return calculateHashCodeForMap(this);
} }
private static Object copy(Object object) { private static Object copy(Object object) {
@ -187,8 +197,8 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
} }
/** Returns a deep copy of this map field. */ /** Returns a deep copy of this map field. */
public MapFieldLite<K, V> copy() { public MapFieldLite<K, V> mutableCopy() {
return new MapFieldLite<K, V>(copy(mapData)); return isEmpty() ? new MapFieldLite<K, V>() : new MapFieldLite<K, V>(this);
} }
/** /**
@ -206,344 +216,9 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
return isMutable; return isMutable;
} }
@Override private void ensureMutable() {
public void ensureMutable() {
if (!isMutable()) { if (!isMutable()) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
} }
/**
* An internal map that checks for mutability before delegating.
*/
static class MutatabilityAwareMap<K, V> implements Map<K, V> {
private final MutabilityOracle mutabilityOracle;
private final Map<K, V> delegate;
MutatabilityAwareMap(MutabilityOracle mutabilityOracle, Map<K, V> delegate) {
this.mutabilityOracle = mutabilityOracle;
this.delegate = delegate;
}
@Override
public int size() {
return delegate.size();
}
@Override
public boolean isEmpty() {
return delegate.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return delegate.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return delegate.containsValue(value);
}
@Override
public V get(Object key) {
return delegate.get(key);
}
@Override
public V put(K key, V value) {
mutabilityOracle.ensureMutable();
return delegate.put(key, value);
}
@Override
public V remove(Object key) {
mutabilityOracle.ensureMutable();
return delegate.remove(key);
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
mutabilityOracle.ensureMutable();
delegate.putAll(m);
}
@Override
public void clear() {
mutabilityOracle.ensureMutable();
delegate.clear();
}
@Override
public Set<K> keySet() {
return new MutatabilityAwareSet<K>(mutabilityOracle, delegate.keySet());
}
@Override
public Collection<V> values() {
return new MutatabilityAwareCollection<V>(mutabilityOracle, delegate.values());
}
@Override
public Set<java.util.Map.Entry<K, V>> entrySet() {
return new MutatabilityAwareSet<Entry<K, V>>(mutabilityOracle, delegate.entrySet());
}
@Override
public boolean equals(Object o) {
return delegate.equals(o);
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public String toString() {
return delegate.toString();
}
}
/**
* An internal collection that checks for mutability before delegating.
*/
private static class MutatabilityAwareCollection<E> implements Collection<E> {
private final MutabilityOracle mutabilityOracle;
private final Collection<E> delegate;
MutatabilityAwareCollection(MutabilityOracle mutabilityOracle, Collection<E> delegate) {
this.mutabilityOracle = mutabilityOracle;
this.delegate = delegate;
}
@Override
public int size() {
return delegate.size();
}
@Override
public boolean isEmpty() {
return delegate.isEmpty();
}
@Override
public boolean contains(Object o) {
return delegate.contains(o);
}
@Override
public Iterator<E> iterator() {
return new MutatabilityAwareIterator<E>(mutabilityOracle, delegate.iterator());
}
@Override
public Object[] toArray() {
return delegate.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return delegate.toArray(a);
}
@Override
public boolean add(E e) {
// Unsupported operation in the delegate.
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object o) {
mutabilityOracle.ensureMutable();
return delegate.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return delegate.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends E> c) {
// Unsupported operation in the delegate.
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(Collection<?> c) {
mutabilityOracle.ensureMutable();
return delegate.removeAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
mutabilityOracle.ensureMutable();
return delegate.retainAll(c);
}
@Override
public void clear() {
mutabilityOracle.ensureMutable();
delegate.clear();
}
@Override
public boolean equals(Object o) {
return delegate.equals(o);
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public String toString() {
return delegate.toString();
}
}
/**
* An internal set that checks for mutability before delegating.
*/
private static class MutatabilityAwareSet<E> implements Set<E> {
private final MutabilityOracle mutabilityOracle;
private final Set<E> delegate;
MutatabilityAwareSet(MutabilityOracle mutabilityOracle, Set<E> delegate) {
this.mutabilityOracle = mutabilityOracle;
this.delegate = delegate;
}
@Override
public int size() {
return delegate.size();
}
@Override
public boolean isEmpty() {
return delegate.isEmpty();
}
@Override
public boolean contains(Object o) {
return delegate.contains(o);
}
@Override
public Iterator<E> iterator() {
return new MutatabilityAwareIterator<E>(mutabilityOracle, delegate.iterator());
}
@Override
public Object[] toArray() {
return delegate.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return delegate.toArray(a);
}
@Override
public boolean add(E e) {
mutabilityOracle.ensureMutable();
return delegate.add(e);
}
@Override
public boolean remove(Object o) {
mutabilityOracle.ensureMutable();
return delegate.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return delegate.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends E> c) {
mutabilityOracle.ensureMutable();
return delegate.addAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
mutabilityOracle.ensureMutable();
return delegate.retainAll(c);
}
@Override
public boolean removeAll(Collection<?> c) {
mutabilityOracle.ensureMutable();
return delegate.removeAll(c);
}
@Override
public void clear() {
mutabilityOracle.ensureMutable();
delegate.clear();
}
@Override
public boolean equals(Object o) {
return delegate.equals(o);
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public String toString() {
return delegate.toString();
}
}
/**
* An internal iterator that checks for mutability before delegating.
*/
private static class MutatabilityAwareIterator<E> implements Iterator<E> {
private final MutabilityOracle mutabilityOracle;
private final Iterator<E> delegate;
MutatabilityAwareIterator(MutabilityOracle mutabilityOracle, Iterator<E> delegate) {
this.mutabilityOracle = mutabilityOracle;
this.delegate = delegate;
}
@Override
public boolean hasNext() {
return delegate.hasNext();
}
@Override
public E next() {
return delegate.next();
}
@Override
public void remove() {
mutabilityOracle.ensureMutable();
delegate.remove();
}
@Override
public boolean equals(Object obj) {
return delegate.equals(obj);
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public String toString() {
return delegate.toString();
}
}
} }

@ -364,7 +364,6 @@ class MessageReflection {
* Finishes the merge and returns the underlying object. * Finishes the merge and returns the underlying object.
*/ */
Object finish(); Object finish();
} }
static class BuilderAdapter implements MergeTarget { static class BuilderAdapter implements MergeTarget {
@ -549,7 +548,6 @@ class MessageReflection {
public Object finish() { public Object finish() {
return builder.buildPartial(); return builder.buildPartial();
} }
} }
@ -713,7 +711,6 @@ class MessageReflection {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"finish() called on FieldSet object"); "finish() called on FieldSet object");
} }
} }
/** /**

@ -0,0 +1,708 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* {@code RepeatedFieldBuilderV3} implements a structure that a protocol
* message uses to hold a repeated field of other protocol messages. It supports
* the classical use case of adding immutable {@link Message}'s to the
* repeated field and is highly optimized around this (no extra memory
* allocations and sharing of immutable arrays).
* <br>
* It also supports the additional use case of adding a {@link Message.Builder}
* to the repeated field and deferring conversion of that {@code Builder}
* to an immutable {@code Message}. In this way, it's possible to maintain
* a tree of {@code Builder}'s that acts as a fully read/write data
* structure.
* <br>
* Logically, one can think of a tree of builders as converting the entire tree
* to messages when build is called on the root or when any method is called
* that desires a Message instead of a Builder. In terms of the implementation,
* the {@code SingleFieldBuilderV3} and {@code RepeatedFieldBuilderV3}
* classes cache messages that were created so that messages only need to be
* created when some change occurred in its builder or a builder for one of its
* descendants.
*
* @param <MType> the type of message for the field
* @param <BType> the type of builder for the field
* @param <IType> the common interface for the message and the builder
*
* @author jonp@google.com (Jon Perlow)
*/
public class RepeatedFieldBuilderV3
<MType extends AbstractMessage,
BType extends AbstractMessage.Builder,
IType extends MessageOrBuilder>
implements AbstractMessage.BuilderParent {
// Parent to send changes to.
private AbstractMessage.BuilderParent parent;
// List of messages. Never null. It may be immutable, in which case
// isMessagesListMutable will be false. See note below.
private List<MType> messages;
// Whether messages is an mutable array that can be modified.
private boolean isMessagesListMutable;
// List of builders. May be null, in which case, no nested builders were
// created. If not null, entries represent the builder for that index.
private List<SingleFieldBuilderV3<MType, BType, IType>> builders;
// Here are the invariants for messages and builders:
// 1. messages is never null and its count corresponds to the number of items
// in the repeated field.
// 2. If builders is non-null, messages and builders MUST always
// contain the same number of items.
// 3. Entries in either array can be null, but for any index, there MUST be
// either a Message in messages or a builder in builders.
// 4. If the builder at an index is non-null, the builder is
// authoritative. This is the case where a Builder was set on the index.
// Any message in the messages array MUST be ignored.
// t. If the builder at an index is null, the message in the messages
// list is authoritative. This is the case where a Message (not a Builder)
// was set directly for an index.
// Indicates that we've built a message and so we are now obligated
// to dispatch dirty invalidations. See AbstractMessage.BuilderListener.
private boolean isClean;
// A view of this builder that exposes a List interface of messages. This is
// initialized on demand. This is fully backed by this object and all changes
// are reflected in it. Access to any item converts it to a message if it
// was a builder.
private MessageExternalList<MType, BType, IType> externalMessageList;
// A view of this builder that exposes a List interface of builders. This is
// initialized on demand. This is fully backed by this object and all changes
// are reflected in it. Access to any item converts it to a builder if it
// was a message.
private BuilderExternalList<MType, BType, IType> externalBuilderList;
// A view of this builder that exposes a List interface of the interface
// implemented by messages and builders. This is initialized on demand. This
// is fully backed by this object and all changes are reflected in it.
// Access to any item returns either a builder or message depending on
// what is most efficient.
private MessageOrBuilderExternalList<MType, BType, IType>
externalMessageOrBuilderList;
/**
* Constructs a new builder with an empty list of messages.
*
* @param messages the current list of messages
* @param isMessagesListMutable Whether the messages list is mutable
* @param parent a listener to notify of changes
* @param isClean whether the builder is initially marked clean
*/
public RepeatedFieldBuilderV3(
List<MType> messages,
boolean isMessagesListMutable,
AbstractMessage.BuilderParent parent,
boolean isClean) {
this.messages = messages;
this.isMessagesListMutable = isMessagesListMutable;
this.parent = parent;
this.isClean = isClean;
}
public void dispose() {
// Null out parent so we stop sending it invalidations.
parent = null;
}
/**
* Ensures that the list of messages is mutable so it can be updated. If it's
* immutable, a copy is made.
*/
private void ensureMutableMessageList() {
if (!isMessagesListMutable) {
messages = new ArrayList<MType>(messages);
isMessagesListMutable = true;
}
}
/**
* Ensures that the list of builders is not null. If it's null, the list is
* created and initialized to be the same size as the messages list with
* null entries.
*/
private void ensureBuilders() {
if (this.builders == null) {
this.builders =
new ArrayList<SingleFieldBuilderV3<MType, BType, IType>>(
messages.size());
for (int i = 0; i < messages.size(); i++) {
builders.add(null);
}
}
}
/**
* Gets the count of items in the list.
*
* @return the count of items in the list.
*/
public int getCount() {
return messages.size();
}
/**
* Gets whether the list is empty.
*
* @return whether the list is empty
*/
public boolean isEmpty() {
return messages.isEmpty();
}
/**
* Get the message at the specified index. If the message is currently stored
* as a {@code Builder}, it is converted to a {@code Message} by
* calling {@link Message.Builder#buildPartial} on it.
*
* @param index the index of the message to get
* @return the message for the specified index
*/
public MType getMessage(int index) {
return getMessage(index, false);
}
/**
* Get the message at the specified index. If the message is currently stored
* as a {@code Builder}, it is converted to a {@code Message} by
* calling {@link Message.Builder#buildPartial} on it.
*
* @param index the index of the message to get
* @param forBuild this is being called for build so we want to make sure
* we SingleFieldBuilderV3.build to send dirty invalidations
* @return the message for the specified index
*/
private MType getMessage(int index, boolean forBuild) {
if (this.builders == null) {
// We don't have any builders -- return the current Message.
// This is the case where no builder was created, so we MUST have a
// Message.
return messages.get(index);
}
SingleFieldBuilderV3<MType, BType, IType> builder = builders.get(index);
if (builder == null) {
// We don't have a builder -- return the current message.
// This is the case where no builder was created for the entry at index,
// so we MUST have a message.
return messages.get(index);
} else {
return forBuild ? builder.build() : builder.getMessage();
}
}
/**
* Gets a builder for the specified index. If no builder has been created for
* that index, a builder is created on demand by calling
* {@link Message#toBuilder}.
*
* @param index the index of the message to get
* @return The builder for that index
*/
public BType getBuilder(int index) {
ensureBuilders();
SingleFieldBuilderV3<MType, BType, IType> builder = builders.get(index);
if (builder == null) {
MType message = messages.get(index);
builder = new SingleFieldBuilderV3<MType, BType, IType>(
message, this, isClean);
builders.set(index, builder);
}
return builder.getBuilder();
}
/**
* Gets the base class interface for the specified index. This may either be
* a builder or a message. It will return whatever is more efficient.
*
* @param index the index of the message to get
* @return the message or builder for the index as the base class interface
*/
@SuppressWarnings("unchecked")
public IType getMessageOrBuilder(int index) {
if (this.builders == null) {
// We don't have any builders -- return the current Message.
// This is the case where no builder was created, so we MUST have a
// Message.
return (IType) messages.get(index);
}
SingleFieldBuilderV3<MType, BType, IType> builder = builders.get(index);
if (builder == null) {
// We don't have a builder -- return the current message.
// This is the case where no builder was created for the entry at index,
// so we MUST have a message.
return (IType) messages.get(index);
} else {
return builder.getMessageOrBuilder();
}
}
/**
* Sets a message at the specified index replacing the existing item at
* that index.
*
* @param index the index to set.
* @param message the message to set
* @return the builder
*/
public RepeatedFieldBuilderV3<MType, BType, IType> setMessage(
int index, MType message) {
if (message == null) {
throw new NullPointerException();
}
ensureMutableMessageList();
messages.set(index, message);
if (builders != null) {
SingleFieldBuilderV3<MType, BType, IType> entry =
builders.set(index, null);
if (entry != null) {
entry.dispose();
}
}
onChanged();
incrementModCounts();
return this;
}
/**
* Appends the specified element to the end of this list.
*
* @param message the message to add
* @return the builder
*/
public RepeatedFieldBuilderV3<MType, BType, IType> addMessage(
MType message) {
if (message == null) {
throw new NullPointerException();
}
ensureMutableMessageList();
messages.add(message);
if (builders != null) {
builders.add(null);
}
onChanged();
incrementModCounts();
return this;
}
/**
* Inserts the specified message at the specified position in this list.
* Shifts the element currently at that position (if any) and any subsequent
* elements to the right (adds one to their indices).
*
* @param index the index at which to insert the message
* @param message the message to add
* @return the builder
*/
public RepeatedFieldBuilderV3<MType, BType, IType> addMessage(
int index, MType message) {
if (message == null) {
throw new NullPointerException();
}
ensureMutableMessageList();
messages.add(index, message);
if (builders != null) {
builders.add(index, null);
}
onChanged();
incrementModCounts();
return this;
}
/**
* Appends all of the messages in the specified collection to the end of
* this list, in the order that they are returned by the specified
* collection's iterator.
*
* @param values the messages to add
* @return the builder
*/
public RepeatedFieldBuilderV3<MType, BType, IType> addAllMessages(
Iterable<? extends MType> values) {
for (final MType value : values) {
if (value == null) {
throw new NullPointerException();
}
}
// If we can inspect the size, we can more efficiently add messages.
int size = -1;
if (values instanceof Collection) {
@SuppressWarnings("unchecked") final
Collection<MType> collection = (Collection<MType>) values;
if (collection.size() == 0) {
return this;
}
size = collection.size();
}
ensureMutableMessageList();
if (size >= 0 && messages instanceof ArrayList) {
((ArrayList<MType>) messages)
.ensureCapacity(messages.size() + size);
}
for (MType value : values) {
addMessage(value);
}
onChanged();
incrementModCounts();
return this;
}
/**
* Appends a new builder to the end of this list and returns the builder.
*
* @param message the message to add which is the basis of the builder
* @return the new builder
*/
public BType addBuilder(MType message) {
ensureMutableMessageList();
ensureBuilders();
SingleFieldBuilderV3<MType, BType, IType> builder =
new SingleFieldBuilderV3<MType, BType, IType>(
message, this, isClean);
messages.add(null);
builders.add(builder);
onChanged();
incrementModCounts();
return builder.getBuilder();
}
/**
* Inserts a new builder at the specified position in this list.
* Shifts the element currently at that position (if any) and any subsequent
* elements to the right (adds one to their indices).
*
* @param index the index at which to insert the builder
* @param message the message to add which is the basis of the builder
* @return the builder
*/
public BType addBuilder(int index, MType message) {
ensureMutableMessageList();
ensureBuilders();
SingleFieldBuilderV3<MType, BType, IType> builder =
new SingleFieldBuilderV3<MType, BType, IType>(
message, this, isClean);
messages.add(index, null);
builders.add(index, builder);
onChanged();
incrementModCounts();
return builder.getBuilder();
}
/**
* Removes the element at the specified position in this list. Shifts any
* subsequent elements to the left (subtracts one from their indices).
* Returns the element that was removed from the list.
*
* @param index the index at which to remove the message
*/
public void remove(int index) {
ensureMutableMessageList();
messages.remove(index);
if (builders != null) {
SingleFieldBuilderV3<MType, BType, IType> entry =
builders.remove(index);
if (entry != null) {
entry.dispose();
}
}
onChanged();
incrementModCounts();
}
/**
* Removes all of the elements from this list.
* The list will be empty after this call returns.
*/
public void clear() {
messages = Collections.emptyList();
isMessagesListMutable = false;
if (builders != null) {
for (SingleFieldBuilderV3<MType, BType, IType> entry :
builders) {
if (entry != null) {
entry.dispose();
}
}
builders = null;
}
onChanged();
incrementModCounts();
}
/**
* Builds the list of messages from the builder and returns them.
*
* @return an immutable list of messages
*/
public List<MType> build() {
// Now that build has been called, we are required to dispatch
// invalidations.
isClean = true;
if (!isMessagesListMutable && builders == null) {
// We still have an immutable list and we never created a builder.
return messages;
}
boolean allMessagesInSync = true;
if (!isMessagesListMutable) {
// We still have an immutable list. Let's see if any of them are out
// of sync with their builders.
for (int i = 0; i < messages.size(); i++) {
Message message = messages.get(i);
SingleFieldBuilderV3<MType, BType, IType> builder = builders.get(i);
if (builder != null) {
if (builder.build() != message) {
allMessagesInSync = false;
break;
}
}
}
if (allMessagesInSync) {
// Immutable list is still in sync.
return messages;
}
}
// Need to make sure messages is up to date
ensureMutableMessageList();
for (int i = 0; i < messages.size(); i++) {
messages.set(i, getMessage(i, true));
}
// We're going to return our list as immutable so we mark that we can
// no longer update it.
messages = Collections.unmodifiableList(messages);
isMessagesListMutable = false;
return messages;
}
/**
* Gets a view of the builder as a list of messages. The returned list is live
* and will reflect any changes to the underlying builder.
*
* @return the messages in the list
*/
public List<MType> getMessageList() {
if (externalMessageList == null) {
externalMessageList =
new MessageExternalList<MType, BType, IType>(this);
}
return externalMessageList;
}
/**
* Gets a view of the builder as a list of builders. This returned list is
* live and will reflect any changes to the underlying builder.
*
* @return the builders in the list
*/
public List<BType> getBuilderList() {
if (externalBuilderList == null) {
externalBuilderList =
new BuilderExternalList<MType, BType, IType>(this);
}
return externalBuilderList;
}
/**
* Gets a view of the builder as a list of MessageOrBuilders. This returned
* list is live and will reflect any changes to the underlying builder.
*
* @return the builders in the list
*/
public List<IType> getMessageOrBuilderList() {
if (externalMessageOrBuilderList == null) {
externalMessageOrBuilderList =
new MessageOrBuilderExternalList<MType, BType, IType>(this);
}
return externalMessageOrBuilderList;
}
/**
* Called when a the builder or one of its nested children has changed
* and any parent should be notified of its invalidation.
*/
private void onChanged() {
if (isClean && parent != null) {
parent.markDirty();
// Don't keep dispatching invalidations until build is called again.
isClean = false;
}
}
@Override
public void markDirty() {
onChanged();
}
/**
* Increments the mod counts so that an ConcurrentModificationException can
* be thrown if calling code tries to modify the builder while its iterating
* the list.
*/
private void incrementModCounts() {
if (externalMessageList != null) {
externalMessageList.incrementModCount();
}
if (externalBuilderList != null) {
externalBuilderList.incrementModCount();
}
if (externalMessageOrBuilderList != null) {
externalMessageOrBuilderList.incrementModCount();
}
}
/**
* Provides a live view of the builder as a list of messages.
*
* @param <MType> the type of message for the field
* @param <BType> the type of builder for the field
* @param <IType> the common interface for the message and the builder
*/
private static class MessageExternalList<
MType extends AbstractMessage,
BType extends AbstractMessage.Builder,
IType extends MessageOrBuilder>
extends AbstractList<MType> implements List<MType> {
RepeatedFieldBuilderV3<MType, BType, IType> builder;
MessageExternalList(
RepeatedFieldBuilderV3<MType, BType, IType> builder) {
this.builder = builder;
}
@Override
public int size() {
return this.builder.getCount();
}
@Override
public MType get(int index) {
return builder.getMessage(index);
}
void incrementModCount() {
modCount++;
}
}
/**
* Provides a live view of the builder as a list of builders.
*
* @param <MType> the type of message for the field
* @param <BType> the type of builder for the field
* @param <IType> the common interface for the message and the builder
*/
private static class BuilderExternalList<
MType extends AbstractMessage,
BType extends AbstractMessage.Builder,
IType extends MessageOrBuilder>
extends AbstractList<BType> implements List<BType> {
RepeatedFieldBuilderV3<MType, BType, IType> builder;
BuilderExternalList(
RepeatedFieldBuilderV3<MType, BType, IType> builder) {
this.builder = builder;
}
@Override
public int size() {
return this.builder.getCount();
}
@Override
public BType get(int index) {
return builder.getBuilder(index);
}
void incrementModCount() {
modCount++;
}
}
/**
* Provides a live view of the builder as a list of builders.
*
* @param <MType> the type of message for the field
* @param <BType> the type of builder for the field
* @param <IType> the common interface for the message and the builder
*/
private static class MessageOrBuilderExternalList<
MType extends AbstractMessage,
BType extends AbstractMessage.Builder,
IType extends MessageOrBuilder>
extends AbstractList<IType> implements List<IType> {
RepeatedFieldBuilderV3<MType, BType, IType> builder;
MessageOrBuilderExternalList(
RepeatedFieldBuilderV3<MType, BType, IType> builder) {
this.builder = builder;
}
@Override
public int size() {
return this.builder.getCount();
}
@Override
public IType get(int index) {
return builder.getMessageOrBuilder(index);
}
void incrementModCount() {
modCount++;
}
}
}

@ -0,0 +1,241 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
/**
* {@code SingleFieldBuilderV3} implements a structure that a protocol
* message uses to hold a single field of another protocol message. It supports
* the classical use case of setting an immutable {@link Message} as the value
* of the field and is highly optimized around this.
* <br>
* It also supports the additional use case of setting a {@link Message.Builder}
* as the field and deferring conversion of that {@code Builder}
* to an immutable {@code Message}. In this way, it's possible to maintain
* a tree of {@code Builder}'s that acts as a fully read/write data
* structure.
* <br>
* Logically, one can think of a tree of builders as converting the entire tree
* to messages when build is called on the root or when any method is called
* that desires a Message instead of a Builder. In terms of the implementation,
* the {@code SingleFieldBuilderV3} and {@code RepeatedFieldBuilderV3}
* classes cache messages that were created so that messages only need to be
* created when some change occurred in its builder or a builder for one of its
* descendants.
*
* @param <MType> the type of message for the field
* @param <BType> the type of builder for the field
* @param <IType> the common interface for the message and the builder
*
* @author jonp@google.com (Jon Perlow)
*/
public class SingleFieldBuilderV3
<MType extends AbstractMessage,
BType extends AbstractMessage.Builder,
IType extends MessageOrBuilder>
implements AbstractMessage.BuilderParent {
// Parent to send changes to.
private AbstractMessage.BuilderParent parent;
// Invariant: one of builder or message fields must be non-null.
// If set, this is the case where we are backed by a builder. In this case,
// message field represents a cached message for the builder (or null if
// there is no cached message).
private BType builder;
// If builder is non-null, this represents a cached message from the builder.
// If builder is null, this is the authoritative message for the field.
private MType message;
// Indicates that we've built a message and so we are now obligated
// to dispatch dirty invalidations. See AbstractMessage.BuilderListener.
private boolean isClean;
public SingleFieldBuilderV3(
MType message,
AbstractMessage.BuilderParent parent,
boolean isClean) {
if (message == null) {
throw new NullPointerException();
}
this.message = message;
this.parent = parent;
this.isClean = isClean;
}
public void dispose() {
// Null out parent so we stop sending it invalidations.
parent = null;
}
/**
* Get the message for the field. If the message is currently stored
* as a {@code Builder}, it is converted to a {@code Message} by
* calling {@link Message.Builder#buildPartial} on it. If no message has
* been set, returns the default instance of the message.
*
* @return the message for the field
*/
@SuppressWarnings("unchecked")
public MType getMessage() {
if (message == null) {
// If message is null, the invariant is that we must be have a builder.
message = (MType) builder.buildPartial();
}
return message;
}
/**
* Builds the message and returns it.
*
* @return the message
*/
public MType build() {
// Now that build has been called, we are required to dispatch
// invalidations.
isClean = true;
return getMessage();
}
/**
* Gets a builder for the field. If no builder has been created yet, a
* builder is created on demand by calling {@link Message#toBuilder}.
*
* @return The builder for the field
*/
@SuppressWarnings("unchecked")
public BType getBuilder() {
if (builder == null) {
// builder.mergeFrom() on a fresh builder
// does not create any sub-objects with independent clean/dirty states,
// therefore setting the builder itself to clean without actually calling
// build() cannot break any invariants.
builder = (BType) message.newBuilderForType(this);
builder.mergeFrom(message); // no-op if message is the default message
builder.markClean();
}
return builder;
}
/**
* Gets the base class interface for the field. This may either be a builder
* or a message. It will return whatever is more efficient.
*
* @return the message or builder for the field as the base class interface
*/
@SuppressWarnings("unchecked")
public IType getMessageOrBuilder() {
if (builder != null) {
return (IType) builder;
} else {
return (IType) message;
}
}
/**
* Sets a message for the field replacing any existing value.
*
* @param message the message to set
* @return the builder
*/
public SingleFieldBuilderV3<MType, BType, IType> setMessage(
MType message) {
if (message == null) {
throw new NullPointerException();
}
this.message = message;
if (builder != null) {
builder.dispose();
builder = null;
}
onChanged();
return this;
}
/**
* Merges the field from another field.
*
* @param value the value to merge from
* @return the builder
*/
public SingleFieldBuilderV3<MType, BType, IType> mergeFrom(
MType value) {
if (builder == null && message == message.getDefaultInstanceForType()) {
message = value;
} else {
getBuilder().mergeFrom(value);
}
onChanged();
return this;
}
/**
* Clears the value of the field.
*
* @return the builder
*/
@SuppressWarnings("unchecked")
public SingleFieldBuilderV3<MType, BType, IType> clear() {
message = (MType) (message != null ?
message.getDefaultInstanceForType() :
builder.getDefaultInstanceForType());
if (builder != null) {
builder.dispose();
builder = null;
}
onChanged();
return this;
}
/**
* Called when a the builder or one of its nested children has changed
* and any parent should be notified of its invalidation.
*/
private void onChanged() {
// If builder is null, this is the case where onChanged is being called
// from setMessage or clear.
if (builder != null) {
message = null;
}
if (isClean && parent != null) {
parent.markDirty();
// Don't keep dispatching invalidations until build is called again.
isClean = false;
}
}
@Override
public void markDirty() {
onChanged();
}
}

@ -661,6 +661,14 @@ public final class TextFormat {
nextToken(); nextToken();
} }
int getPreviousLine() {
return previousLine;
}
int getPreviousColumn() {
return previousColumn;
}
int getLine() { int getLine() {
return line; return line;
} }
@ -1374,6 +1382,28 @@ public final class TextFormat {
return text; return text;
} }
// Check both unknown fields and unknown extensions and log warming messages
// or throw exceptions according to the flag.
private void checkUnknownFields(final List<String> unknownFields)
throws ParseException {
if (unknownFields.isEmpty()) {
return;
}
StringBuilder msg = new StringBuilder("Input contains unknown fields and/or extensions:");
for (String field : unknownFields) {
msg.append('\n').append(field);
}
if (allowUnknownFields) {
logger.warning(msg.toString());
} else {
String[] lineColumn = unknownFields.get(0).split(":");
throw new ParseException(Integer.valueOf(lineColumn[0]),
Integer.valueOf(lineColumn[1]), msg.toString());
}
}
/** /**
* Parse a text-format message from {@code input} and merge the contents * Parse a text-format message from {@code input} and merge the contents
* into {@code builder}. Extensions will be recognized if they are * into {@code builder}. Extensions will be recognized if they are
@ -1387,9 +1417,13 @@ public final class TextFormat {
MessageReflection.BuilderAdapter target = MessageReflection.BuilderAdapter target =
new MessageReflection.BuilderAdapter(builder); new MessageReflection.BuilderAdapter(builder);
List<String> unknownFields = new ArrayList<String>();
while (!tokenizer.atEnd()) { while (!tokenizer.atEnd()) {
mergeField(tokenizer, extensionRegistry, target); mergeField(tokenizer, extensionRegistry, target, unknownFields);
} }
checkUnknownFields(unknownFields);
} }
@ -1399,9 +1433,11 @@ public final class TextFormat {
*/ */
private void mergeField(final Tokenizer tokenizer, private void mergeField(final Tokenizer tokenizer,
final ExtensionRegistry extensionRegistry, final ExtensionRegistry extensionRegistry,
final MessageReflection.MergeTarget target) final MessageReflection.MergeTarget target,
List<String> unknownFields)
throws ParseException { throws ParseException {
mergeField(tokenizer, extensionRegistry, target, parseInfoTreeBuilder); mergeField(tokenizer, extensionRegistry, target, parseInfoTreeBuilder,
unknownFields);
} }
/** /**
@ -1411,7 +1447,8 @@ public final class TextFormat {
private void mergeField(final Tokenizer tokenizer, private void mergeField(final Tokenizer tokenizer,
final ExtensionRegistry extensionRegistry, final ExtensionRegistry extensionRegistry,
final MessageReflection.MergeTarget target, final MessageReflection.MergeTarget target,
TextFormatParseInfoTree.Builder parseTreeBuilder) TextFormatParseInfoTree.Builder parseTreeBuilder,
List<String> unknownFields)
throws ParseException { throws ParseException {
FieldDescriptor field = null; FieldDescriptor field = null;
int startLine = tokenizer.getLine(); int startLine = tokenizer.getLine();
@ -1432,13 +1469,9 @@ public final class TextFormat {
extensionRegistry, name.toString()); extensionRegistry, name.toString());
if (extension == null) { if (extension == null) {
if (!allowUnknownFields) { unknownFields.add((tokenizer.getPreviousLine() + 1) + ":" +
throw tokenizer.parseExceptionPreviousToken( (tokenizer.getPreviousColumn() + 1) + ":\t" +
"Extension \"" + name + "\" not found in the ExtensionRegistry."); type.getFullName() + ".[" + name + "]");
} else {
logger.warning(
"Extension \"" + name + "\" not found in the ExtensionRegistry.");
}
} else { } else {
if (extension.descriptor.getContainingType() != type) { if (extension.descriptor.getContainingType() != type) {
throw tokenizer.parseExceptionPreviousToken( throw tokenizer.parseExceptionPreviousToken(
@ -1473,16 +1506,9 @@ public final class TextFormat {
} }
if (field == null) { if (field == null) {
if (!allowUnknownFields) { unknownFields.add((tokenizer.getPreviousLine() + 1) + ":" +
throw tokenizer.unknownFieldParseExceptionPreviousToken( (tokenizer.getPreviousColumn() + 1) + ":\t" +
name, type.getFullName() + "." + name);
"Message type \"" + type.getFullName()
+ "\" has no field named \"" + name + "\".");
} else {
logger.warning(
"Message type \"" + type.getFullName()
+ "\" has no field named \"" + name + "\".");
}
} }
} }
@ -1511,15 +1537,15 @@ public final class TextFormat {
TextFormatParseInfoTree.Builder childParseTreeBuilder = TextFormatParseInfoTree.Builder childParseTreeBuilder =
parseTreeBuilder.getBuilderForSubMessageField(field); parseTreeBuilder.getBuilderForSubMessageField(field);
consumeFieldValues(tokenizer, extensionRegistry, target, field, extension, consumeFieldValues(tokenizer, extensionRegistry, target, field, extension,
childParseTreeBuilder); childParseTreeBuilder, unknownFields);
} else { } else {
consumeFieldValues(tokenizer, extensionRegistry, target, field, extension, consumeFieldValues(tokenizer, extensionRegistry, target, field, extension,
parseTreeBuilder); parseTreeBuilder, unknownFields);
} }
} else { } else {
tokenizer.consume(":"); // required tokenizer.consume(":"); // required
consumeFieldValues( consumeFieldValues(tokenizer, extensionRegistry, target, field,
tokenizer, extensionRegistry, target, field, extension, parseTreeBuilder); extension, parseTreeBuilder, unknownFields);
} }
if (parseTreeBuilder != null) { if (parseTreeBuilder != null) {
@ -1544,14 +1570,15 @@ public final class TextFormat {
final MessageReflection.MergeTarget target, final MessageReflection.MergeTarget target,
final FieldDescriptor field, final FieldDescriptor field,
final ExtensionRegistry.ExtensionInfo extension, final ExtensionRegistry.ExtensionInfo extension,
final TextFormatParseInfoTree.Builder parseTreeBuilder) final TextFormatParseInfoTree.Builder parseTreeBuilder,
List<String> unknownFields)
throws ParseException { throws ParseException {
// Support specifying repeated field values as a comma-separated list. // Support specifying repeated field values as a comma-separated list.
// Ex."foo: [1, 2, 3]" // Ex."foo: [1, 2, 3]"
if (field.isRepeated() && tokenizer.tryConsume("[")) { if (field.isRepeated() && tokenizer.tryConsume("[")) {
while (true) { while (true) {
consumeFieldValue(tokenizer, extensionRegistry, target, field, extension, consumeFieldValue(tokenizer, extensionRegistry, target, field, extension,
parseTreeBuilder); parseTreeBuilder, unknownFields);
if (tokenizer.tryConsume("]")) { if (tokenizer.tryConsume("]")) {
// End of list. // End of list.
break; break;
@ -1559,8 +1586,8 @@ public final class TextFormat {
tokenizer.consume(","); tokenizer.consume(",");
} }
} else { } else {
consumeFieldValue( consumeFieldValue(tokenizer, extensionRegistry, target, field,
tokenizer, extensionRegistry, target, field, extension, parseTreeBuilder); extension, parseTreeBuilder, unknownFields);
} }
} }
@ -1574,7 +1601,8 @@ public final class TextFormat {
final MessageReflection.MergeTarget target, final MessageReflection.MergeTarget target,
final FieldDescriptor field, final FieldDescriptor field,
final ExtensionRegistry.ExtensionInfo extension, final ExtensionRegistry.ExtensionInfo extension,
final TextFormatParseInfoTree.Builder parseTreeBuilder) final TextFormatParseInfoTree.Builder parseTreeBuilder,
List<String> unknownFields)
throws ParseException { throws ParseException {
Object value = null; Object value = null;
@ -1596,7 +1624,8 @@ public final class TextFormat {
throw tokenizer.parseException( throw tokenizer.parseException(
"Expected \"" + endToken + "\"."); "Expected \"" + endToken + "\".");
} }
mergeField(tokenizer, extensionRegistry, subField, parseTreeBuilder); mergeField(tokenizer, extensionRegistry, subField, parseTreeBuilder,
unknownFields);
} }
value = subField.finish(); value = subField.finish();

@ -57,6 +57,7 @@ import java.util.TreeMap;
* @author kenton@google.com Kenton Varda * @author kenton@google.com Kenton Varda
*/ */
public final class UnknownFieldSet implements MessageLite { public final class UnknownFieldSet implements MessageLite {
private UnknownFieldSet() {} private UnknownFieldSet() {}
/** Create a new {@link Builder}. */ /** Create a new {@link Builder}. */
@ -130,7 +131,8 @@ public final class UnknownFieldSet implements MessageLite {
@Override @Override
public void writeTo(final CodedOutputStream output) throws IOException { public void writeTo(final CodedOutputStream output) throws IOException {
for (final Map.Entry<Integer, Field> entry : fields.entrySet()) { for (final Map.Entry<Integer, Field> entry : fields.entrySet()) {
entry.getValue().writeTo(entry.getKey(), output); Field field = entry.getValue();
field.writeTo(entry.getKey(), output);
} }
} }

@ -42,6 +42,23 @@ import java.nio.ByteBuffer;
* guaranteed that the buffer backing the {@link ByteString} will never change! Mutation of a * guaranteed that the buffer backing the {@link ByteString} will never change! Mutation of a
* {@link ByteString} can lead to unexpected and undesirable consequences in your application, * {@link ByteString} can lead to unexpected and undesirable consequences in your application,
* and will likely be difficult to debug. Proceed with caution! * and will likely be difficult to debug. Proceed with caution!
*
* <p>This can have a number of significant side affects that have
* spooky-action-at-a-distance-like behavior. In particular, if the bytes value changes out from
* under a Protocol Buffer:
* <ul>
* <li>serialization may throw
* <li>serialization may succeed but the wrong bytes may be written out
* <li>messages are no longer threadsafe
* <li>hashCode may be incorrect
* <ul>
* <li>can result in a permanent memory leak when used as a key in a long-lived HashMap
* <li> the semantics of many programs may be violated if this is the case
* </ul>
* </ul>
* Each of these issues will occur in parts of the code base that are entirely distinct from the
* parts of the code base modifying the buffer. In fact, both parts of the code base may be correct
* - it is the bridging with the unsafe operations that was in error!
*/ */
@ExperimentalApi @ExperimentalApi
public final class UnsafeByteOperations { public final class UnsafeByteOperations {

@ -0,0 +1,210 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
/**
* Utility class for working with unsafe operations.
*/
// TODO(nathanmittler): Add support for Android Memory/MemoryBlock
final class UnsafeUtil {
private static final sun.misc.Unsafe UNSAFE = getUnsafe();
private static final boolean HAS_UNSAFE_BYTEBUFFER_OPERATIONS =
supportsUnsafeByteBufferOperations();
private static final boolean HAS_UNSAFE_ARRAY_OPERATIONS = supportsUnsafeArrayOperations();
private static final long ARRAY_BASE_OFFSET = byteArrayBaseOffset();
private static final long BUFFER_ADDRESS_OFFSET = fieldOffset(field(Buffer.class, "address"));
private UnsafeUtil() {
}
static boolean hasUnsafeArrayOperations() {
return HAS_UNSAFE_ARRAY_OPERATIONS;
}
static boolean hasUnsafeByteBufferOperations() {
return HAS_UNSAFE_BYTEBUFFER_OPERATIONS;
}
static long getArrayBaseOffset() {
return ARRAY_BASE_OFFSET;
}
static byte getByte(byte[] target, long offset) {
return UNSAFE.getByte(target, offset);
}
static void putByte(byte[] target, long offset, byte value) {
UNSAFE.putByte(target, offset, value);
}
static void copyMemory(
byte[] src, long srcOffset, byte[] target, long targetOffset, long length) {
UNSAFE.copyMemory(src, srcOffset, target, targetOffset, length);
}
static long getLong(byte[] target, long offset) {
return UNSAFE.getLong(target, offset);
}
static byte getByte(long address) {
return UNSAFE.getByte(address);
}
static void putByte(long address, byte value) {
UNSAFE.putByte(address, value);
}
static long getLong(long address) {
return UNSAFE.getLong(address);
}
static void copyMemory(long srcAddress, long targetAddress, long length) {
UNSAFE.copyMemory(srcAddress, targetAddress, length);
}
/**
* Gets the offset of the {@code address} field of the given direct {@link ByteBuffer}.
*/
static long addressOffset(ByteBuffer buffer) {
return UNSAFE.getLong(buffer, BUFFER_ADDRESS_OFFSET);
}
/**
* Gets the {@code sun.misc.Unsafe} instance, or {@code null} if not available on this platform.
*/
private static sun.misc.Unsafe getUnsafe() {
sun.misc.Unsafe unsafe = null;
try {
unsafe =
AccessController.doPrivileged(
new PrivilegedExceptionAction<Unsafe>() {
@Override
public sun.misc.Unsafe run() throws Exception {
Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class;
for (Field f : k.getDeclaredFields()) {
f.setAccessible(true);
Object x = f.get(null);
if (k.isInstance(x)) {
return k.cast(x);
}
}
// The sun.misc.Unsafe field does not exist.
return null;
}
});
} catch (Throwable e) {
// Catching Throwable here due to the fact that Google AppEngine raises NoClassDefFoundError
// for Unsafe.
}
return unsafe;
}
/**
* Indicates whether or not unsafe array operations are supported on this platform.
*/
private static boolean supportsUnsafeArrayOperations() {
boolean supported = false;
if (UNSAFE != null) {
try {
Class<?> clazz = UNSAFE.getClass();
clazz.getMethod("arrayBaseOffset", Class.class);
clazz.getMethod("getByte", Object.class, long.class);
clazz.getMethod("putByte", Object.class, long.class, byte.class);
clazz.getMethod("getLong", Object.class, long.class);
clazz.getMethod(
"copyMemory", Object.class, long.class, Object.class, long.class, long.class);
supported = true;
} catch (Throwable e) {
// Do nothing.
}
}
return supported;
}
private static boolean supportsUnsafeByteBufferOperations() {
boolean supported = false;
if (UNSAFE != null) {
try {
Class<?> clazz = UNSAFE.getClass();
clazz.getMethod("objectFieldOffset", Field.class);
clazz.getMethod("getByte", long.class);
clazz.getMethod("getLong", Object.class, long.class);
clazz.getMethod("putByte", long.class, byte.class);
clazz.getMethod("getLong", long.class);
clazz.getMethod("copyMemory", long.class, long.class, long.class);
supported = true;
} catch (Throwable e) {
// Do nothing.
}
}
return supported;
}
/**
* Get the base offset for byte arrays, or {@code -1} if {@code sun.misc.Unsafe} is not available.
*/
private static int byteArrayBaseOffset() {
return HAS_UNSAFE_ARRAY_OPERATIONS ? UNSAFE.arrayBaseOffset(byte[].class) : -1;
}
/**
* Returns the offset of the provided field, or {@code -1} if {@code sun.misc.Unsafe} is not
* available.
*/
private static long fieldOffset(Field field) {
return field == null || UNSAFE == null ? -1 : UNSAFE.objectFieldOffset(field);
}
/**
* Gets the field with the given name within the class, or {@code null} if not found. If found,
* the field is made accessible.
*/
private static Field field(Class<?> clazz, String fieldName) {
Field field;
try {
field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
} catch (Throwable t) {
// Failed to access the fields.
field = null;
}
return field;
}
}

@ -30,18 +30,16 @@
package com.google.protobuf; package com.google.protobuf;
import static com.google.protobuf.UnsafeUtil.addressOffset;
import static com.google.protobuf.UnsafeUtil.getArrayBaseOffset;
import static com.google.protobuf.UnsafeUtil.hasUnsafeArrayOperations;
import static com.google.protobuf.UnsafeUtil.hasUnsafeByteBufferOperations;
import static java.lang.Character.MAX_SURROGATE; import static java.lang.Character.MAX_SURROGATE;
import static java.lang.Character.MIN_SURROGATE; import static java.lang.Character.MIN_SURROGATE;
import static java.lang.Character.isSurrogatePair; import static java.lang.Character.isSurrogatePair;
import static java.lang.Character.toCodePoint; import static java.lang.Character.toCodePoint;
import java.lang.reflect.Field;
import java.nio.Buffer;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.logging.Level;
import java.util.logging.Logger;
/** /**
* A set of low-level, high-performance static utility methods related * A set of low-level, high-performance static utility methods related
@ -79,7 +77,6 @@ import java.util.logging.Logger;
*/ */
// TODO(nathanmittler): Copy changes in this class back to Guava // TODO(nathanmittler): Copy changes in this class back to Guava
final class Utf8 { final class Utf8 {
private static final Logger logger = Logger.getLogger(Utf8.class.getName());
/** /**
* UTF-8 is a runtime hot spot so we attempt to provide heavily optimized implementations * UTF-8 is a runtime hot spot so we attempt to provide heavily optimized implementations
@ -237,7 +234,7 @@ final class Utf8 {
// fallback to more lenient behavior. // fallback to more lenient behavior.
static class UnpairedSurrogateException extends IllegalArgumentException { static class UnpairedSurrogateException extends IllegalArgumentException {
private UnpairedSurrogateException(int index, int length) { UnpairedSurrogateException(int index, int length) {
super("Unpaired surrogate at index " + index + " of " + length); super("Unpaired surrogate at index " + index + " of " + length);
} }
} }
@ -991,23 +988,11 @@ final class Utf8 {
* {@link Processor} that uses {@code sun.misc.Unsafe} where possible to improve performance. * {@link Processor} that uses {@code sun.misc.Unsafe} where possible to improve performance.
*/ */
static final class UnsafeProcessor extends Processor { static final class UnsafeProcessor extends Processor {
private static final sun.misc.Unsafe UNSAFE = getUnsafe();
private static final long BUFFER_ADDRESS_OFFSET =
fieldOffset(field(Buffer.class, "address"));
private static final int ARRAY_BASE_OFFSET = byteArrayBaseOffset();
/**
* We only use Unsafe operations if we have access to direct {@link ByteBuffer}'s address
* and the array base offset is a multiple of 8 (needed by Unsafe.getLong()).
*/
private static final boolean AVAILABLE =
BUFFER_ADDRESS_OFFSET != -1 && ARRAY_BASE_OFFSET % 8 == 0;
/** /**
* Indicates whether or not all required unsafe operations are supported on this platform. * Indicates whether or not all required unsafe operations are supported on this platform.
*/ */
static boolean isAvailable() { static boolean isAvailable() {
return AVAILABLE; return hasUnsafeArrayOperations() && hasUnsafeByteBufferOperations();
} }
@Override @Override
@ -1016,8 +1001,8 @@ final class Utf8 {
throw new ArrayIndexOutOfBoundsException( throw new ArrayIndexOutOfBoundsException(
String.format("Array length=%d, index=%d, limit=%d", bytes.length, index, limit)); String.format("Array length=%d, index=%d, limit=%d", bytes.length, index, limit));
} }
long offset = ARRAY_BASE_OFFSET + index; long offset = getArrayBaseOffset() + index;
final long offsetLimit = ARRAY_BASE_OFFSET + limit; final long offsetLimit = getArrayBaseOffset() + limit;
if (state != COMPLETE) { if (state != COMPLETE) {
// The previous decoding operation was incomplete (or malformed). // The previous decoding operation was incomplete (or malformed).
// We look for a well-formed sequence consisting of bytes from // We look for a well-formed sequence consisting of bytes from
@ -1038,7 +1023,7 @@ final class Utf8 {
// leading position and overlong 2-byte form. // leading position and overlong 2-byte form.
if (byte1 < (byte) 0xC2 if (byte1 < (byte) 0xC2
// byte2 trailing-byte test // byte2 trailing-byte test
|| UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) { || UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
return MALFORMED; return MALFORMED;
} }
} else if (byte1 < (byte) 0xF0) { } else if (byte1 < (byte) 0xF0) {
@ -1047,7 +1032,7 @@ final class Utf8 {
// Get byte2 from saved state or array // Get byte2 from saved state or array
int byte2 = (byte) ~(state >> 8); int byte2 = (byte) ~(state >> 8);
if (byte2 == 0) { if (byte2 == 0) {
byte2 = UNSAFE.getByte(bytes, offset++); byte2 = UnsafeUtil.getByte(bytes, offset++);
if (offset >= offsetLimit) { if (offset >= offsetLimit) {
return incompleteStateFor(byte1, byte2); return incompleteStateFor(byte1, byte2);
} }
@ -1058,7 +1043,7 @@ final class Utf8 {
// illegal surrogate codepoint? // illegal surrogate codepoint?
|| (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
// byte3 trailing-byte test // byte3 trailing-byte test
|| UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) { || UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
return MALFORMED; return MALFORMED;
} }
} else { } else {
@ -1068,7 +1053,7 @@ final class Utf8 {
int byte2 = (byte) ~(state >> 8); int byte2 = (byte) ~(state >> 8);
int byte3 = 0; int byte3 = 0;
if (byte2 == 0) { if (byte2 == 0) {
byte2 = UNSAFE.getByte(bytes, offset++); byte2 = UnsafeUtil.getByte(bytes, offset++);
if (offset >= offsetLimit) { if (offset >= offsetLimit) {
return incompleteStateFor(byte1, byte2); return incompleteStateFor(byte1, byte2);
} }
@ -1076,7 +1061,7 @@ final class Utf8 {
byte3 = (byte) (state >> 16); byte3 = (byte) (state >> 16);
} }
if (byte3 == 0) { if (byte3 == 0) {
byte3 = UNSAFE.getByte(bytes, offset++); byte3 = UnsafeUtil.getByte(bytes, offset++);
if (offset >= offsetLimit) { if (offset >= offsetLimit) {
return incompleteStateFor(byte1, byte2, byte3); return incompleteStateFor(byte1, byte2, byte3);
} }
@ -1095,7 +1080,7 @@ final class Utf8 {
// byte3 trailing-byte test // byte3 trailing-byte test
|| byte3 > (byte) 0xBF || byte3 > (byte) 0xBF
// byte4 trailing-byte test // byte4 trailing-byte test
|| UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) { || UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
return MALFORMED; return MALFORMED;
} }
} }
@ -1134,7 +1119,7 @@ final class Utf8 {
// leading position and overlong 2-byte form. // leading position and overlong 2-byte form.
if (byte1 < (byte) 0xC2 if (byte1 < (byte) 0xC2
// byte2 trailing-byte test // byte2 trailing-byte test
|| UNSAFE.getByte(address++) > (byte) 0xBF) { || UnsafeUtil.getByte(address++) > (byte) 0xBF) {
return MALFORMED; return MALFORMED;
} }
} else if (byte1 < (byte) 0xF0) { } else if (byte1 < (byte) 0xF0) {
@ -1143,7 +1128,7 @@ final class Utf8 {
// Get byte2 from saved state or array // Get byte2 from saved state or array
int byte2 = (byte) ~(state >> 8); int byte2 = (byte) ~(state >> 8);
if (byte2 == 0) { if (byte2 == 0) {
byte2 = UNSAFE.getByte(address++); byte2 = UnsafeUtil.getByte(address++);
if (address >= addressLimit) { if (address >= addressLimit) {
return incompleteStateFor(byte1, byte2); return incompleteStateFor(byte1, byte2);
} }
@ -1154,7 +1139,7 @@ final class Utf8 {
// illegal surrogate codepoint? // illegal surrogate codepoint?
|| (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
// byte3 trailing-byte test // byte3 trailing-byte test
|| UNSAFE.getByte(address++) > (byte) 0xBF) { || UnsafeUtil.getByte(address++) > (byte) 0xBF) {
return MALFORMED; return MALFORMED;
} }
} else { } else {
@ -1164,7 +1149,7 @@ final class Utf8 {
int byte2 = (byte) ~(state >> 8); int byte2 = (byte) ~(state >> 8);
int byte3 = 0; int byte3 = 0;
if (byte2 == 0) { if (byte2 == 0) {
byte2 = UNSAFE.getByte(address++); byte2 = UnsafeUtil.getByte(address++);
if (address >= addressLimit) { if (address >= addressLimit) {
return incompleteStateFor(byte1, byte2); return incompleteStateFor(byte1, byte2);
} }
@ -1172,7 +1157,7 @@ final class Utf8 {
byte3 = (byte) (state >> 16); byte3 = (byte) (state >> 16);
} }
if (byte3 == 0) { if (byte3 == 0) {
byte3 = UNSAFE.getByte(address++); byte3 = UnsafeUtil.getByte(address++);
if (address >= addressLimit) { if (address >= addressLimit) {
return incompleteStateFor(byte1, byte2, byte3); return incompleteStateFor(byte1, byte2, byte3);
} }
@ -1191,7 +1176,7 @@ final class Utf8 {
// byte3 trailing-byte test // byte3 trailing-byte test
|| byte3 > (byte) 0xBF || byte3 > (byte) 0xBF
// byte4 trailing-byte test // byte4 trailing-byte test
|| UNSAFE.getByte(address++) > (byte) 0xBF) { || UnsafeUtil.getByte(address++) > (byte) 0xBF) {
return MALFORMED; return MALFORMED;
} }
} }
@ -1202,7 +1187,7 @@ final class Utf8 {
@Override @Override
int encodeUtf8(final CharSequence in, final byte[] out, final int offset, final int length) { int encodeUtf8(final CharSequence in, final byte[] out, final int offset, final int length) {
long outIx = ARRAY_BASE_OFFSET + offset; long outIx = getArrayBaseOffset() + offset;
final long outLimit = outIx + length; final long outLimit = outIx + length;
final int inLimit = in.length(); final int inLimit = in.length();
if (inLimit > length || out.length - length < offset) { if (inLimit > length || out.length - length < offset) {
@ -1215,25 +1200,25 @@ final class Utf8 {
// https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination // https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination
int inIx = 0; int inIx = 0;
for (char c; inIx < inLimit && (c = in.charAt(inIx)) < 0x80; ++inIx) { for (char c; inIx < inLimit && (c = in.charAt(inIx)) < 0x80; ++inIx) {
UNSAFE.putByte(out, outIx++, (byte) c); UnsafeUtil.putByte(out, outIx++, (byte) c);
} }
if (inIx == inLimit) { if (inIx == inLimit) {
// We're done, it was ASCII encoded. // We're done, it was ASCII encoded.
return (int) (outIx - ARRAY_BASE_OFFSET); return (int) (outIx - getArrayBaseOffset());
} }
for (char c; inIx < inLimit; ++inIx) { for (char c; inIx < inLimit; ++inIx) {
c = in.charAt(inIx); c = in.charAt(inIx);
if (c < 0x80 && outIx < outLimit) { if (c < 0x80 && outIx < outLimit) {
UNSAFE.putByte(out, outIx++, (byte) c); UnsafeUtil.putByte(out, outIx++, (byte) c);
} else if (c < 0x800 && outIx <= outLimit - 2L) { // 11 bits, two UTF-8 bytes } else if (c < 0x800 && outIx <= outLimit - 2L) { // 11 bits, two UTF-8 bytes
UNSAFE.putByte(out, outIx++, (byte) ((0xF << 6) | (c >>> 6))); UnsafeUtil.putByte(out, outIx++, (byte) ((0xF << 6) | (c >>> 6)));
UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & c))); UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & c)));
} else if ((c < MIN_SURROGATE || MAX_SURROGATE < c) && outIx <= outLimit - 3L) { } else if ((c < MIN_SURROGATE || MAX_SURROGATE < c) && outIx <= outLimit - 3L) {
// Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes // Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes
UNSAFE.putByte(out, outIx++, (byte) ((0xF << 5) | (c >>> 12))); UnsafeUtil.putByte(out, outIx++, (byte) ((0xF << 5) | (c >>> 12)));
UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & (c >>> 6)))); UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & (c >>> 6))));
UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & c))); UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & c)));
} else if (outIx <= outLimit - 4L) { } else if (outIx <= outLimit - 4L) {
// Minimum code point represented by a surrogate pair is 0x10000, 17 bits, four UTF-8 // Minimum code point represented by a surrogate pair is 0x10000, 17 bits, four UTF-8
// bytes // bytes
@ -1242,10 +1227,10 @@ final class Utf8 {
throw new UnpairedSurrogateException((inIx - 1), inLimit); throw new UnpairedSurrogateException((inIx - 1), inLimit);
} }
int codePoint = toCodePoint(c, low); int codePoint = toCodePoint(c, low);
UNSAFE.putByte(out, outIx++, (byte) ((0xF << 4) | (codePoint >>> 18))); UnsafeUtil.putByte(out, outIx++, (byte) ((0xF << 4) | (codePoint >>> 18)));
UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 12)))); UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 12))));
UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 6)))); UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 6))));
UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & codePoint))); UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & codePoint)));
} else { } else {
if ((MIN_SURROGATE <= c && c <= MAX_SURROGATE) if ((MIN_SURROGATE <= c && c <= MAX_SURROGATE)
&& (inIx + 1 == inLimit || !isSurrogatePair(c, in.charAt(inIx + 1)))) { && (inIx + 1 == inLimit || !isSurrogatePair(c, in.charAt(inIx + 1)))) {
@ -1258,7 +1243,7 @@ final class Utf8 {
} }
// All bytes have been encoded. // All bytes have been encoded.
return (int) (outIx - ARRAY_BASE_OFFSET); return (int) (outIx - getArrayBaseOffset());
} }
@Override @Override
@ -1277,7 +1262,7 @@ final class Utf8 {
// https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination // https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination
int inIx = 0; int inIx = 0;
for (char c; inIx < inLimit && (c = in.charAt(inIx)) < 0x80; ++inIx) { for (char c; inIx < inLimit && (c = in.charAt(inIx)) < 0x80; ++inIx) {
UNSAFE.putByte(outIx++, (byte) c); UnsafeUtil.putByte(outIx++, (byte) c);
} }
if (inIx == inLimit) { if (inIx == inLimit) {
// We're done, it was ASCII encoded. // We're done, it was ASCII encoded.
@ -1288,15 +1273,15 @@ final class Utf8 {
for (char c; inIx < inLimit; ++inIx) { for (char c; inIx < inLimit; ++inIx) {
c = in.charAt(inIx); c = in.charAt(inIx);
if (c < 0x80 && outIx < outLimit) { if (c < 0x80 && outIx < outLimit) {
UNSAFE.putByte(outIx++, (byte) c); UnsafeUtil.putByte(outIx++, (byte) c);
} else if (c < 0x800 && outIx <= outLimit - 2L) { // 11 bits, two UTF-8 bytes } else if (c < 0x800 && outIx <= outLimit - 2L) { // 11 bits, two UTF-8 bytes
UNSAFE.putByte(outIx++, (byte) ((0xF << 6) | (c >>> 6))); UnsafeUtil.putByte(outIx++, (byte) ((0xF << 6) | (c >>> 6)));
UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & c))); UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & c)));
} else if ((c < MIN_SURROGATE || MAX_SURROGATE < c) && outIx <= outLimit - 3L) { } else if ((c < MIN_SURROGATE || MAX_SURROGATE < c) && outIx <= outLimit - 3L) {
// Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes // Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes
UNSAFE.putByte(outIx++, (byte) ((0xF << 5) | (c >>> 12))); UnsafeUtil.putByte(outIx++, (byte) ((0xF << 5) | (c >>> 12)));
UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & (c >>> 6)))); UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & (c >>> 6))));
UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & c))); UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & c)));
} else if (outIx <= outLimit - 4L) { } else if (outIx <= outLimit - 4L) {
// Minimum code point represented by a surrogate pair is 0x10000, 17 bits, four UTF-8 // Minimum code point represented by a surrogate pair is 0x10000, 17 bits, four UTF-8
// bytes // bytes
@ -1305,10 +1290,10 @@ final class Utf8 {
throw new UnpairedSurrogateException((inIx - 1), inLimit); throw new UnpairedSurrogateException((inIx - 1), inLimit);
} }
int codePoint = toCodePoint(c, low); int codePoint = toCodePoint(c, low);
UNSAFE.putByte(outIx++, (byte) ((0xF << 4) | (codePoint >>> 18))); UnsafeUtil.putByte(outIx++, (byte) ((0xF << 4) | (codePoint >>> 18)));
UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 12)))); UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 12))));
UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 6)))); UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 6))));
UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & codePoint))); UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & codePoint)));
} else { } else {
if ((MIN_SURROGATE <= c && c <= MAX_SURROGATE) if ((MIN_SURROGATE <= c && c <= MAX_SURROGATE)
&& (inIx + 1 == inLimit || !isSurrogatePair(c, in.charAt(inIx + 1)))) { && (inIx + 1 == inLimit || !isSurrogatePair(c, in.charAt(inIx + 1)))) {
@ -1349,7 +1334,7 @@ final class Utf8 {
// we're 8-byte aligned. // we're 8-byte aligned.
final int unaligned = (int) offset & 7; final int unaligned = (int) offset & 7;
for (int j = unaligned; j > 0; j--) { for (int j = unaligned; j > 0; j--) {
if (UNSAFE.getByte(bytes, offset++) < 0) { if (UnsafeUtil.getByte(bytes, offset++) < 0) {
return unaligned - j; return unaligned - j;
} }
} }
@ -1358,7 +1343,7 @@ final class Utf8 {
// To speed things up further, we're reading longs instead of bytes so we use a mask to // To speed things up further, we're reading longs instead of bytes so we use a mask to
// determine if any byte in the current long is non-ASCII. // determine if any byte in the current long is non-ASCII.
remaining -= unaligned; remaining -= unaligned;
for (; remaining >= 8 && (UNSAFE.getLong(bytes, offset) & ASCII_MASK_LONG) == 0; for (; remaining >= 8 && (UnsafeUtil.getLong(bytes, offset) & ASCII_MASK_LONG) == 0;
offset += 8, remaining -= 8) {} offset += 8, remaining -= 8) {}
return maxChars - remaining; return maxChars - remaining;
} }
@ -1379,7 +1364,7 @@ final class Utf8 {
// be read before we're 8-byte aligned. // be read before we're 8-byte aligned.
final int unaligned = (int) address & 7; final int unaligned = (int) address & 7;
for (int j = unaligned; j > 0; j--) { for (int j = unaligned; j > 0; j--) {
if (UNSAFE.getByte(address++) < 0) { if (UnsafeUtil.getByte(address++) < 0) {
return unaligned - j; return unaligned - j;
} }
} }
@ -1388,7 +1373,7 @@ final class Utf8 {
// To speed things up further, we're reading longs instead of bytes so we use a mask to // To speed things up further, we're reading longs instead of bytes so we use a mask to
// determine if any byte in the current long is non-ASCII. // determine if any byte in the current long is non-ASCII.
remaining -= unaligned; remaining -= unaligned;
for (; remaining >= 8 && (UNSAFE.getLong(address) & ASCII_MASK_LONG) == 0; for (; remaining >= 8 && (UnsafeUtil.getLong(address) & ASCII_MASK_LONG) == 0;
address += 8, remaining -= 8) {} address += 8, remaining -= 8) {}
return maxChars - remaining; return maxChars - remaining;
} }
@ -1404,7 +1389,7 @@ final class Utf8 {
// TODO(nathanmittler): Consider checking 8 bytes at a time after some threshold? // TODO(nathanmittler): Consider checking 8 bytes at a time after some threshold?
// Maybe after seeing a few in a row that are ASCII, go back to fast mode? // Maybe after seeing a few in a row that are ASCII, go back to fast mode?
int byte1 = 0; int byte1 = 0;
for (; remaining > 0 && (byte1 = UNSAFE.getByte(bytes, offset++)) >= 0; --remaining) { for (; remaining > 0 && (byte1 = UnsafeUtil.getByte(bytes, offset++)) >= 0; --remaining) {
} }
if (remaining == 0) { if (remaining == 0) {
return COMPLETE; return COMPLETE;
@ -1423,7 +1408,7 @@ final class Utf8 {
// Simultaneously checks for illegal trailing-byte in // Simultaneously checks for illegal trailing-byte in
// leading position and overlong 2-byte form. // leading position and overlong 2-byte form.
if (byte1 < (byte) 0xC2 if (byte1 < (byte) 0xC2
|| UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) { || UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
return MALFORMED; return MALFORMED;
} }
} else if (byte1 < (byte) 0xF0) { } else if (byte1 < (byte) 0xF0) {
@ -1435,13 +1420,13 @@ final class Utf8 {
remaining -= 2; remaining -= 2;
final int byte2; final int byte2;
if ((byte2 = UNSAFE.getByte(bytes, offset++)) > (byte) 0xBF if ((byte2 = UnsafeUtil.getByte(bytes, offset++)) > (byte) 0xBF
// overlong? 5 most significant bits must not all be zero // overlong? 5 most significant bits must not all be zero
|| (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0)
// check for illegal surrogate codepoints // check for illegal surrogate codepoints
|| (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
// byte3 trailing-byte test // byte3 trailing-byte test
|| UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) { || UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
return MALFORMED; return MALFORMED;
} }
} else { } else {
@ -1453,16 +1438,16 @@ final class Utf8 {
remaining -= 3; remaining -= 3;
final int byte2; final int byte2;
if ((byte2 = UNSAFE.getByte(bytes, offset++)) > (byte) 0xBF if ((byte2 = UnsafeUtil.getByte(bytes, offset++)) > (byte) 0xBF
// Check that 1 <= plane <= 16. Tricky optimized form of: // Check that 1 <= plane <= 16. Tricky optimized form of:
// if (byte1 > (byte) 0xF4 || // if (byte1 > (byte) 0xF4 ||
// byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 || // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
// byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F) // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
|| (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0
// byte3 trailing-byte test // byte3 trailing-byte test
|| UNSAFE.getByte(bytes, offset++) > (byte) 0xBF || UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF
// byte4 trailing-byte test // byte4 trailing-byte test
|| UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) { || UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
return MALFORMED; return MALFORMED;
} }
} }
@ -1480,7 +1465,7 @@ final class Utf8 {
// TODO(nathanmittler): Consider checking 8 bytes at a time after some threshold? // TODO(nathanmittler): Consider checking 8 bytes at a time after some threshold?
// Maybe after seeing a few in a row that are ASCII, go back to fast mode? // Maybe after seeing a few in a row that are ASCII, go back to fast mode?
int byte1 = 0; int byte1 = 0;
for (; remaining > 0 && (byte1 = UNSAFE.getByte(address++)) >= 0; --remaining) { for (; remaining > 0 && (byte1 = UnsafeUtil.getByte(address++)) >= 0; --remaining) {
} }
if (remaining == 0) { if (remaining == 0) {
return COMPLETE; return COMPLETE;
@ -1498,7 +1483,7 @@ final class Utf8 {
// Simultaneously checks for illegal trailing-byte in // Simultaneously checks for illegal trailing-byte in
// leading position and overlong 2-byte form. // leading position and overlong 2-byte form.
if (byte1 < (byte) 0xC2 || UNSAFE.getByte(address++) > (byte) 0xBF) { if (byte1 < (byte) 0xC2 || UnsafeUtil.getByte(address++) > (byte) 0xBF) {
return MALFORMED; return MALFORMED;
} }
} else if (byte1 < (byte) 0xF0) { } else if (byte1 < (byte) 0xF0) {
@ -1510,14 +1495,14 @@ final class Utf8 {
} }
remaining -= 2; remaining -= 2;
final byte byte2 = UNSAFE.getByte(address++); final byte byte2 = UnsafeUtil.getByte(address++);
if (byte2 > (byte) 0xBF if (byte2 > (byte) 0xBF
// overlong? 5 most significant bits must not all be zero // overlong? 5 most significant bits must not all be zero
|| (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0)
// check for illegal surrogate codepoints // check for illegal surrogate codepoints
|| (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
// byte3 trailing-byte test // byte3 trailing-byte test
|| UNSAFE.getByte(address++) > (byte) 0xBF) { || UnsafeUtil.getByte(address++) > (byte) 0xBF) {
return MALFORMED; return MALFORMED;
} }
} else { } else {
@ -1529,7 +1514,7 @@ final class Utf8 {
} }
remaining -= 3; remaining -= 3;
final byte byte2 = UNSAFE.getByte(address++); final byte byte2 = UnsafeUtil.getByte(address++);
if (byte2 > (byte) 0xBF if (byte2 > (byte) 0xBF
// Check that 1 <= plane <= 16. Tricky optimized form of: // Check that 1 <= plane <= 16. Tricky optimized form of:
// if (byte1 > (byte) 0xF4 || // if (byte1 > (byte) 0xF4 ||
@ -1537,9 +1522,9 @@ final class Utf8 {
// byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F) // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
|| (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0
// byte3 trailing-byte test // byte3 trailing-byte test
|| UNSAFE.getByte(address++) > (byte) 0xBF || UnsafeUtil.getByte(address++) > (byte) 0xBF
// byte4 trailing-byte test // byte4 trailing-byte test
|| UNSAFE.getByte(address++) > (byte) 0xBF) { || UnsafeUtil.getByte(address++) > (byte) 0xBF) {
return MALFORMED; return MALFORMED;
} }
} }
@ -1553,11 +1538,11 @@ final class Utf8 {
return incompleteStateFor(byte1); return incompleteStateFor(byte1);
} }
case 1: { case 1: {
return incompleteStateFor(byte1, UNSAFE.getByte(bytes, offset)); return incompleteStateFor(byte1, UnsafeUtil.getByte(bytes, offset));
} }
case 2: { case 2: {
return incompleteStateFor(byte1, UNSAFE.getByte(bytes, offset), return incompleteStateFor(byte1, UnsafeUtil.getByte(bytes, offset),
UNSAFE.getByte(bytes, offset + 1)); UnsafeUtil.getByte(bytes, offset + 1));
} }
default: { default: {
throw new AssertionError(); throw new AssertionError();
@ -1571,112 +1556,17 @@ final class Utf8 {
return incompleteStateFor(byte1); return incompleteStateFor(byte1);
} }
case 1: { case 1: {
return incompleteStateFor(byte1, UNSAFE.getByte(address)); return incompleteStateFor(byte1, UnsafeUtil.getByte(address));
} }
case 2: { case 2: {
return incompleteStateFor(byte1, UNSAFE.getByte(address), UNSAFE.getByte(address + 1)); return incompleteStateFor(byte1, UnsafeUtil.getByte(address),
UnsafeUtil.getByte(address + 1));
} }
default: { default: {
throw new AssertionError(); throw new AssertionError();
} }
} }
} }
/**
* Gets the field with the given name within the class, or {@code null} if not found. If
* found, the field is made accessible.
*/
private static Field field(Class<?> clazz, String fieldName) {
Field field;
try {
field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
} catch (Throwable t) {
// Failed to access the fields.
field = null;
}
logger.log(Level.FINEST, "{0}.{1}: {2}",
new Object[] {clazz.getName(), fieldName, (field != null ? "available" : "unavailable")});
return field;
}
/**
* Returns the offset of the provided field, or {@code -1} if {@code sun.misc.Unsafe} is not
* available.
*/
private static long fieldOffset(Field field) {
return field == null || UNSAFE == null ? -1 : UNSAFE.objectFieldOffset(field);
}
/**
* Get the base offset for byte arrays, or {@code -1} if {@code sun.misc.Unsafe} is not
* available.
*/
private static <T> int byteArrayBaseOffset() {
return UNSAFE == null ? -1 : UNSAFE.arrayBaseOffset(byte[].class);
}
/**
* Gets the offset of the {@code address} field of the given direct {@link ByteBuffer}.
*/
private static long addressOffset(ByteBuffer buffer) {
return UNSAFE.getLong(buffer, BUFFER_ADDRESS_OFFSET);
}
/**
* Gets the {@code sun.misc.Unsafe} instance, or {@code null} if not available on this
* platform.
*/
private static sun.misc.Unsafe getUnsafe() {
sun.misc.Unsafe unsafe = null;
try {
unsafe = AccessController.doPrivileged(new PrivilegedExceptionAction<sun.misc.Unsafe>() {
@Override
public sun.misc.Unsafe run() throws Exception {
Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class;
// Check that this platform supports all of the required unsafe methods.
checkRequiredMethods(k);
for (Field f : k.getDeclaredFields()) {
f.setAccessible(true);
Object x = f.get(null);
if (k.isInstance(x)) {
return k.cast(x);
}
}
// The sun.misc.Unsafe field does not exist.
return null;
}
});
} catch (Throwable e) {
// Catching Throwable here due to the fact that Google AppEngine raises NoClassDefFoundError
// for Unsafe.
}
logger.log(Level.FINEST, "sun.misc.Unsafe: {}",
unsafe != null ? "available" : "unavailable");
return unsafe;
}
/**
* Verifies that all required methods of {@code sun.misc.Unsafe} are available on this platform.
*/
private static void checkRequiredMethods(Class<sun.misc.Unsafe> clazz)
throws NoSuchMethodException, SecurityException {
// Needed for Unsafe byte[] access
clazz.getMethod("arrayBaseOffset", Class.class);
clazz.getMethod("getByte", Object.class, long.class);
clazz.getMethod("putByte", Object.class, long.class, byte.class);
clazz.getMethod("getLong", Object.class, long.class);
// Needed for Unsafe Direct ByteBuffer access
clazz.getMethod("objectFieldOffset", Field.class);
clazz.getMethod("getByte", long.class);
clazz.getMethod("getLong", Object.class, long.class);
clazz.getMethod("putByte", long.class, byte.class);
clazz.getMethod("getLong", long.class);
}
} }
private Utf8() {} private Utf8() {}

@ -45,9 +45,10 @@ import java.util.Iterator;
*/ */
public class BooleanArrayListTest extends TestCase { public class BooleanArrayListTest extends TestCase {
private static final BooleanArrayList UNARY_LIST = newImmutableBooleanArrayList(true); private static final BooleanArrayList UNARY_LIST =
newImmutableBooleanArrayList(true);
private static final BooleanArrayList TERTIARY_LIST = private static final BooleanArrayList TERTIARY_LIST =
newImmutableBooleanArrayList(true, true, false); newImmutableBooleanArrayList(true, false, true);
private BooleanArrayList list; private BooleanArrayList list;
@ -74,7 +75,7 @@ public class BooleanArrayListTest extends TestCase {
} }
public void testModificationWithIteration() { public void testModificationWithIteration() {
list.addAll(asList(true, false, false, true)); list.addAll(asList(true, false, true, false));
Iterator<Boolean> iterator = list.iterator(); Iterator<Boolean> iterator = list.iterator();
assertEquals(4, list.size()); assertEquals(4, list.size());
assertEquals(true, (boolean) list.get(0)); assertEquals(true, (boolean) list.get(0));
@ -102,8 +103,8 @@ public class BooleanArrayListTest extends TestCase {
public void testGet() { public void testGet() {
assertEquals(true, (boolean) TERTIARY_LIST.get(0)); assertEquals(true, (boolean) TERTIARY_LIST.get(0));
assertEquals(true, (boolean) TERTIARY_LIST.get(1)); assertEquals(false, (boolean) TERTIARY_LIST.get(1));
assertEquals(false, (boolean) TERTIARY_LIST.get(2)); assertEquals(true, (boolean) TERTIARY_LIST.get(2));
try { try {
TERTIARY_LIST.get(-1); TERTIARY_LIST.get(-1);
@ -120,10 +121,10 @@ public class BooleanArrayListTest extends TestCase {
} }
} }
public void testGetInt() { public void testGetBoolean() {
assertEquals(true, TERTIARY_LIST.getBoolean(0)); assertEquals(true, TERTIARY_LIST.getBoolean(0));
assertEquals(true, TERTIARY_LIST.getBoolean(1)); assertEquals(false, TERTIARY_LIST.getBoolean(1));
assertEquals(false, TERTIARY_LIST.getBoolean(2)); assertEquals(true, TERTIARY_LIST.getBoolean(2));
try { try {
TERTIARY_LIST.get(-1); TERTIARY_LIST.get(-1);
@ -169,7 +170,7 @@ public class BooleanArrayListTest extends TestCase {
assertEquals(false, list.getBoolean(1)); assertEquals(false, list.getBoolean(1));
try { try {
list.set(-1, true); list.set(-1, false);
fail(); fail();
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
// expected // expected
@ -183,7 +184,7 @@ public class BooleanArrayListTest extends TestCase {
} }
} }
public void testSetInt() { public void testSetBoolean() {
list.addBoolean(true); list.addBoolean(true);
list.addBoolean(true); list.addBoolean(true);
@ -201,7 +202,7 @@ public class BooleanArrayListTest extends TestCase {
} }
try { try {
list.setBoolean(2, true); list.setBoolean(2, false);
fail(); fail();
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
// expected // expected
@ -211,23 +212,25 @@ public class BooleanArrayListTest extends TestCase {
public void testAdd() { public void testAdd() {
assertEquals(0, list.size()); assertEquals(0, list.size());
assertTrue(list.add(true));
assertEquals(asList(true), list);
assertTrue(list.add(false)); assertTrue(list.add(false));
list.add(0, false); assertEquals(asList(false), list);
assertEquals(asList(false, true, false), list);
assertTrue(list.add(true));
list.add(0, false); list.add(0, false);
assertEquals(asList(false, false, true), list);
list.add(0, true); list.add(0, true);
list.add(0, false);
// Force a resize by getting up to 11 elements. // Force a resize by getting up to 11 elements.
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
list.add(true); list.add(i % 2 == 0);
} }
assertEquals(asList(true, false, false, true, false, true, true, true, true, true, true), list); assertEquals(
asList(false, true, false, false, true, true, false, true, false, true, false),
list);
try { try {
list.add(-1, false); list.add(-1, true);
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
// expected // expected
} }
@ -239,29 +242,29 @@ public class BooleanArrayListTest extends TestCase {
} }
} }
public void testAddInt() { public void testAddBoolean() {
assertEquals(0, list.size()); assertEquals(0, list.size());
list.addBoolean(true);
assertEquals(asList(true), list);
list.addBoolean(false); list.addBoolean(false);
assertEquals(asList(true, false), list); assertEquals(asList(false), list);
list.addBoolean(true);
assertEquals(asList(false, true), list);
} }
public void testAddAll() { public void testAddAll() {
assertEquals(0, list.size()); assertEquals(0, list.size());
assertTrue(list.addAll(Collections.singleton(false))); assertTrue(list.addAll(Collections.singleton(true)));
assertEquals(1, list.size()); assertEquals(1, list.size());
assertEquals(false, (boolean) list.get(0)); assertEquals(true, (boolean) list.get(0));
assertEquals(false, list.getBoolean(0)); assertEquals(true, list.getBoolean(0));
assertTrue(list.addAll(asList(true, false, false, false, true))); assertTrue(list.addAll(asList(false, true, false, true, false)));
assertEquals(asList(false, true, false, false, false, true), list); assertEquals(asList(true, false, true, false, true, false), list);
assertTrue(list.addAll(TERTIARY_LIST)); assertTrue(list.addAll(TERTIARY_LIST));
assertEquals(asList(false, true, false, false, false, true, true, true, false), list); assertEquals(asList(true, false, true, false, true, false, true, false, true), list);
assertFalse(list.addAll(Collections.<Boolean>emptyList())); assertFalse(list.addAll(Collections.<Boolean>emptyList()));
assertFalse(list.addAll(BooleanArrayList.emptyList())); assertFalse(list.addAll(BooleanArrayList.emptyList()));
@ -270,7 +273,7 @@ public class BooleanArrayListTest extends TestCase {
public void testRemove() { public void testRemove() {
list.addAll(TERTIARY_LIST); list.addAll(TERTIARY_LIST);
assertEquals(true, (boolean) list.remove(0)); assertEquals(true, (boolean) list.remove(0));
assertEquals(asList(true, false), list); assertEquals(asList(false, true), list);
assertTrue(list.remove(Boolean.TRUE)); assertTrue(list.remove(Boolean.TRUE));
assertEquals(asList(false), list); assertEquals(asList(false), list);
@ -296,8 +299,9 @@ public class BooleanArrayListTest extends TestCase {
} }
private void assertImmutable(BooleanArrayList list) { private void assertImmutable(BooleanArrayList list) {
try { try {
list.add(false); list.add(true);
fail(); fail();
} catch (UnsupportedOperationException e) { } catch (UnsupportedOperationException e) {
// expected // expected
@ -318,7 +322,7 @@ public class BooleanArrayListTest extends TestCase {
} }
try { try {
list.addAll(Collections.singletonList(false)); list.addAll(Collections.singletonList(true));
fail(); fail();
} catch (UnsupportedOperationException e) { } catch (UnsupportedOperationException e) {
// expected // expected
@ -360,7 +364,7 @@ public class BooleanArrayListTest extends TestCase {
} }
try { try {
list.addBoolean(true); list.addBoolean(false);
fail(); fail();
} catch (UnsupportedOperationException e) { } catch (UnsupportedOperationException e) {
// expected // expected
@ -416,7 +420,7 @@ public class BooleanArrayListTest extends TestCase {
} }
try { try {
list.retainAll(Collections.singleton(Boolean.TRUE)); list.removeAll(Collections.singleton(Boolean.TRUE));
fail(); fail();
} catch (UnsupportedOperationException e) { } catch (UnsupportedOperationException e) {
// expected // expected
@ -430,7 +434,7 @@ public class BooleanArrayListTest extends TestCase {
} }
try { try {
list.set(0, true); list.set(0, false);
fail(); fail();
} catch (UnsupportedOperationException e) { } catch (UnsupportedOperationException e) {
// expected // expected

@ -382,6 +382,14 @@ public class DescriptorsTest extends TestCase {
assertEquals(Long.valueOf(8765432109L), assertEquals(Long.valueOf(8765432109L),
field.getOptions().getExtension(UnittestCustomOptions.fieldOpt1)); field.getOptions().getExtension(UnittestCustomOptions.fieldOpt1));
OneofDescriptor oneof = descriptor.getOneofs().get(0);
assertNotNull(oneof);
assertTrue(
oneof.getOptions().hasExtension(UnittestCustomOptions.oneofOpt1));
assertEquals(Integer.valueOf(-99),
oneof.getOptions().getExtension(UnittestCustomOptions.oneofOpt1));
EnumDescriptor enumType = EnumDescriptor enumType =
UnittestCustomOptions.TestMessageWithCustomOptions.AnEnum.getDescriptor(); UnittestCustomOptions.TestMessageWithCustomOptions.AnEnum.getDescriptor();

@ -45,7 +45,8 @@ import java.util.Iterator;
*/ */
public class DoubleArrayListTest extends TestCase { public class DoubleArrayListTest extends TestCase {
private static final DoubleArrayList UNARY_LIST = newImmutableDoubleArrayList(1); private static final DoubleArrayList UNARY_LIST =
newImmutableDoubleArrayList(1);
private static final DoubleArrayList TERTIARY_LIST = private static final DoubleArrayList TERTIARY_LIST =
newImmutableDoubleArrayList(1, 2, 3); newImmutableDoubleArrayList(1, 2, 3);
@ -65,10 +66,10 @@ public class DoubleArrayListTest extends TestCase {
} }
public void testMakeImmutable() { public void testMakeImmutable() {
list.addDouble(2); list.addDouble(3);
list.addDouble(4); list.addDouble(4);
list.addDouble(6); list.addDouble(5);
list.addDouble(8); list.addDouble(7);
list.makeImmutable(); list.makeImmutable();
assertImmutable(list); assertImmutable(list);
} }
@ -120,7 +121,7 @@ public class DoubleArrayListTest extends TestCase {
} }
} }
public void testGetInt() { public void testGetDouble() {
assertEquals(1D, TERTIARY_LIST.getDouble(0)); assertEquals(1D, TERTIARY_LIST.getDouble(0));
assertEquals(2D, TERTIARY_LIST.getDouble(1)); assertEquals(2D, TERTIARY_LIST.getDouble(1));
assertEquals(3D, TERTIARY_LIST.getDouble(2)); assertEquals(3D, TERTIARY_LIST.getDouble(2));
@ -145,7 +146,7 @@ public class DoubleArrayListTest extends TestCase {
assertEquals(1, UNARY_LIST.size()); assertEquals(1, UNARY_LIST.size());
assertEquals(3, TERTIARY_LIST.size()); assertEquals(3, TERTIARY_LIST.size());
list.addDouble(2); list.addDouble(3);
list.addDouble(4); list.addDouble(4);
list.addDouble(6); list.addDouble(6);
list.addDouble(8); list.addDouble(8);
@ -154,7 +155,7 @@ public class DoubleArrayListTest extends TestCase {
list.remove(0); list.remove(0);
assertEquals(3, list.size()); assertEquals(3, list.size());
list.add(16D); list.add(17D);
assertEquals(4, list.size()); assertEquals(4, list.size());
} }
@ -162,8 +163,8 @@ public class DoubleArrayListTest extends TestCase {
list.addDouble(2); list.addDouble(2);
list.addDouble(4); list.addDouble(4);
assertEquals(2D, (double) list.set(0, 0D)); assertEquals(2D, (double) list.set(0, 3D));
assertEquals(0D, list.getDouble(0)); assertEquals(3D, list.getDouble(0));
assertEquals(4D, (double) list.set(1, 0D)); assertEquals(4D, (double) list.set(1, 0D));
assertEquals(0D, list.getDouble(1)); assertEquals(0D, list.getDouble(1));
@ -183,14 +184,14 @@ public class DoubleArrayListTest extends TestCase {
} }
} }
public void testSetInt() { public void testSetDouble() {
list.addDouble(2); list.addDouble(1);
list.addDouble(4); list.addDouble(3);
assertEquals(2D, list.setDouble(0, 0)); assertEquals(1D, list.setDouble(0, 0));
assertEquals(0D, list.getDouble(0)); assertEquals(0D, list.getDouble(0));
assertEquals(4D, list.setDouble(1, 0)); assertEquals(3D, list.setDouble(1, 0));
assertEquals(0D, list.getDouble(1)); assertEquals(0D, list.getDouble(1));
try { try {
@ -224,7 +225,9 @@ public class DoubleArrayListTest extends TestCase {
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
list.add(Double.valueOf(5 + i)); list.add(Double.valueOf(5 + i));
} }
assertEquals(asList(0D, 1D, 4D, 2D, 3D, 5D, 6D, 7D, 8D, 9D, 10D), list); assertEquals(
asList(0D, 1D, 4D, 2D, 3D, 5D, 6D, 7D, 8D, 9D, 10D),
list);
try { try {
list.add(-1, 5D); list.add(-1, 5D);
@ -239,7 +242,7 @@ public class DoubleArrayListTest extends TestCase {
} }
} }
public void testAddInt() { public void testAddDouble() {
assertEquals(0, list.size()); assertEquals(0, list.size());
list.addDouble(2); list.addDouble(2);

@ -0,0 +1,245 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import protobuf_unittest.NonNestedExtension;
import protobuf_unittest.NonNestedExtensionLite;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import java.lang.reflect.Method;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* Tests for {@link ExtensionRegistryFactory} and the {@link ExtensionRegistry} instances it
* creates.
*
* <p>This test simulates the runtime behaviour of the ExtensionRegistryFactory by delegating test
* definitions to two inner classes {@link InnerTest} and {@link InnerLiteTest}, the latter of
* which is executed using a custom ClassLoader, simulating the ProtoLite environment.
*
* <p>The test mechanism employed here is based on the pattern in
* {@code com.google.common.util.concurrent.AbstractFutureFallbackAtomicHelperTest}
*/
public class ExtensionRegistryFactoryTest extends TestCase {
// A classloader which blacklists some non-Lite classes.
private static final ClassLoader LITE_CLASS_LOADER = getLiteOnlyClassLoader();
/**
* Defines the set of test methods which will be run.
*/
static interface RegistryTests {
void testCreate();
void testEmpty();
void testIsFullRegistry();
void testAdd();
}
/**
* Test implementations for the non-Lite usage of ExtensionRegistryFactory.
*/
public static class InnerTest implements RegistryTests {
@Override
public void testCreate() {
ExtensionRegistryLite registry = ExtensionRegistryFactory.create();
assertEquals(registry.getClass(), ExtensionRegistry.class);
}
@Override
public void testEmpty() {
ExtensionRegistryLite emptyRegistry = ExtensionRegistryFactory.createEmpty();
assertEquals(emptyRegistry.getClass(), ExtensionRegistry.class);
assertEquals(emptyRegistry, ExtensionRegistry.EMPTY_REGISTRY);
}
@Override
public void testIsFullRegistry() {
ExtensionRegistryLite registry = ExtensionRegistryFactory.create();
assertTrue(ExtensionRegistryFactory.isFullRegistry(registry));
}
@Override
public void testAdd() {
ExtensionRegistryLite registry1 = ExtensionRegistryLite.newInstance();
NonNestedExtensionLite.registerAllExtensions(registry1);
registry1.add(NonNestedExtensionLite.nonNestedExtensionLite);
ExtensionRegistryLite registry2 = ExtensionRegistryLite.newInstance();
NonNestedExtension.registerAllExtensions((ExtensionRegistry) registry2);
registry2.add(NonNestedExtension.nonNestedExtension);
ExtensionRegistry fullRegistry1 = (ExtensionRegistry) registry1;
ExtensionRegistry fullRegistry2 = (ExtensionRegistry) registry2;
assertTrue("Test is using a non-lite extension",
GeneratedMessageLite.GeneratedExtension.class.isAssignableFrom(
NonNestedExtensionLite.nonNestedExtensionLite.getClass()));
assertNull("Extension is not registered in masqueraded full registry",
fullRegistry1.findImmutableExtensionByName("protobuf_unittest.nonNestedExtension"));
GeneratedMessageLite.GeneratedExtension<NonNestedExtensionLite.MessageLiteToBeExtended, ?>
extension = registry1.findLiteExtensionByNumber(
NonNestedExtensionLite.MessageLiteToBeExtended.getDefaultInstance(), 1);
assertNotNull("Extension registered in lite registry", extension);
assertTrue("Test is using a non-lite extension",
GeneratedMessage.GeneratedExtension.class.isAssignableFrom(
NonNestedExtension.nonNestedExtension.getClass()));
assertNotNull("Extension is registered in masqueraded full registry",
fullRegistry2.findImmutableExtensionByName("protobuf_unittest.nonNestedExtension"));
}
}
/**
* Test implementations for the Lite usage of ExtensionRegistryFactory.
*/
public static final class InnerLiteTest implements RegistryTests {
@Override
public void testCreate() {
ExtensionRegistryLite registry = ExtensionRegistryFactory.create();
assertEquals(registry.getClass(), ExtensionRegistryLite.class);
}
@Override
public void testEmpty() {
ExtensionRegistryLite emptyRegistry = ExtensionRegistryFactory.createEmpty();
assertEquals(emptyRegistry.getClass(), ExtensionRegistryLite.class);
assertEquals(emptyRegistry, ExtensionRegistryLite.EMPTY_REGISTRY_LITE);
}
@Override
public void testIsFullRegistry() {
ExtensionRegistryLite registry = ExtensionRegistryFactory.create();
assertFalse(ExtensionRegistryFactory.isFullRegistry(registry));
}
@Override
public void testAdd() {
ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance();
NonNestedExtensionLite.registerAllExtensions(registry);
GeneratedMessageLite.GeneratedExtension<NonNestedExtensionLite.MessageLiteToBeExtended, ?>
extension = registry.findLiteExtensionByNumber(
NonNestedExtensionLite.MessageLiteToBeExtended.getDefaultInstance(), 1);
assertNotNull("Extension is registered in Lite registry", extension);
}
}
/**
* Defines a suite of tests which the JUnit3 runner retrieves by reflection.
*/
public static Test suite() {
TestSuite suite = new TestSuite();
for (Method method : RegistryTests.class.getMethods()) {
suite.addTest(TestSuite.createTest(ExtensionRegistryFactoryTest.class, method.getName()));
}
return suite;
}
/**
* Sequentially runs first the Lite and then the non-Lite test variant via classloader
* manipulation.
*/
@Override
public void runTest() throws Exception {
ClassLoader storedClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(LITE_CLASS_LOADER);
try {
runTestMethod(LITE_CLASS_LOADER, InnerLiteTest.class);
} finally {
Thread.currentThread().setContextClassLoader(storedClassLoader);
}
try {
runTestMethod(storedClassLoader, InnerTest.class);
} finally {
Thread.currentThread().setContextClassLoader(storedClassLoader);
}
}
private void runTestMethod(ClassLoader classLoader, Class<? extends RegistryTests> testClass)
throws Exception {
classLoader.loadClass(ExtensionRegistryFactory.class.getName());
Class<?> test = classLoader.loadClass(testClass.getName());
String testName = getName();
test.getMethod(testName).invoke(test.newInstance());
}
/**
* Constructs a custom ClassLoader blacklisting the classes which are inspected in the SUT
* to determine the Lite/non-Lite runtime.
*/
private static ClassLoader getLiteOnlyClassLoader() {
ClassLoader testClassLoader = ExtensionRegistryFactoryTest.class.getClassLoader();
final Set<String> classNamesNotInLite =
Collections.unmodifiableSet(
new HashSet<String>(
Arrays.asList(
ExtensionRegistryFactory.FULL_REGISTRY_CLASS_NAME,
ExtensionRegistry.EXTENSION_CLASS_NAME)));
// Construct a URLClassLoader delegating to the system ClassLoader, and looking up classes
// in jar files based on the URLs already configured for this test's UrlClassLoader.
// Certain classes throw a ClassNotFoundException by design.
return new URLClassLoader(((URLClassLoader) testClassLoader).getURLs(),
ClassLoader.getSystemClassLoader()) {
@Override
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (classNamesNotInLite.contains(name)) {
throw new ClassNotFoundException("Class deliberately blacklisted by test.");
}
Class<?> loadedClass = null;
try {
loadedClass = findLoadedClass(name);
if (loadedClass == null) {
loadedClass = findClass(name);
if (resolve) {
resolveClass(loadedClass);
}
}
} catch (ClassNotFoundException e) {
loadedClass = super.loadClass(name, resolve);
}
return loadedClass;
}
};
}
}

@ -152,6 +152,26 @@ public class FieldPresenceTest extends TestCase {
assertFalse(message1.equals(message2)); assertFalse(message1.equals(message2));
} }
public void testLazyField() throws Exception {
// Test default constructed message.
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
TestAllTypes message = builder.build();
assertFalse(message.hasOptionalLazyMessage());
assertEquals(0, message.getSerializedSize());
assertEquals(ByteString.EMPTY, message.toByteString());
// Set default instance to the field.
builder.setOptionalLazyMessage(TestAllTypes.NestedMessage.getDefaultInstance());
message = builder.build();
assertTrue(message.hasOptionalLazyMessage());
assertEquals(2, message.getSerializedSize());
// Test parse zero-length from wire sets the presence.
TestAllTypes parsed = TestAllTypes.parseFrom(message.toByteString());
assertTrue(parsed.hasOptionalLazyMessage());
assertEquals(message.getOptionalLazyMessage(), parsed.getOptionalLazyMessage());
}
public void testFieldPresence() { public void testFieldPresence() {
// Optional non-message fields set to their default value are treated the // Optional non-message fields set to their default value are treated the
// same way as not set. // same way as not set.

@ -45,7 +45,8 @@ import java.util.Iterator;
*/ */
public class FloatArrayListTest extends TestCase { public class FloatArrayListTest extends TestCase {
private static final FloatArrayList UNARY_LIST = newImmutableFloatArrayList(1); private static final FloatArrayList UNARY_LIST =
newImmutableFloatArrayList(1);
private static final FloatArrayList TERTIARY_LIST = private static final FloatArrayList TERTIARY_LIST =
newImmutableFloatArrayList(1, 2, 3); newImmutableFloatArrayList(1, 2, 3);
@ -65,10 +66,10 @@ public class FloatArrayListTest extends TestCase {
} }
public void testMakeImmutable() { public void testMakeImmutable() {
list.addFloat(2); list.addFloat(3);
list.addFloat(4); list.addFloat(4);
list.addFloat(6); list.addFloat(5);
list.addFloat(8); list.addFloat(7);
list.makeImmutable(); list.makeImmutable();
assertImmutable(list); assertImmutable(list);
} }
@ -145,7 +146,7 @@ public class FloatArrayListTest extends TestCase {
assertEquals(1, UNARY_LIST.size()); assertEquals(1, UNARY_LIST.size());
assertEquals(3, TERTIARY_LIST.size()); assertEquals(3, TERTIARY_LIST.size());
list.addFloat(2); list.addFloat(3);
list.addFloat(4); list.addFloat(4);
list.addFloat(6); list.addFloat(6);
list.addFloat(8); list.addFloat(8);
@ -154,7 +155,7 @@ public class FloatArrayListTest extends TestCase {
list.remove(0); list.remove(0);
assertEquals(3, list.size()); assertEquals(3, list.size());
list.add(16F); list.add(17F);
assertEquals(4, list.size()); assertEquals(4, list.size());
} }
@ -162,8 +163,8 @@ public class FloatArrayListTest extends TestCase {
list.addFloat(2); list.addFloat(2);
list.addFloat(4); list.addFloat(4);
assertEquals(2F, (float) list.set(0, 0F)); assertEquals(2F, (float) list.set(0, 3F));
assertEquals(0F, list.getFloat(0)); assertEquals(3F, list.getFloat(0));
assertEquals(4F, (float) list.set(1, 0F)); assertEquals(4F, (float) list.set(1, 0F));
assertEquals(0F, list.getFloat(1)); assertEquals(0F, list.getFloat(1));
@ -184,13 +185,13 @@ public class FloatArrayListTest extends TestCase {
} }
public void testSetFloat() { public void testSetFloat() {
list.addFloat(2); list.addFloat(1);
list.addFloat(4); list.addFloat(3);
assertEquals(2F, list.setFloat(0, 0)); assertEquals(1F, list.setFloat(0, 0));
assertEquals(0F, list.getFloat(0)); assertEquals(0F, list.getFloat(0));
assertEquals(4F, list.setFloat(1, 0)); assertEquals(3F, list.setFloat(1, 0));
assertEquals(0F, list.getFloat(1)); assertEquals(0F, list.getFloat(1));
try { try {
@ -224,7 +225,9 @@ public class FloatArrayListTest extends TestCase {
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
list.add(Float.valueOf(5 + i)); list.add(Float.valueOf(5 + i));
} }
assertEquals(asList(0F, 1F, 4F, 2F, 3F, 5F, 6F, 7F, 8F, 9F, 10F), list); assertEquals(
asList(0F, 1F, 4F, 2F, 3F, 5F, 6F, 7F, 8F, 9F, 10F),
list);
try { try {
list.add(-1, 5F); list.add(-1, 5F);
@ -448,9 +451,9 @@ public class FloatArrayListTest extends TestCase {
} }
} }
private static FloatArrayList newImmutableFloatArrayList(int... elements) { private static FloatArrayList newImmutableFloatArrayList(float... elements) {
FloatArrayList list = new FloatArrayList(); FloatArrayList list = new FloatArrayList();
for (int element : elements) { for (float element : elements) {
list.addFloat(element); list.addFloat(element);
} }
list.makeImmutable(); list.makeImmutable();

@ -45,7 +45,8 @@ import java.util.Iterator;
*/ */
public class IntArrayListTest extends TestCase { public class IntArrayListTest extends TestCase {
private static final IntArrayList UNARY_LIST = newImmutableIntArrayList(1); private static final IntArrayList UNARY_LIST =
newImmutableIntArrayList(1);
private static final IntArrayList TERTIARY_LIST = private static final IntArrayList TERTIARY_LIST =
newImmutableIntArrayList(1, 2, 3); newImmutableIntArrayList(1, 2, 3);
@ -65,10 +66,10 @@ public class IntArrayListTest extends TestCase {
} }
public void testMakeImmutable() { public void testMakeImmutable() {
list.addInt(2); list.addInt(3);
list.addInt(4); list.addInt(4);
list.addInt(6); list.addInt(5);
list.addInt(8); list.addInt(7);
list.makeImmutable(); list.makeImmutable();
assertImmutable(list); assertImmutable(list);
} }
@ -145,7 +146,7 @@ public class IntArrayListTest extends TestCase {
assertEquals(1, UNARY_LIST.size()); assertEquals(1, UNARY_LIST.size());
assertEquals(3, TERTIARY_LIST.size()); assertEquals(3, TERTIARY_LIST.size());
list.addInt(2); list.addInt(3);
list.addInt(4); list.addInt(4);
list.addInt(6); list.addInt(6);
list.addInt(8); list.addInt(8);
@ -154,7 +155,7 @@ public class IntArrayListTest extends TestCase {
list.remove(0); list.remove(0);
assertEquals(3, list.size()); assertEquals(3, list.size());
list.add(16); list.add(17);
assertEquals(4, list.size()); assertEquals(4, list.size());
} }
@ -162,8 +163,8 @@ public class IntArrayListTest extends TestCase {
list.addInt(2); list.addInt(2);
list.addInt(4); list.addInt(4);
assertEquals(2, (int) list.set(0, 0)); assertEquals(2, (int) list.set(0, 3));
assertEquals(0, list.getInt(0)); assertEquals(3, list.getInt(0));
assertEquals(4, (int) list.set(1, 0)); assertEquals(4, (int) list.set(1, 0));
assertEquals(0, list.getInt(1)); assertEquals(0, list.getInt(1));
@ -184,13 +185,13 @@ public class IntArrayListTest extends TestCase {
} }
public void testSetInt() { public void testSetInt() {
list.addInt(2); list.addInt(1);
list.addInt(4); list.addInt(3);
assertEquals(2, list.setInt(0, 0)); assertEquals(1, list.setInt(0, 0));
assertEquals(0, list.getInt(0)); assertEquals(0, list.getInt(0));
assertEquals(4, list.setInt(1, 0)); assertEquals(3, list.setInt(1, 0));
assertEquals(0, list.getInt(1)); assertEquals(0, list.getInt(1));
try { try {
@ -222,9 +223,11 @@ public class IntArrayListTest extends TestCase {
list.add(0, 0); list.add(0, 0);
// Force a resize by getting up to 11 elements. // Force a resize by getting up to 11 elements.
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
list.add(5 + i); list.add(Integer.valueOf(5 + i));
} }
assertEquals(asList(0, 1, 4, 2, 3, 5, 6, 7, 8, 9, 10), list); assertEquals(
asList(0, 1, 4, 2, 3, 5, 6, 7, 8, 9, 10),
list);
try { try {
list.add(-1, 5); list.add(-1, 5);

@ -251,6 +251,23 @@ public class LazyMessageLiteTest extends TestCase {
assertEquals(42, merged.getOneofInner().getNumWithDefault()); assertEquals(42, merged.getOneofInner().getNumWithDefault());
} }
// Regression test for b/28198805.
public void testMergeOneofMessages() throws Exception {
LazyInnerMessageLite inner = LazyInnerMessageLite.newBuilder().build();
LazyMessageLite outer = LazyMessageLite.newBuilder().setOneofInner(inner).build();
ByteString data1 = outer.toByteString();
// The following should not alter the content of the 'outer' message.
LazyMessageLite.Builder merged = LazyMessageLite.newBuilder().mergeFrom(outer);
LazyInnerMessageLite anotherInner = LazyInnerMessageLite.newBuilder().setNum(12345).build();
merged.setOneofInner(anotherInner);
// Check that the 'outer' stays the same.
ByteString data2 = outer.toByteString();
assertEquals(data1, data2);
assertEquals(0, outer.getOneofInner().getNum());
}
public void testSerialize() throws InvalidProtocolBufferException { public void testSerialize() throws InvalidProtocolBufferException {
LazyNestedInnerMessageLite nested = LazyNestedInnerMessageLite.newBuilder() LazyNestedInnerMessageLite nested = LazyNestedInnerMessageLite.newBuilder()
.setNum(3) .setNum(3)

@ -1643,6 +1643,18 @@ public class LiteTest extends TestCase {
assertEquals(one.hashCode(), two.hashCode()); assertEquals(one.hashCode(), two.hashCode());
} }
public void testMergeFromNoLazyFieldSharing() throws Exception {
TestAllTypesLite.Builder sourceBuilder = TestAllTypesLite.newBuilder().setOptionalLazyMessage(
TestAllTypesLite.NestedMessage.newBuilder().setBb(1));
TestAllTypesLite.Builder targetBuilder =
TestAllTypesLite.newBuilder().mergeFrom(sourceBuilder.build());
assertEquals(1, sourceBuilder.getOptionalLazyMessage().getBb());
// now change the sourceBuilder, and target value shouldn't be affected.
sourceBuilder.setOptionalLazyMessage(
TestAllTypesLite.NestedMessage.newBuilder().setBb(2));
assertEquals(1, targetBuilder.getOptionalLazyMessage().getBb());
}
public void testEquals_notEqual() throws Exception { public void testEquals_notEqual() throws Exception {
TestAllTypesLite one = TestUtilLite.getAllLiteSetBuilder().build(); TestAllTypesLite one = TestUtilLite.getAllLiteSetBuilder().build();
byte[] bytes = one.toByteArray(); byte[] bytes = one.toByteArray();
@ -2203,6 +2215,21 @@ public class LiteTest extends TestCase {
assertEqualsAndHashCodeAreFalse(fooWithValueAndExtension, fooWithValueAndUnknownFields); assertEqualsAndHashCodeAreFalse(fooWithValueAndExtension, fooWithValueAndUnknownFields);
} }
public void testEqualsAndHashCodeWithExtensions() throws InvalidProtocolBufferException {
Foo fooWithOnlyValue = Foo.newBuilder()
.setValue(1)
.build();
Foo fooWithValueAndExtension = fooWithOnlyValue.toBuilder()
.setValue(1)
.setExtension(Bar.fooExt, Bar.newBuilder()
.setName("name")
.build())
.build();
assertEqualsAndHashCodeAreFalse(fooWithOnlyValue, fooWithValueAndExtension);
}
// Test to ensure we avoid a class cast exception with oneofs. // Test to ensure we avoid a class cast exception with oneofs.
public void testEquals_oneOfMessages() { public void testEquals_oneOfMessages() {
TestAllTypesLite mine = TestAllTypesLite.newBuilder() TestAllTypesLite mine = TestAllTypesLite.newBuilder()

@ -45,7 +45,8 @@ import java.util.Iterator;
*/ */
public class LongArrayListTest extends TestCase { public class LongArrayListTest extends TestCase {
private static final LongArrayList UNARY_LIST = newImmutableLongArrayList(1); private static final LongArrayList UNARY_LIST =
newImmutableLongArrayList(1);
private static final LongArrayList TERTIARY_LIST = private static final LongArrayList TERTIARY_LIST =
newImmutableLongArrayList(1, 2, 3); newImmutableLongArrayList(1, 2, 3);
@ -65,10 +66,10 @@ public class LongArrayListTest extends TestCase {
} }
public void testMakeImmutable() { public void testMakeImmutable() {
list.addLong(2); list.addLong(3);
list.addLong(4); list.addLong(4);
list.addLong(6); list.addLong(5);
list.addLong(8); list.addLong(7);
list.makeImmutable(); list.makeImmutable();
assertImmutable(list); assertImmutable(list);
} }
@ -77,10 +78,10 @@ public class LongArrayListTest extends TestCase {
list.addAll(asList(1L, 2L, 3L, 4L)); list.addAll(asList(1L, 2L, 3L, 4L));
Iterator<Long> iterator = list.iterator(); Iterator<Long> iterator = list.iterator();
assertEquals(4, list.size()); assertEquals(4, list.size());
assertEquals(1, (long) list.get(0)); assertEquals(1L, (long) list.get(0));
assertEquals(1, (long) iterator.next()); assertEquals(1L, (long) iterator.next());
list.set(0, 1L); list.set(0, 1L);
assertEquals(2, (long) iterator.next()); assertEquals(2L, (long) iterator.next());
list.remove(0); list.remove(0);
try { try {
@ -101,9 +102,9 @@ public class LongArrayListTest extends TestCase {
} }
public void testGet() { public void testGet() {
assertEquals(1, (long) TERTIARY_LIST.get(0)); assertEquals(1L, (long) TERTIARY_LIST.get(0));
assertEquals(2, (long) TERTIARY_LIST.get(1)); assertEquals(2L, (long) TERTIARY_LIST.get(1));
assertEquals(3, (long) TERTIARY_LIST.get(2)); assertEquals(3L, (long) TERTIARY_LIST.get(2));
try { try {
TERTIARY_LIST.get(-1); TERTIARY_LIST.get(-1);
@ -121,9 +122,9 @@ public class LongArrayListTest extends TestCase {
} }
public void testGetLong() { public void testGetLong() {
assertEquals(1, TERTIARY_LIST.getLong(0)); assertEquals(1L, TERTIARY_LIST.getLong(0));
assertEquals(2, TERTIARY_LIST.getLong(1)); assertEquals(2L, TERTIARY_LIST.getLong(1));
assertEquals(3, TERTIARY_LIST.getLong(2)); assertEquals(3L, TERTIARY_LIST.getLong(2));
try { try {
TERTIARY_LIST.get(-1); TERTIARY_LIST.get(-1);
@ -145,7 +146,7 @@ public class LongArrayListTest extends TestCase {
assertEquals(1, UNARY_LIST.size()); assertEquals(1, UNARY_LIST.size());
assertEquals(3, TERTIARY_LIST.size()); assertEquals(3, TERTIARY_LIST.size());
list.addLong(2); list.addLong(3);
list.addLong(4); list.addLong(4);
list.addLong(6); list.addLong(6);
list.addLong(8); list.addLong(8);
@ -154,7 +155,7 @@ public class LongArrayListTest extends TestCase {
list.remove(0); list.remove(0);
assertEquals(3, list.size()); assertEquals(3, list.size());
list.add(16L); list.add(17L);
assertEquals(4, list.size()); assertEquals(4, list.size());
} }
@ -162,11 +163,11 @@ public class LongArrayListTest extends TestCase {
list.addLong(2); list.addLong(2);
list.addLong(4); list.addLong(4);
assertEquals(2, (long) list.set(0, 0L)); assertEquals(2L, (long) list.set(0, 3L));
assertEquals(0, list.getLong(0)); assertEquals(3L, list.getLong(0));
assertEquals(4, (long) list.set(1, 0L)); assertEquals(4L, (long) list.set(1, 0L));
assertEquals(0, list.getLong(1)); assertEquals(0L, list.getLong(1));
try { try {
list.set(-1, 0L); list.set(-1, 0L);
@ -184,14 +185,14 @@ public class LongArrayListTest extends TestCase {
} }
public void testSetLong() { public void testSetLong() {
list.addLong(2); list.addLong(1);
list.addLong(4); list.addLong(3);
assertEquals(2, list.setLong(0, 0)); assertEquals(1L, list.setLong(0, 0));
assertEquals(0, list.getLong(0)); assertEquals(0L, list.getLong(0));
assertEquals(4, list.setLong(1, 0)); assertEquals(3L, list.setLong(1, 0));
assertEquals(0, list.getLong(1)); assertEquals(0L, list.getLong(1));
try { try {
list.setLong(-1, 0); list.setLong(-1, 0);
@ -224,7 +225,9 @@ public class LongArrayListTest extends TestCase {
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
list.add(Long.valueOf(5 + i)); list.add(Long.valueOf(5 + i));
} }
assertEquals(asList(0L, 1L, 4L, 2L, 3L, 5L, 6L, 7L, 8L, 9L, 10L), list); assertEquals(
asList(0L, 1L, 4L, 2L, 3L, 5L, 6L, 7L, 8L, 9L, 10L),
list);
try { try {
list.add(-1, 5L); list.add(-1, 5L);
@ -254,8 +257,8 @@ public class LongArrayListTest extends TestCase {
assertTrue(list.addAll(Collections.singleton(1L))); assertTrue(list.addAll(Collections.singleton(1L)));
assertEquals(1, list.size()); assertEquals(1, list.size());
assertEquals(1, (long) list.get(0)); assertEquals(1L, (long) list.get(0));
assertEquals(1, list.getLong(0)); assertEquals(1L, list.getLong(0));
assertTrue(list.addAll(asList(2L, 3L, 4L, 5L, 6L))); assertTrue(list.addAll(asList(2L, 3L, 4L, 5L, 6L)));
assertEquals(asList(1L, 2L, 3L, 4L, 5L, 6L), list); assertEquals(asList(1L, 2L, 3L, 4L, 5L, 6L), list);
@ -269,16 +272,16 @@ public class LongArrayListTest extends TestCase {
public void testRemove() { public void testRemove() {
list.addAll(TERTIARY_LIST); list.addAll(TERTIARY_LIST);
assertEquals(1, (long) list.remove(0)); assertEquals(1L, (long) list.remove(0));
assertEquals(asList(2L, 3L), list); assertEquals(asList(2L, 3L), list);
assertTrue(list.remove(3L)); assertTrue(list.remove(Long.valueOf(3)));
assertEquals(asList(2L), list); assertEquals(asList(2L), list);
assertFalse(list.remove(3L)); assertFalse(list.remove(Long.valueOf(3)));
assertEquals(asList(2L), list); assertEquals(asList(2L), list);
assertEquals(2, (long) list.remove(0)); assertEquals(2L, (long) list.remove(0));
assertEquals(asList(), list); assertEquals(asList(), list);
try { try {

@ -30,12 +30,16 @@
package com.google.protobuf; package com.google.protobuf;
import map_lite_test.MapForProto2TestProto.BizarroTestMap;
import map_lite_test.MapForProto2TestProto.TestMap; import map_lite_test.MapForProto2TestProto.TestMap;
import map_lite_test.MapForProto2TestProto.TestMap.MessageValue; import map_lite_test.MapForProto2TestProto.TestMap.MessageValue;
import map_lite_test.MapForProto2TestProto.TestMapOrBuilder;
import map_lite_test.MapForProto2TestProto.TestUnknownEnumValue; import map_lite_test.MapForProto2TestProto.TestUnknownEnumValue;
import junit.framework.TestCase; import junit.framework.TestCase;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@ -44,34 +48,40 @@ import java.util.Map;
/** /**
* Unit tests for map fields. * Unit tests for map fields.
*/ */
public class MapForProto2LiteTest extends TestCase { public final class MapForProto2LiteTest extends TestCase {
private void setMapValues(TestMap.Builder builder) {
builder.getMutableInt32ToInt32Field().put(1, 11);
builder.getMutableInt32ToInt32Field().put(2, 22);
builder.getMutableInt32ToInt32Field().put(3, 33);
builder.getMutableInt32ToStringField().put(1, "11");
builder.getMutableInt32ToStringField().put(2, "22");
builder.getMutableInt32ToStringField().put(3, "33");
builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("11"));
builder.getMutableInt32ToBytesField().put(2, TestUtil.toBytes("22"));
builder.getMutableInt32ToBytesField().put(3, TestUtil.toBytes("33"));
builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.FOO);
builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.BAR);
builder.getMutableInt32ToEnumField().put(3, TestMap.EnumValue.BAZ);
builder.getMutableInt32ToMessageField().put( private void setMapValues(TestMap.Builder builder) {
1, MessageValue.newBuilder().setValue(11).build()); builder
builder.getMutableInt32ToMessageField().put( .putInt32ToInt32Field(1, 11)
2, MessageValue.newBuilder().setValue(22).build()); .putInt32ToInt32Field(2, 22)
builder.getMutableInt32ToMessageField().put( .putInt32ToInt32Field(3, 33)
3, MessageValue.newBuilder().setValue(33).build());
.putInt32ToStringField(1, "11")
.putInt32ToStringField(2, "22")
.putInt32ToStringField(3, "33")
.putInt32ToBytesField(1, TestUtil.toBytes("11"))
.putInt32ToBytesField(2, TestUtil.toBytes("22"))
.putInt32ToBytesField(3, TestUtil.toBytes("33"))
.putInt32ToEnumField(1, TestMap.EnumValue.FOO)
.putInt32ToEnumField(2, TestMap.EnumValue.BAR)
.putInt32ToEnumField(3, TestMap.EnumValue.BAZ)
.putInt32ToMessageField(1, MessageValue.newBuilder().setValue(11).build())
.putInt32ToMessageField(2, MessageValue.newBuilder().setValue(22).build())
.putInt32ToMessageField(3, MessageValue.newBuilder().setValue(33).build())
.putStringToInt32Field("1", 11)
.putStringToInt32Field("2", 22)
.putStringToInt32Field("3", 33);
}
builder.getMutableStringToInt32Field().put("1", 11); public void testSetMapValues() {
builder.getMutableStringToInt32Field().put("2", 22); TestMap.Builder mapBuilder = TestMap.newBuilder();
builder.getMutableStringToInt32Field().put("3", 33); setMapValues(mapBuilder);
TestMap map = mapBuilder.build();
assertMapValuesSet(map);
} }
private void copyMapValues(TestMap source, TestMap.Builder destination) { private void copyMapValues(TestMap source, TestMap.Builder destination) {
@ -117,31 +127,42 @@ public class MapForProto2LiteTest extends TestCase {
} }
private void updateMapValues(TestMap.Builder builder) { private void updateMapValues(TestMap.Builder builder) {
builder.getMutableInt32ToInt32Field().put(1, 111); builder
builder.getMutableInt32ToInt32Field().remove(2); .putInt32ToInt32Field(1, 111)
builder.getMutableInt32ToInt32Field().put(4, 44); .removeInt32ToInt32Field(2)
.putInt32ToInt32Field(4, 44)
builder.getMutableInt32ToStringField().put(1, "111");
builder.getMutableInt32ToStringField().remove(2); .putInt32ToStringField(1, "111")
builder.getMutableInt32ToStringField().put(4, "44"); .removeInt32ToStringField(2)
.putInt32ToStringField(4, "44")
builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("111"));
builder.getMutableInt32ToBytesField().remove(2); .putInt32ToBytesField(1, TestUtil.toBytes("111"))
builder.getMutableInt32ToBytesField().put(4, TestUtil.toBytes("44")); .removeInt32ToBytesField(2)
.putInt32ToBytesField(4, TestUtil.toBytes("44"))
builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.BAR);
builder.getMutableInt32ToEnumField().remove(2); .putInt32ToEnumField(1, TestMap.EnumValue.BAR)
builder.getMutableInt32ToEnumField().put(4, TestMap.EnumValue.QUX); .removeInt32ToEnumField(2)
.putInt32ToEnumField(4, TestMap.EnumValue.QUX)
.putInt32ToMessageField(1, MessageValue.newBuilder().setValue(111).build())
.removeInt32ToMessageField(2)
.putInt32ToMessageField(4, MessageValue.newBuilder().setValue(44).build())
.putStringToInt32Field("1", 111)
.removeStringToInt32Field("2")
.putStringToInt32Field("4", 44);
}
builder.getMutableInt32ToMessageField().put( public void testUpdateMapValues() {
1, MessageValue.newBuilder().setValue(111).build()); TestMap.Builder mapBuilder = TestMap.newBuilder();
builder.getMutableInt32ToMessageField().remove(2); setMapValues(mapBuilder);
builder.getMutableInt32ToMessageField().put( TestMap map = mapBuilder.build();
4, MessageValue.newBuilder().setValue(44).build()); assertMapValuesSet(map);
builder.getMutableStringToInt32Field().put("1", 111); mapBuilder = map.toBuilder();
builder.getMutableStringToInt32Field().remove("2"); updateMapValues(mapBuilder);
builder.getMutableStringToInt32Field().put("4", 44); map = mapBuilder.build();
assertMapValuesUpdated(map);
} }
private void assertMapValuesUpdated(TestMap message) { private void assertMapValuesUpdated(TestMap message) {
@ -176,13 +197,19 @@ public class MapForProto2LiteTest extends TestCase {
assertEquals(44, message.getStringToInt32Field().get("4").intValue()); assertEquals(44, message.getStringToInt32Field().get("4").intValue());
} }
private void assertMapValuesCleared(TestMap message) { private void assertMapValuesCleared(TestMapOrBuilder testMapOrBuilder) {
assertEquals(0, message.getInt32ToInt32Field().size()); assertEquals(0, testMapOrBuilder.getInt32ToInt32Field().size());
assertEquals(0, message.getInt32ToStringField().size()); assertEquals(0, testMapOrBuilder.getInt32ToInt32FieldCount());
assertEquals(0, message.getInt32ToBytesField().size()); assertEquals(0, testMapOrBuilder.getInt32ToStringField().size());
assertEquals(0, message.getInt32ToEnumField().size()); assertEquals(0, testMapOrBuilder.getInt32ToStringFieldCount());
assertEquals(0, message.getInt32ToMessageField().size()); assertEquals(0, testMapOrBuilder.getInt32ToBytesField().size());
assertEquals(0, message.getStringToInt32Field().size()); assertEquals(0, testMapOrBuilder.getInt32ToBytesFieldCount());
assertEquals(0, testMapOrBuilder.getInt32ToEnumField().size());
assertEquals(0, testMapOrBuilder.getInt32ToEnumFieldCount());
assertEquals(0, testMapOrBuilder.getInt32ToMessageField().size());
assertEquals(0, testMapOrBuilder.getInt32ToMessageFieldCount());
assertEquals(0, testMapOrBuilder.getStringToInt32Field().size());
assertEquals(0, testMapOrBuilder.getStringToInt32FieldCount());
} }
public void testSanityCopyOnWrite() throws InvalidProtocolBufferException { public void testSanityCopyOnWrite() throws InvalidProtocolBufferException {
@ -192,133 +219,87 @@ public class MapForProto2LiteTest extends TestCase {
TestMap.Builder builder = TestMap.newBuilder(); TestMap.Builder builder = TestMap.newBuilder();
TestMap message = builder.build(); TestMap message = builder.build();
Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field(); builder.putInt32ToInt32Field(1, 2);
intMap.put(1, 2);
assertTrue(message.getInt32ToInt32Field().isEmpty()); assertTrue(message.getInt32ToInt32Field().isEmpty());
message = builder.build(); message = builder.build();
try {
intMap.put(2, 3);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
assertEquals(newMap(1, 2), message.getInt32ToInt32Field()); assertEquals(newMap(1, 2), message.getInt32ToInt32Field());
assertEquals(newMap(1, 2), builder.getInt32ToInt32Field()); assertEquals(newMap(1, 2), builder.getInt32ToInt32Field());
builder.getMutableInt32ToInt32Field().put(2, 3); builder.putInt32ToInt32Field(2, 3);
assertEquals(newMap(1, 2), message.getInt32ToInt32Field()); assertEquals(newMap(1, 2), message.getInt32ToInt32Field());
assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field()); assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field());
} }
public void testMutableMapLifecycle() { public void testGetMapIsImmutable() {
TestMap.Builder builder = TestMap.newBuilder(); TestMap.Builder builder = TestMap.newBuilder();
Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field(); assertMapsAreImmutable(builder);
intMap.put(1, 2); assertMapsAreImmutable(builder.build());
assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
setMapValues(builder);
assertMapsAreImmutable(builder);
assertMapsAreImmutable(builder.build());
}
private void assertMapsAreImmutable(TestMapOrBuilder testMapOrBuilder) {
assertImmutable(testMapOrBuilder.getInt32ToInt32Field(), 1, 2);
assertImmutable(testMapOrBuilder.getInt32ToStringField(), 1, "2");
assertImmutable(testMapOrBuilder.getInt32ToBytesField(), 1, TestUtil.toBytes("2"));
assertImmutable(testMapOrBuilder.getInt32ToEnumField(), 1, TestMap.EnumValue.FOO);
assertImmutable(
testMapOrBuilder.getInt32ToMessageField(), 1, MessageValue.getDefaultInstance());
assertImmutable(testMapOrBuilder.getStringToInt32Field(), "1", 2);
}
private <K, V> void assertImmutable(Map<K, V> map, K key, V value) {
try { try {
intMap.put(2, 3); map.put(key, value);
fail(); fail();
} catch (UnsupportedOperationException e) { } catch (UnsupportedOperationException e) {
// expected // expected
} }
assertEquals(newMap(1, 2), builder.getInt32ToInt32Field()); if (!map.isEmpty()) {
builder.getMutableInt32ToInt32Field().put(2, 3);
assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field());
Map<Integer, TestMap.EnumValue> enumMap = builder.getMutableInt32ToEnumField();
enumMap.put(1, TestMap.EnumValue.BAR);
assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.build().getInt32ToEnumField());
try { try {
enumMap.put(2, TestMap.EnumValue.FOO); map.entrySet().remove(map.entrySet().iterator().next());
fail(); fail();
} catch (UnsupportedOperationException e) { } catch (UnsupportedOperationException e) {
// expected // expected
} }
}
}
public void testMutableMapLifecycle() {
TestMap.Builder builder = TestMap.newBuilder()
.putInt32ToInt32Field(1, 2);
assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
assertEquals(newMap(1, 2), builder.getInt32ToInt32Field());
builder.putInt32ToInt32Field(2, 3);
assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field());
builder.putInt32ToEnumField(1, TestMap.EnumValue.BAR);
assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.build().getInt32ToEnumField());
assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.getInt32ToEnumField()); assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.getInt32ToEnumField());
builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.FOO); builder.putInt32ToEnumField(2, TestMap.EnumValue.FOO);
assertEquals( assertEquals(
newMap(1, TestMap.EnumValue.BAR, 2, TestMap.EnumValue.FOO), newMap(1, TestMap.EnumValue.BAR, 2, TestMap.EnumValue.FOO),
builder.getInt32ToEnumField()); builder.getInt32ToEnumField());
Map<Integer, String> stringMap = builder.getMutableInt32ToStringField(); builder.putInt32ToStringField(1, "1");
stringMap.put(1, "1");
assertEquals(newMap(1, "1"), builder.build().getInt32ToStringField()); assertEquals(newMap(1, "1"), builder.build().getInt32ToStringField());
try {
stringMap.put(2, "2");
fail();
} catch (UnsupportedOperationException e) {
// expected
}
assertEquals(newMap(1, "1"), builder.getInt32ToStringField()); assertEquals(newMap(1, "1"), builder.getInt32ToStringField());
builder.getMutableInt32ToStringField().put(2, "2"); builder.putInt32ToStringField(2, "2");
assertEquals( assertEquals(newMap(1, "1", 2, "2"), builder.getInt32ToStringField());
newMap(1, "1", 2, "2"),
builder.getInt32ToStringField());
Map<Integer, TestMap.MessageValue> messageMap = builder.getMutableInt32ToMessageField(); builder.putInt32ToMessageField(1, TestMap.MessageValue.getDefaultInstance());
messageMap.put(1, TestMap.MessageValue.getDefaultInstance());
assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()), assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()),
builder.build().getInt32ToMessageField()); builder.build().getInt32ToMessageField());
try {
messageMap.put(2, TestMap.MessageValue.getDefaultInstance());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()), assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()),
builder.getInt32ToMessageField()); builder.getInt32ToMessageField());
builder.getMutableInt32ToMessageField().put(2, TestMap.MessageValue.getDefaultInstance()); builder.putInt32ToMessageField(2, TestMap.MessageValue.getDefaultInstance());
assertEquals( assertEquals(
newMap(1, TestMap.MessageValue.getDefaultInstance(), newMap(1, TestMap.MessageValue.getDefaultInstance(),
2, TestMap.MessageValue.getDefaultInstance()), 2, TestMap.MessageValue.getDefaultInstance()),
builder.getInt32ToMessageField()); builder.getInt32ToMessageField());
} }
public void testMutableMapLifecycle_collections() {
TestMap.Builder builder = TestMap.newBuilder();
Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field();
intMap.put(1, 2);
assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
try {
intMap.remove(2);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
intMap.entrySet().remove(new Object());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
intMap.entrySet().iterator().remove();
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
intMap.keySet().remove(new Object());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
intMap.values().remove(new Object());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
intMap.values().iterator().remove();
fail();
} catch (UnsupportedOperationException e) {
// expected
}
assertEquals(newMap(1, 2), intMap);
assertEquals(newMap(1, 2), builder.getInt32ToInt32Field());
assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
}
public void testGettersAndSetters() throws Exception { public void testGettersAndSetters() throws Exception {
TestMap.Builder builder = TestMap.newBuilder(); TestMap.Builder builder = TestMap.newBuilder();
TestMap message = builder.build(); TestMap message = builder.build();
@ -336,6 +317,7 @@ public class MapForProto2LiteTest extends TestCase {
builder = message.toBuilder(); builder = message.toBuilder();
builder.clear(); builder.clear();
assertMapValuesCleared(builder);
message = builder.build(); message = builder.build();
assertMapValuesCleared(message); assertMapValuesCleared(message);
} }
@ -344,12 +326,52 @@ public class MapForProto2LiteTest extends TestCase {
TestMap.Builder sourceBuilder = TestMap.newBuilder(); TestMap.Builder sourceBuilder = TestMap.newBuilder();
setMapValues(sourceBuilder); setMapValues(sourceBuilder);
TestMap source = sourceBuilder.build(); TestMap source = sourceBuilder.build();
assertMapValuesSet(source);
TestMap.Builder destination = TestMap.newBuilder(); TestMap.Builder destination = TestMap.newBuilder();
copyMapValues(source, destination); copyMapValues(source, destination);
assertMapValuesSet(destination.build()); assertMapValuesSet(destination.build());
} }
public void testPutChecksNullKeysAndValues() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
try {
builder.putInt32ToStringField(1, null);
fail();
} catch (NullPointerException e) {
// expected.
}
try {
builder.putInt32ToBytesField(1, null);
fail();
} catch (NullPointerException e) {
// expected.
}
try {
builder.putInt32ToEnumField(1, null);
fail();
} catch (NullPointerException e) {
// expected.
}
try {
builder.putInt32ToMessageField(1, null);
fail();
} catch (NullPointerException e) {
// expected.
}
try {
builder.putStringToInt32Field(null, 1);
fail();
} catch (NullPointerException e) {
// expected.
}
}
public void testSerializeAndParse() throws Exception { public void testSerializeAndParse() throws Exception {
TestMap.Builder builder = TestMap.newBuilder(); TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder); setMapValues(builder);
@ -373,6 +395,55 @@ public class MapForProto2LiteTest extends TestCase {
assertMapValuesCleared(message); assertMapValuesCleared(message);
} }
private TestMap tryParseTestMap(BizarroTestMap bizarroMap) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
CodedOutputStream output = CodedOutputStream.newInstance(byteArrayOutputStream);
bizarroMap.writeTo(output);
output.flush();
return TestMap.parser().parseFrom(ByteString.copyFrom(byteArrayOutputStream.toByteArray()));
}
public void testParseError() throws Exception {
ByteString bytes = TestUtil.toBytes("SOME BYTES");
String stringKey = "a string key";
TestMap map = tryParseTestMap(BizarroTestMap.newBuilder()
.putInt32ToInt32Field(5, bytes)
.build());
assertEquals(map.getInt32ToInt32FieldOrDefault(5, -1), 0);
map = tryParseTestMap(BizarroTestMap.newBuilder()
.putInt32ToStringField(stringKey, 5)
.build());
assertEquals(map.getInt32ToStringFieldOrDefault(0, null), "");
map = tryParseTestMap(BizarroTestMap.newBuilder()
.putInt32ToBytesField(stringKey, 5)
.build());
assertEquals(map.getInt32ToBytesFieldOrDefault(0, null), ByteString.EMPTY);
map = tryParseTestMap(BizarroTestMap.newBuilder()
.putInt32ToEnumField(stringKey, bytes)
.build());
assertEquals(map.getInt32ToEnumFieldOrDefault(0, null), TestMap.EnumValue.FOO);
try {
tryParseTestMap(BizarroTestMap.newBuilder()
.putInt32ToMessageField(stringKey, bytes)
.build());
fail();
} catch (InvalidProtocolBufferException expected) {
assertTrue(expected.getUnfinishedMessage() instanceof TestMap);
map = (TestMap) expected.getUnfinishedMessage();
assertTrue(map.getInt32ToMessageField().isEmpty());
}
map = tryParseTestMap(BizarroTestMap.newBuilder()
.putStringToInt32Field(stringKey, bytes)
.build());
assertEquals(map.getStringToInt32FieldOrDefault(stringKey, -1), 0);
}
public void testMergeFrom() throws Exception { public void testMergeFrom() throws Exception {
TestMap.Builder builder = TestMap.newBuilder(); TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder); setMapValues(builder);
@ -389,23 +460,23 @@ public class MapForProto2LiteTest extends TestCase {
// We can't control the order of elements in a HashMap. The best we can do // We can't control the order of elements in a HashMap. The best we can do
// here is to add elements in different order. // here is to add elements in different order.
TestMap.Builder b1 = TestMap.newBuilder(); TestMap.Builder b1 = TestMap.newBuilder()
b1.getMutableInt32ToInt32Field().put(1, 2); .putInt32ToInt32Field(1, 2)
b1.getMutableInt32ToInt32Field().put(3, 4); .putInt32ToInt32Field(3, 4)
b1.getMutableInt32ToInt32Field().put(5, 6); .putInt32ToInt32Field(5, 6);
TestMap m1 = b1.build(); TestMap m1 = b1.build();
TestMap.Builder b2 = TestMap.newBuilder(); TestMap.Builder b2 = TestMap.newBuilder()
b2.getMutableInt32ToInt32Field().put(5, 6); .putInt32ToInt32Field(5, 6)
b2.getMutableInt32ToInt32Field().put(1, 2); .putInt32ToInt32Field(1, 2)
b2.getMutableInt32ToInt32Field().put(3, 4); .putInt32ToInt32Field(3, 4);
TestMap m2 = b2.build(); TestMap m2 = b2.build();
assertEquals(m1, m2); assertEquals(m1, m2);
assertEquals(m1.hashCode(), m2.hashCode()); assertEquals(m1.hashCode(), m2.hashCode());
// Make sure we did compare map fields. // Make sure we did compare map fields.
b2.getMutableInt32ToInt32Field().put(1, 0); b2.putInt32ToInt32Field(1, 0);
m2 = b2.build(); m2 = b2.build();
assertFalse(m1.equals(m2)); assertFalse(m1.equals(m2));
// Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed // Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed
@ -413,10 +484,9 @@ public class MapForProto2LiteTest extends TestCase {
} }
public void testUnknownEnumValues() throws Exception { public void testUnknownEnumValues() throws Exception {
TestUnknownEnumValue.Builder builder = TestUnknownEnumValue.Builder builder = TestUnknownEnumValue.newBuilder()
TestUnknownEnumValue.newBuilder(); .putInt32ToInt32Field(1, 1)
builder.getMutableInt32ToInt32Field().put(1, 1); .putInt32ToInt32Field(2, 54321);
builder.getMutableInt32ToInt32Field().put(2, 54321);
ByteString data = builder.build().toByteString(); ByteString data = builder.build().toByteString();
TestMap message = TestMap.parseFrom(data); TestMap message = TestMap.parseFrom(data);
@ -455,4 +525,275 @@ public class MapForProto2LiteTest extends TestCase {
map.put(key2, value2); map.put(key2, value2);
return map; return map;
} }
public void testGetMap() {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
TestMap message = builder.build();
assertEquals(
message.getStringToInt32Field(),
message.getStringToInt32FieldMap());
assertEquals(
message.getInt32ToBytesField(),
message.getInt32ToBytesFieldMap());
assertEquals(
message.getInt32ToEnumField(),
message.getInt32ToEnumFieldMap());
assertEquals(
message.getInt32ToMessageField(),
message.getInt32ToMessageFieldMap());
}
public void testContains() {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
assertMapContainsSetValues(builder);
assertMapContainsSetValues(builder.build());
}
private void assertMapContainsSetValues(TestMapOrBuilder testMapOrBuilder) {
assertTrue(testMapOrBuilder.containsInt32ToInt32Field(1));
assertTrue(testMapOrBuilder.containsInt32ToInt32Field(2));
assertTrue(testMapOrBuilder.containsInt32ToInt32Field(3));
assertFalse(testMapOrBuilder.containsInt32ToInt32Field(-1));
assertTrue(testMapOrBuilder.containsInt32ToStringField(1));
assertTrue(testMapOrBuilder.containsInt32ToStringField(2));
assertTrue(testMapOrBuilder.containsInt32ToStringField(3));
assertFalse(testMapOrBuilder.containsInt32ToStringField(-1));
assertTrue(testMapOrBuilder.containsInt32ToBytesField(1));
assertTrue(testMapOrBuilder.containsInt32ToBytesField(2));
assertTrue(testMapOrBuilder.containsInt32ToBytesField(3));
assertFalse(testMapOrBuilder.containsInt32ToBytesField(-1));
assertTrue(testMapOrBuilder.containsInt32ToEnumField(1));
assertTrue(testMapOrBuilder.containsInt32ToEnumField(2));
assertTrue(testMapOrBuilder.containsInt32ToEnumField(3));
assertFalse(testMapOrBuilder.containsInt32ToEnumField(-1));
assertTrue(testMapOrBuilder.containsInt32ToMessageField(1));
assertTrue(testMapOrBuilder.containsInt32ToMessageField(2));
assertTrue(testMapOrBuilder.containsInt32ToMessageField(3));
assertFalse(testMapOrBuilder.containsInt32ToMessageField(-1));
assertTrue(testMapOrBuilder.containsStringToInt32Field("1"));
assertTrue(testMapOrBuilder.containsStringToInt32Field("2"));
assertTrue(testMapOrBuilder.containsStringToInt32Field("3"));
assertFalse(testMapOrBuilder.containsStringToInt32Field("-1"));
}
public void testCount() {
TestMap.Builder builder = TestMap.newBuilder();
assertMapCounts(0, builder);
setMapValues(builder);
assertMapCounts(3, builder);
TestMap message = builder.build();
assertMapCounts(3, message);
builder = message.toBuilder().putInt32ToInt32Field(4, 44);
assertEquals(4, builder.getInt32ToInt32FieldCount());
assertEquals(4, builder.build().getInt32ToInt32FieldCount());
// already present - should be unchanged
builder.putInt32ToInt32Field(4, 44);
assertEquals(4, builder.getInt32ToInt32FieldCount());
}
private void assertMapCounts(int expectedCount, TestMapOrBuilder testMapOrBuilder) {
assertEquals(expectedCount, testMapOrBuilder.getInt32ToInt32FieldCount());
assertEquals(expectedCount, testMapOrBuilder.getInt32ToStringFieldCount());
assertEquals(expectedCount, testMapOrBuilder.getInt32ToBytesFieldCount());
assertEquals(expectedCount, testMapOrBuilder.getInt32ToEnumFieldCount());
assertEquals(expectedCount, testMapOrBuilder.getInt32ToMessageFieldCount());
assertEquals(expectedCount, testMapOrBuilder.getStringToInt32FieldCount());
}
public void testGetOrDefault() {
TestMap.Builder builder = TestMap.newBuilder();
assertMapCounts(0, builder);
setMapValues(builder);
doTestGetOrDefault(builder);
doTestGetOrDefault(builder.build());
}
public void doTestGetOrDefault(TestMapOrBuilder testMapOrBuilder) {
assertEquals(11, testMapOrBuilder.getInt32ToInt32FieldOrDefault(1, -11));
assertEquals(-11, testMapOrBuilder.getInt32ToInt32FieldOrDefault(-1, -11));
assertEquals("11", testMapOrBuilder.getInt32ToStringFieldOrDefault(1, "-11"));
assertNull("-11", testMapOrBuilder.getInt32ToStringFieldOrDefault(-1, null));
assertEquals(TestUtil.toBytes("11"), testMapOrBuilder.getInt32ToBytesFieldOrDefault(1, null));
assertNull(testMapOrBuilder.getInt32ToBytesFieldOrDefault(-1, null));
assertEquals(TestMap.EnumValue.FOO, testMapOrBuilder.getInt32ToEnumFieldOrDefault(1, null));
assertNull(testMapOrBuilder.getInt32ToEnumFieldOrDefault(-1, null));
assertEquals(MessageValue.newBuilder().setValue(11).build(),
testMapOrBuilder.getInt32ToMessageFieldOrDefault(1, null));
assertNull(testMapOrBuilder.getInt32ToMessageFieldOrDefault(-1, null));
assertEquals(11, testMapOrBuilder.getStringToInt32FieldOrDefault("1", -11));
assertEquals(-11, testMapOrBuilder.getStringToInt32FieldOrDefault("-1", -11));
try {
testMapOrBuilder.getStringToInt32FieldOrDefault(null, -11);
fail();
} catch (NullPointerException e) {
// expected
}
}
public void testGetOrThrow() {
TestMap.Builder builder = TestMap.newBuilder();
assertMapCounts(0, builder);
setMapValues(builder);
doTestGetOrDefault(builder);
doTestGetOrDefault(builder.build());
}
public void doTestGetOrThrow(TestMapOrBuilder testMapOrBuilder) {
assertEquals(11, testMapOrBuilder.getInt32ToInt32FieldOrThrow(1));
try {
testMapOrBuilder.getInt32ToInt32FieldOrThrow(-1);
fail();
} catch (IllegalArgumentException e) {
// expected
}
assertEquals("11", testMapOrBuilder.getInt32ToStringFieldOrThrow(1));
try {
testMapOrBuilder.getInt32ToStringFieldOrThrow(-1);
fail();
} catch (IllegalArgumentException e) {
// expected
}
assertEquals(TestUtil.toBytes("11"), testMapOrBuilder.getInt32ToBytesFieldOrThrow(1));
try {
testMapOrBuilder.getInt32ToBytesFieldOrThrow(-1);
fail();
} catch (IllegalArgumentException e) {
// expected
}
assertEquals(TestMap.EnumValue.FOO, testMapOrBuilder.getInt32ToEnumFieldOrThrow(1));
try {
testMapOrBuilder.getInt32ToEnumFieldOrThrow(-1);
fail();
} catch (IllegalArgumentException e) {
// expected
}
assertEquals(MessageValue.newBuilder().setValue(11).build(),
testMapOrBuilder.getInt32ToMessageFieldOrThrow(1));
try {
testMapOrBuilder.getInt32ToMessageFieldOrThrow(-1);
fail();
} catch (IllegalArgumentException e) {
// expected
}
assertEquals(11, testMapOrBuilder.getStringToInt32FieldOrThrow("1"));
try {
testMapOrBuilder.getStringToInt32FieldOrThrow("-1");
fail();
} catch (IllegalArgumentException e) {
// expected
}
try {
testMapOrBuilder.getStringToInt32FieldOrThrow(null);
fail();
} catch (NullPointerException e) {
// expected
}
}
public void testPut() {
TestMap.Builder builder = TestMap.newBuilder();
builder.putInt32ToInt32Field(1, 11);
assertEquals(11, builder.getInt32ToInt32FieldOrThrow(1));
builder.putInt32ToStringField(1, "a");
assertEquals("a", builder.getInt32ToStringFieldOrThrow(1));
try {
builder.putInt32ToStringField(1, null);
fail();
} catch (NullPointerException e) {
// expected
}
builder.putInt32ToBytesField(1, TestUtil.toBytes("11"));
assertEquals(TestUtil.toBytes("11"), builder.getInt32ToBytesFieldOrThrow(1));
try {
builder.putInt32ToBytesField(1, null);
fail();
} catch (NullPointerException e) {
// expected
}
builder.putInt32ToEnumField(1, TestMap.EnumValue.FOO);
assertEquals(TestMap.EnumValue.FOO, builder.getInt32ToEnumFieldOrThrow(1));
try {
builder.putInt32ToEnumField(1, null);
fail();
} catch (NullPointerException e) {
// expected
}
builder.putStringToInt32Field("a", 1);
assertEquals(1, builder.getStringToInt32FieldOrThrow("a"));
try {
builder.putStringToInt32Field(null, -1);
} catch (NullPointerException e) {
// expected
}
}
public void testRemove() {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
assertEquals(11, builder.getInt32ToInt32FieldOrThrow(1));
for (int times = 0; times < 2; times++) {
builder.removeInt32ToInt32Field(1);
assertEquals(-1, builder.getInt32ToInt32FieldOrDefault(1, -1));
}
assertEquals("11", builder.getInt32ToStringFieldOrThrow(1));
for (int times = 0; times < 2; times++) {
builder.removeInt32ToStringField(1);
assertNull(builder.getInt32ToStringFieldOrDefault(1, null));
}
assertEquals(TestUtil.toBytes("11"), builder.getInt32ToBytesFieldOrThrow(1));
for (int times = 0; times < 2; times++) {
builder.removeInt32ToBytesField(1);
assertNull(builder.getInt32ToBytesFieldOrDefault(1, null));
}
assertEquals(TestMap.EnumValue.FOO, builder.getInt32ToEnumFieldOrThrow(1));
for (int times = 0; times < 2; times++) {
builder.removeInt32ToEnumField(1);
assertNull(builder.getInt32ToEnumFieldOrDefault(1, null));
}
assertEquals(11, builder.getStringToInt32FieldOrThrow("1"));
for (int times = 0; times < 2; times++) {
builder.removeStringToInt32Field("1");
assertEquals(-1, builder.getStringToInt32FieldOrDefault("1", -1));
}
try {
builder.removeStringToInt32Field(null);
fail();
} catch (NullPointerException e) {
// expected
}
}
} }

@ -31,13 +31,17 @@
package com.google.protobuf; package com.google.protobuf;
import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.Descriptors.FieldDescriptor;
import map_test.MapForProto2TestProto.BizarroTestMap;
import map_test.MapForProto2TestProto.TestMap; import map_test.MapForProto2TestProto.TestMap;
import map_test.MapForProto2TestProto.TestMap.MessageValue; import map_test.MapForProto2TestProto.TestMap.MessageValue;
import map_test.MapForProto2TestProto.TestMap.MessageWithRequiredFields; import map_test.MapForProto2TestProto.TestMap.MessageWithRequiredFields;
import map_test.MapForProto2TestProto.TestMapOrBuilder;
import map_test.MapForProto2TestProto.TestRecursiveMap; import map_test.MapForProto2TestProto.TestRecursiveMap;
import map_test.MapForProto2TestProto.TestUnknownEnumValue; import map_test.MapForProto2TestProto.TestUnknownEnumValue;
import junit.framework.TestCase; import junit.framework.TestCase;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@ -48,7 +52,8 @@ import java.util.Map;
* Unit tests for map fields in proto2 protos. * Unit tests for map fields in proto2 protos.
*/ */
public class MapForProto2Test extends TestCase { public class MapForProto2Test extends TestCase {
private void setMapValues(TestMap.Builder builder) {
private void setMapValuesUsingMutableMap(TestMap.Builder builder) {
builder.getMutableInt32ToInt32Field().put(1, 11); builder.getMutableInt32ToInt32Field().put(1, 11);
builder.getMutableInt32ToInt32Field().put(2, 22); builder.getMutableInt32ToInt32Field().put(2, 22);
builder.getMutableInt32ToInt32Field().put(3, 33); builder.getMutableInt32ToInt32Field().put(3, 33);
@ -77,6 +82,46 @@ public class MapForProto2Test extends TestCase {
builder.getMutableStringToInt32Field().put("3", 33); builder.getMutableStringToInt32Field().put("3", 33);
} }
private void setMapValuesUsingAccessors(TestMap.Builder builder) {
builder
.putInt32ToInt32Field(1, 11)
.putInt32ToInt32Field(2, 22)
.putInt32ToInt32Field(3, 33)
.putInt32ToStringField(1, "11")
.putInt32ToStringField(2, "22")
.putInt32ToStringField(3, "33")
.putInt32ToBytesField(1, TestUtil.toBytes("11"))
.putInt32ToBytesField(2, TestUtil.toBytes("22"))
.putInt32ToBytesField(3, TestUtil.toBytes("33"))
.putInt32ToEnumField(1, TestMap.EnumValue.FOO)
.putInt32ToEnumField(2, TestMap.EnumValue.BAR)
.putInt32ToEnumField(3, TestMap.EnumValue.BAZ)
.putInt32ToMessageField(1, MessageValue.newBuilder().setValue(11).build())
.putInt32ToMessageField(2, MessageValue.newBuilder().setValue(22).build())
.putInt32ToMessageField(3, MessageValue.newBuilder().setValue(33).build())
.putStringToInt32Field("1", 11)
.putStringToInt32Field("2", 22)
.putStringToInt32Field("3", 33);
}
public void testSetMapValues() {
TestMap.Builder usingMutableMapBuilder = TestMap.newBuilder();
setMapValuesUsingMutableMap(usingMutableMapBuilder);
TestMap usingMutableMap = usingMutableMapBuilder.build();
assertMapValuesSet(usingMutableMap);
TestMap.Builder usingAccessorsBuilder = TestMap.newBuilder();
setMapValuesUsingAccessors(usingAccessorsBuilder);
TestMap usingAccessors = usingAccessorsBuilder.build();
assertMapValuesSet(usingAccessors);
assertEquals(usingAccessors, usingMutableMap);
}
private void copyMapValues(TestMap source, TestMap.Builder destination) { private void copyMapValues(TestMap source, TestMap.Builder destination) {
destination destination
.putAllInt32ToInt32Field(source.getInt32ToInt32Field()) .putAllInt32ToInt32Field(source.getInt32ToInt32Field())
@ -87,7 +132,7 @@ public class MapForProto2Test extends TestCase {
.putAllStringToInt32Field(source.getStringToInt32Field()); .putAllStringToInt32Field(source.getStringToInt32Field());
} }
private void assertMapValuesSet(TestMap message) { private void assertMapValuesSet(TestMapOrBuilder message) {
assertEquals(3, message.getInt32ToInt32Field().size()); assertEquals(3, message.getInt32ToInt32Field().size());
assertEquals(11, message.getInt32ToInt32Field().get(1).intValue()); assertEquals(11, message.getInt32ToInt32Field().get(1).intValue());
assertEquals(22, message.getInt32ToInt32Field().get(2).intValue()); assertEquals(22, message.getInt32ToInt32Field().get(2).intValue());
@ -119,7 +164,7 @@ public class MapForProto2Test extends TestCase {
assertEquals(33, message.getStringToInt32Field().get("3").intValue()); assertEquals(33, message.getStringToInt32Field().get("3").intValue());
} }
private void updateMapValues(TestMap.Builder builder) { private void updateMapValuesUsingMutableMap(TestMap.Builder builder) {
builder.getMutableInt32ToInt32Field().put(1, 111); builder.getMutableInt32ToInt32Field().put(1, 111);
builder.getMutableInt32ToInt32Field().remove(2); builder.getMutableInt32ToInt32Field().remove(2);
builder.getMutableInt32ToInt32Field().put(4, 44); builder.getMutableInt32ToInt32Field().put(4, 44);
@ -147,6 +192,58 @@ public class MapForProto2Test extends TestCase {
builder.getMutableStringToInt32Field().put("4", 44); builder.getMutableStringToInt32Field().put("4", 44);
} }
private void updateMapValuesUsingAccessors(TestMap.Builder builder) {
builder
.putInt32ToInt32Field(1, 111)
.removeInt32ToInt32Field(2)
.putInt32ToInt32Field(4, 44)
.putInt32ToStringField(1, "111")
.removeInt32ToStringField(2)
.putInt32ToStringField(4, "44")
.putInt32ToBytesField(1, TestUtil.toBytes("111"))
.removeInt32ToBytesField(2)
.putInt32ToBytesField(4, TestUtil.toBytes("44"))
.putInt32ToEnumField(1, TestMap.EnumValue.BAR)
.removeInt32ToEnumField(2)
.putInt32ToEnumField(4, TestMap.EnumValue.QUX)
.putInt32ToMessageField(1, MessageValue.newBuilder().setValue(111).build())
.removeInt32ToMessageField(2)
.putInt32ToMessageField(4, MessageValue.newBuilder().setValue(44).build())
.putStringToInt32Field("1", 111)
.removeStringToInt32Field("2")
.putStringToInt32Field("4", 44);
}
public void testUpdateMapValues() {
TestMap.Builder usingMutableMapBuilder = TestMap.newBuilder();
setMapValuesUsingMutableMap(usingMutableMapBuilder);
TestMap usingMutableMap = usingMutableMapBuilder.build();
assertMapValuesSet(usingMutableMap);
TestMap.Builder usingAccessorsBuilder = TestMap.newBuilder();
setMapValuesUsingAccessors(usingAccessorsBuilder);
TestMap usingAccessors = usingAccessorsBuilder.build();
assertMapValuesSet(usingAccessors);
assertEquals(usingAccessors, usingMutableMap);
usingMutableMapBuilder = usingMutableMap.toBuilder();
updateMapValuesUsingMutableMap(usingMutableMapBuilder);
usingMutableMap = usingMutableMapBuilder.build();
assertMapValuesUpdated(usingMutableMap);
usingAccessorsBuilder = usingAccessors.toBuilder();
updateMapValuesUsingAccessors(usingAccessorsBuilder);
usingAccessors = usingAccessorsBuilder.build();
assertMapValuesUpdated(usingAccessors);
assertEquals(usingAccessors, usingMutableMap);
}
private void assertMapValuesUpdated(TestMap message) { private void assertMapValuesUpdated(TestMap message) {
assertEquals(3, message.getInt32ToInt32Field().size()); assertEquals(3, message.getInt32ToInt32Field().size());
assertEquals(111, message.getInt32ToInt32Field().get(1).intValue()); assertEquals(111, message.getInt32ToInt32Field().get(1).intValue());
@ -179,13 +276,48 @@ public class MapForProto2Test extends TestCase {
assertEquals(44, message.getStringToInt32Field().get("4").intValue()); assertEquals(44, message.getStringToInt32Field().get("4").intValue());
} }
private void assertMapValuesCleared(TestMap message) { private void assertMapValuesCleared(TestMapOrBuilder testMapOrBuilder) {
assertEquals(0, message.getInt32ToInt32Field().size()); assertEquals(0, testMapOrBuilder.getInt32ToInt32Field().size());
assertEquals(0, message.getInt32ToStringField().size()); assertEquals(0, testMapOrBuilder.getInt32ToInt32FieldCount());
assertEquals(0, message.getInt32ToBytesField().size()); assertEquals(0, testMapOrBuilder.getInt32ToStringField().size());
assertEquals(0, message.getInt32ToEnumField().size()); assertEquals(0, testMapOrBuilder.getInt32ToStringFieldCount());
assertEquals(0, message.getInt32ToMessageField().size()); assertEquals(0, testMapOrBuilder.getInt32ToBytesField().size());
assertEquals(0, message.getStringToInt32Field().size()); assertEquals(0, testMapOrBuilder.getInt32ToBytesFieldCount());
assertEquals(0, testMapOrBuilder.getInt32ToEnumField().size());
assertEquals(0, testMapOrBuilder.getInt32ToEnumFieldCount());
assertEquals(0, testMapOrBuilder.getInt32ToMessageField().size());
assertEquals(0, testMapOrBuilder.getInt32ToMessageFieldCount());
assertEquals(0, testMapOrBuilder.getStringToInt32Field().size());
assertEquals(0, testMapOrBuilder.getStringToInt32FieldCount());
}
public void testGetMapIsImmutable() {
TestMap.Builder builder = TestMap.newBuilder();
assertMapsAreImmutable(builder);
assertMapsAreImmutable(builder.build());
setMapValuesUsingAccessors(builder);
assertMapsAreImmutable(builder);
assertMapsAreImmutable(builder.build());
}
private void assertMapsAreImmutable(TestMapOrBuilder testMapOrBuilder) {
assertImmutable(testMapOrBuilder.getInt32ToInt32Field(), 1, 2);
assertImmutable(testMapOrBuilder.getInt32ToStringField(), 1, "2");
assertImmutable(testMapOrBuilder.getInt32ToBytesField(), 1, TestUtil.toBytes("2"));
assertImmutable(testMapOrBuilder.getInt32ToEnumField(), 1, TestMap.EnumValue.FOO);
assertImmutable(
testMapOrBuilder.getInt32ToMessageField(), 1, MessageValue.getDefaultInstance());
assertImmutable(testMapOrBuilder.getStringToInt32Field(), "1", 2);
}
private <K, V> void assertImmutable(Map<K, V> map, K key, V value) {
try {
map.put(key, value);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
} }
public void testMutableMapLifecycle() { public void testMutableMapLifecycle() {
@ -304,41 +436,84 @@ public class MapForProto2Test extends TestCase {
assertMapValuesCleared(message); assertMapValuesCleared(message);
builder = message.toBuilder(); builder = message.toBuilder();
setMapValues(builder); setMapValuesUsingMutableMap(builder);
message = builder.build(); message = builder.build();
assertMapValuesSet(message); assertMapValuesSet(message);
builder = message.toBuilder(); builder = message.toBuilder();
updateMapValues(builder); updateMapValuesUsingMutableMap(builder);
message = builder.build(); message = builder.build();
assertMapValuesUpdated(message); assertMapValuesUpdated(message);
builder = message.toBuilder(); builder = message.toBuilder();
builder.clear(); builder.clear();
assertMapValuesCleared(builder);
message = builder.build(); message = builder.build();
assertMapValuesCleared(message); assertMapValuesCleared(message);
} }
public void testPutAll() throws Exception { public void testPutAll() throws Exception {
TestMap.Builder sourceBuilder = TestMap.newBuilder(); TestMap.Builder sourceBuilder = TestMap.newBuilder();
setMapValues(sourceBuilder); setMapValuesUsingMutableMap(sourceBuilder);
TestMap source = sourceBuilder.build(); TestMap source = sourceBuilder.build();
assertMapValuesSet(source);
TestMap.Builder destination = TestMap.newBuilder(); TestMap.Builder destination = TestMap.newBuilder();
copyMapValues(source, destination); copyMapValues(source, destination);
assertMapValuesSet(destination.build()); assertMapValuesSet(destination.build());
assertEquals(3, destination.getInt32ToEnumFieldCount());
}
public void testPutChecksNullKeysAndValues() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
try {
builder.putInt32ToStringField(1, null);
fail();
} catch (NullPointerException e) {
// expected.
}
try {
builder.putInt32ToBytesField(1, null);
fail();
} catch (NullPointerException e) {
// expected.
}
try {
builder.putInt32ToEnumField(1, null);
fail();
} catch (NullPointerException e) {
// expected.
}
try {
builder.putInt32ToMessageField(1, null);
fail();
} catch (NullPointerException e) {
// expected.
}
try {
builder.putStringToInt32Field(null, 1);
fail();
} catch (NullPointerException e) {
// expected.
}
} }
public void testSerializeAndParse() throws Exception { public void testSerializeAndParse() throws Exception {
TestMap.Builder builder = TestMap.newBuilder(); TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder); setMapValuesUsingMutableMap(builder);
TestMap message = builder.build(); TestMap message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size()); assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.parser().parseFrom(message.toByteString()); message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesSet(message); assertMapValuesSet(message);
builder = message.toBuilder(); builder = message.toBuilder();
updateMapValues(builder); updateMapValuesUsingMutableMap(builder);
message = builder.build(); message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size()); assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.parser().parseFrom(message.toByteString()); message = TestMap.parser().parseFrom(message.toByteString());
@ -352,9 +527,58 @@ public class MapForProto2Test extends TestCase {
assertMapValuesCleared(message); assertMapValuesCleared(message);
} }
private TestMap tryParseTestMap(BizarroTestMap bizarroMap) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
CodedOutputStream output = CodedOutputStream.newInstance(byteArrayOutputStream);
bizarroMap.writeTo(output);
output.flush();
return TestMap.parser().parseFrom(ByteString.copyFrom(byteArrayOutputStream.toByteArray()));
}
public void testParseError() throws Exception {
ByteString bytes = TestUtil.toBytes("SOME BYTES");
String stringKey = "a string key";
TestMap map = tryParseTestMap(BizarroTestMap.newBuilder()
.putInt32ToInt32Field(5, bytes)
.build());
assertEquals(map.getInt32ToInt32FieldOrDefault(5, -1), 0);
map = tryParseTestMap(BizarroTestMap.newBuilder()
.putInt32ToStringField(stringKey, 5)
.build());
assertEquals(map.getInt32ToStringFieldOrDefault(0, null), "");
map = tryParseTestMap(BizarroTestMap.newBuilder()
.putInt32ToBytesField(stringKey, 5)
.build());
assertEquals(map.getInt32ToBytesFieldOrDefault(0, null), ByteString.EMPTY);
map = tryParseTestMap(BizarroTestMap.newBuilder()
.putInt32ToEnumField(stringKey, bytes)
.build());
assertEquals(map.getInt32ToEnumFieldOrDefault(0, null), TestMap.EnumValue.FOO);
try {
tryParseTestMap(BizarroTestMap.newBuilder()
.putInt32ToMessageField(stringKey, bytes)
.build());
fail();
} catch (InvalidProtocolBufferException expected) {
assertTrue(expected.getUnfinishedMessage() instanceof TestMap);
map = (TestMap) expected.getUnfinishedMessage();
assertTrue(map.getInt32ToMessageField().isEmpty());
}
map = tryParseTestMap(BizarroTestMap.newBuilder()
.putStringToInt32Field(stringKey, bytes)
.build());
assertEquals(map.getStringToInt32FieldOrDefault(stringKey, -1), 0);
}
public void testMergeFrom() throws Exception { public void testMergeFrom() throws Exception {
TestMap.Builder builder = TestMap.newBuilder(); TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder); setMapValuesUsingMutableMap(builder);
TestMap message = builder.build(); TestMap message = builder.build();
TestMap.Builder other = TestMap.newBuilder(); TestMap.Builder other = TestMap.newBuilder();
@ -449,8 +673,7 @@ public class MapForProto2Test extends TestCase {
builder.setField(field, entryList); builder.setField(field, entryList);
} }
private static <KeyType, ValueType> private static <KeyType, ValueType> Map<KeyType, ValueType> mapForValues(
Map<KeyType, ValueType> mapForValues(
KeyType key1, ValueType value1, KeyType key2, ValueType value2) { KeyType key1, ValueType value1, KeyType key2, ValueType value2) {
Map<KeyType, ValueType> map = new HashMap<KeyType, ValueType>(); Map<KeyType, ValueType> map = new HashMap<KeyType, ValueType>();
map.put(key1, value1); map.put(key1, value1);
@ -536,7 +759,7 @@ public class MapForProto2Test extends TestCase {
public void testTextFormat() throws Exception { public void testTextFormat() throws Exception {
TestMap.Builder builder = TestMap.newBuilder(); TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder); setMapValuesUsingMutableMap(builder);
TestMap message = builder.build(); TestMap message = builder.build();
String textData = TextFormat.printToString(message); String textData = TextFormat.printToString(message);
@ -550,7 +773,7 @@ public class MapForProto2Test extends TestCase {
public void testDynamicMessage() throws Exception { public void testDynamicMessage() throws Exception {
TestMap.Builder builder = TestMap.newBuilder(); TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder); setMapValuesUsingMutableMap(builder);
TestMap message = builder.build(); TestMap message = builder.build();
Message dynamicDefaultInstance = Message dynamicDefaultInstance =
@ -646,13 +869,266 @@ public class MapForProto2Test extends TestCase {
public void testIterationOrder() throws Exception { public void testIterationOrder() throws Exception {
TestMap.Builder builder = TestMap.newBuilder(); TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder); setMapValuesUsingMutableMap(builder);
TestMap message = builder.build(); TestMap message = builder.build();
assertEquals(Arrays.asList("1", "2", "3"), assertEquals(Arrays.asList("1", "2", "3"),
new ArrayList<String>(message.getStringToInt32Field().keySet())); new ArrayList<String>(message.getStringToInt32Field().keySet()));
} }
public void testContains() {
TestMap.Builder builder = TestMap.newBuilder();
setMapValuesUsingMutableMap(builder);
assertMapContainsSetValues(builder);
assertMapContainsSetValues(builder.build());
}
private void assertMapContainsSetValues(TestMapOrBuilder testMapOrBuilder) {
assertTrue(testMapOrBuilder.containsInt32ToInt32Field(1));
assertTrue(testMapOrBuilder.containsInt32ToInt32Field(2));
assertTrue(testMapOrBuilder.containsInt32ToInt32Field(3));
assertFalse(testMapOrBuilder.containsInt32ToInt32Field(-1));
assertTrue(testMapOrBuilder.containsInt32ToStringField(1));
assertTrue(testMapOrBuilder.containsInt32ToStringField(2));
assertTrue(testMapOrBuilder.containsInt32ToStringField(3));
assertFalse(testMapOrBuilder.containsInt32ToStringField(-1));
assertTrue(testMapOrBuilder.containsInt32ToBytesField(1));
assertTrue(testMapOrBuilder.containsInt32ToBytesField(2));
assertTrue(testMapOrBuilder.containsInt32ToBytesField(3));
assertFalse(testMapOrBuilder.containsInt32ToBytesField(-1));
assertTrue(testMapOrBuilder.containsInt32ToEnumField(1));
assertTrue(testMapOrBuilder.containsInt32ToEnumField(2));
assertTrue(testMapOrBuilder.containsInt32ToEnumField(3));
assertFalse(testMapOrBuilder.containsInt32ToEnumField(-1));
assertTrue(testMapOrBuilder.containsInt32ToMessageField(1));
assertTrue(testMapOrBuilder.containsInt32ToMessageField(2));
assertTrue(testMapOrBuilder.containsInt32ToMessageField(3));
assertFalse(testMapOrBuilder.containsInt32ToMessageField(-1));
assertTrue(testMapOrBuilder.containsStringToInt32Field("1"));
assertTrue(testMapOrBuilder.containsStringToInt32Field("2"));
assertTrue(testMapOrBuilder.containsStringToInt32Field("3"));
assertFalse(testMapOrBuilder.containsStringToInt32Field("-1"));
}
public void testCount() {
TestMap.Builder builder = TestMap.newBuilder();
assertMapCounts(0, builder);
setMapValuesUsingMutableMap(builder);
assertMapCounts(3, builder);
TestMap message = builder.build();
assertMapCounts(3, message);
builder = message.toBuilder().putInt32ToInt32Field(4, 44);
assertEquals(4, builder.getInt32ToInt32FieldCount());
assertEquals(4, builder.build().getInt32ToInt32FieldCount());
// already present - should be unchanged
builder.putInt32ToInt32Field(4, 44);
assertEquals(4, builder.getInt32ToInt32FieldCount());
}
private void assertMapCounts(int expectedCount, TestMapOrBuilder testMapOrBuilder) {
assertEquals(expectedCount, testMapOrBuilder.getInt32ToInt32FieldCount());
assertEquals(expectedCount, testMapOrBuilder.getInt32ToStringFieldCount());
assertEquals(expectedCount, testMapOrBuilder.getInt32ToBytesFieldCount());
assertEquals(expectedCount, testMapOrBuilder.getInt32ToEnumFieldCount());
assertEquals(expectedCount, testMapOrBuilder.getInt32ToMessageFieldCount());
assertEquals(expectedCount, testMapOrBuilder.getStringToInt32FieldCount());
}
public void testGetOrDefault() {
TestMap.Builder builder = TestMap.newBuilder();
assertMapCounts(0, builder);
setMapValuesUsingAccessors(builder);
doTestGetOrDefault(builder);
doTestGetOrDefault(builder.build());
}
public void doTestGetOrDefault(TestMapOrBuilder testMapOrBuilder) {
assertEquals(11, testMapOrBuilder.getInt32ToInt32FieldOrDefault(1, -11));
assertEquals(-11, testMapOrBuilder.getInt32ToInt32FieldOrDefault(-1, -11));
assertEquals("11", testMapOrBuilder.getInt32ToStringFieldOrDefault(1, "-11"));
assertNull("-11", testMapOrBuilder.getInt32ToStringFieldOrDefault(-1, null));
assertEquals(TestUtil.toBytes("11"), testMapOrBuilder.getInt32ToBytesFieldOrDefault(1, null));
assertNull(testMapOrBuilder.getInt32ToBytesFieldOrDefault(-1, null));
assertEquals(TestMap.EnumValue.FOO, testMapOrBuilder.getInt32ToEnumFieldOrDefault(1, null));
assertNull(testMapOrBuilder.getInt32ToEnumFieldOrDefault(-1, null));
assertEquals(MessageValue.newBuilder().setValue(11).build(),
testMapOrBuilder.getInt32ToMessageFieldOrDefault(1, null));
assertNull(testMapOrBuilder.getInt32ToMessageFieldOrDefault(-1, null));
assertEquals(11, testMapOrBuilder.getStringToInt32FieldOrDefault("1", -11));
assertEquals(-11, testMapOrBuilder.getStringToInt32FieldOrDefault("-1", -11));
try {
testMapOrBuilder.getStringToInt32FieldOrDefault(null, -11);
fail();
} catch (NullPointerException e) {
// expected
}
}
public void testGetOrThrow() {
TestMap.Builder builder = TestMap.newBuilder();
assertMapCounts(0, builder);
setMapValuesUsingAccessors(builder);
doTestGetOrDefault(builder);
doTestGetOrDefault(builder.build());
}
public void doTestGetOrThrow(TestMapOrBuilder testMapOrBuilder) {
assertEquals(11, testMapOrBuilder.getInt32ToInt32FieldOrThrow(1));
try {
testMapOrBuilder.getInt32ToInt32FieldOrThrow(-1);
fail();
} catch (IllegalArgumentException e) {
// expected
}
assertEquals("11", testMapOrBuilder.getInt32ToStringFieldOrThrow(1));
try {
testMapOrBuilder.getInt32ToStringFieldOrThrow(-1);
fail();
} catch (IllegalArgumentException e) {
// expected
}
assertEquals(TestUtil.toBytes("11"), testMapOrBuilder.getInt32ToBytesFieldOrThrow(1));
try {
testMapOrBuilder.getInt32ToBytesFieldOrThrow(-1);
fail();
} catch (IllegalArgumentException e) {
// expected
}
assertEquals(TestMap.EnumValue.FOO, testMapOrBuilder.getInt32ToEnumFieldOrThrow(1));
try {
testMapOrBuilder.getInt32ToEnumFieldOrThrow(-1);
fail();
} catch (IllegalArgumentException e) {
// expected
}
assertEquals(MessageValue.newBuilder().setValue(11).build(),
testMapOrBuilder.getInt32ToMessageFieldOrThrow(1));
try {
testMapOrBuilder.getInt32ToMessageFieldOrThrow(-1);
fail();
} catch (IllegalArgumentException e) {
// expected
}
assertEquals(11, testMapOrBuilder.getStringToInt32FieldOrThrow("1"));
try {
testMapOrBuilder.getStringToInt32FieldOrThrow("-1");
fail();
} catch (IllegalArgumentException e) {
// expected
}
try {
testMapOrBuilder.getStringToInt32FieldOrThrow(null);
fail();
} catch (NullPointerException e) {
// expected
}
}
public void testPut() {
TestMap.Builder builder = TestMap.newBuilder();
builder.putInt32ToInt32Field(1, 11);
assertEquals(11, builder.getInt32ToInt32FieldOrThrow(1));
builder.putInt32ToStringField(1, "a");
assertEquals("a", builder.getInt32ToStringFieldOrThrow(1));
try {
builder.putInt32ToStringField(1, null);
fail();
} catch (NullPointerException e) {
// expected
}
builder.putInt32ToBytesField(1, TestUtil.toBytes("11"));
assertEquals(TestUtil.toBytes("11"), builder.getInt32ToBytesFieldOrThrow(1));
try {
builder.putInt32ToBytesField(1, null);
fail();
} catch (NullPointerException e) {
// expected
}
builder.putInt32ToEnumField(1, TestMap.EnumValue.FOO);
assertEquals(TestMap.EnumValue.FOO, builder.getInt32ToEnumFieldOrThrow(1));
try {
builder.putInt32ToEnumField(1, null);
fail();
} catch (NullPointerException e) {
// expected
}
builder.putStringToInt32Field("a", 1);
assertEquals(1, builder.getStringToInt32FieldOrThrow("a"));
try {
builder.putStringToInt32Field(null, -1);
} catch (NullPointerException e) {
// expected
}
}
public void testRemove() {
TestMap.Builder builder = TestMap.newBuilder();
setMapValuesUsingMutableMap(builder);
assertEquals(11, builder.getInt32ToInt32FieldOrThrow(1));
for (int times = 0; times < 2; times++) {
builder.removeInt32ToInt32Field(1);
assertEquals(-1, builder.getInt32ToInt32FieldOrDefault(1, -1));
}
assertEquals("11", builder.getInt32ToStringFieldOrThrow(1));
for (int times = 0; times < 2; times++) {
builder.removeInt32ToStringField(1);
assertNull(builder.getInt32ToStringFieldOrDefault(1, null));
}
assertEquals(TestUtil.toBytes("11"), builder.getInt32ToBytesFieldOrThrow(1));
for (int times = 0; times < 2; times++) {
builder.removeInt32ToBytesField(1);
assertNull(builder.getInt32ToBytesFieldOrDefault(1, null));
}
assertEquals(TestMap.EnumValue.FOO, builder.getInt32ToEnumFieldOrThrow(1));
for (int times = 0; times < 2; times++) {
builder.removeInt32ToEnumField(1);
assertNull(builder.getInt32ToEnumFieldOrDefault(1, null));
}
assertEquals(11, builder.getStringToInt32FieldOrThrow("1"));
for (int times = 0; times < 2; times++) {
builder.removeStringToInt32Field("1");
assertEquals(-1, builder.getStringToInt32FieldOrDefault("1", -1));
}
try {
builder.removeStringToInt32Field(null);
fail();
} catch (NullPointerException e) {
// expected
}
}
// Regression test for b/20494788 // Regression test for b/20494788
public void testMapInitializationOrder() throws Exception { public void testMapInitializationOrder() throws Exception {
assertEquals("RedactAllTypes", map_test.RedactAllTypes assertEquals("RedactAllTypes", map_test.RedactAllTypes
@ -679,5 +1155,23 @@ public class MapForProto2Test extends TestCase {
map.put(key2, value2); map.put(key2, value2);
return map; return map;
} }
}
public void testGetMap() {
TestMap.Builder builder = TestMap.newBuilder();
setMapValuesUsingAccessors(builder);
assertMapValuesSet(builder);
TestMap message = builder.build();
assertEquals(
message.getStringToInt32Field(),
message.getStringToInt32FieldMap());
assertEquals(
message.getInt32ToBytesField(),
message.getInt32ToBytesFieldMap());
assertEquals(
message.getInt32ToEnumField(),
message.getInt32ToEnumFieldMap());
assertEquals(
message.getInt32ToMessageField(),
message.getInt32ToMessageFieldMap());
}
}

@ -30,15 +30,20 @@
package com.google.protobuf; package com.google.protobuf;
import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.EnumDescriptor; import com.google.protobuf.Descriptors.EnumDescriptor;
import com.google.protobuf.Descriptors.EnumValueDescriptor; import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.Descriptors.FieldDescriptor;
import map_test.MapTestProto.BizarroTestMap;
import map_test.MapTestProto.TestMap; import map_test.MapTestProto.TestMap;
import map_test.MapTestProto.TestMap.MessageValue; import map_test.MapTestProto.TestMap.MessageValue;
import map_test.MapTestProto.TestMapOrBuilder;
import map_test.MapTestProto.TestOnChangeEventPropagation; import map_test.MapTestProto.TestOnChangeEventPropagation;
import junit.framework.TestCase; import junit.framework.TestCase;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@ -49,7 +54,8 @@ import java.util.Map;
* Unit tests for map fields. * Unit tests for map fields.
*/ */
public class MapTest extends TestCase { public class MapTest extends TestCase {
private void setMapValues(TestMap.Builder builder) {
private void setMapValuesUsingMutableMap(TestMap.Builder builder) {
builder.getMutableInt32ToInt32Field().put(1, 11); builder.getMutableInt32ToInt32Field().put(1, 11);
builder.getMutableInt32ToInt32Field().put(2, 22); builder.getMutableInt32ToInt32Field().put(2, 22);
builder.getMutableInt32ToInt32Field().put(3, 33); builder.getMutableInt32ToInt32Field().put(3, 33);
@ -78,6 +84,46 @@ public class MapTest extends TestCase {
builder.getMutableStringToInt32Field().put("3", 33); builder.getMutableStringToInt32Field().put("3", 33);
} }
private void setMapValuesUsingAccessors(TestMap.Builder builder) {
builder
.putInt32ToInt32Field(1, 11)
.putInt32ToInt32Field(2, 22)
.putInt32ToInt32Field(3, 33)
.putInt32ToStringField(1, "11")
.putInt32ToStringField(2, "22")
.putInt32ToStringField(3, "33")
.putInt32ToBytesField(1, TestUtil.toBytes("11"))
.putInt32ToBytesField(2, TestUtil.toBytes("22"))
.putInt32ToBytesField(3, TestUtil.toBytes("33"))
.putInt32ToEnumField(1, TestMap.EnumValue.FOO)
.putInt32ToEnumField(2, TestMap.EnumValue.BAR)
.putInt32ToEnumField(3, TestMap.EnumValue.BAZ)
.putInt32ToMessageField(1, MessageValue.newBuilder().setValue(11).build())
.putInt32ToMessageField(2, MessageValue.newBuilder().setValue(22).build())
.putInt32ToMessageField(3, MessageValue.newBuilder().setValue(33).build())
.putStringToInt32Field("1", 11)
.putStringToInt32Field("2", 22)
.putStringToInt32Field("3", 33);
}
public void testSetMapValues() {
TestMap.Builder usingMutableMapBuilder = TestMap.newBuilder();
setMapValuesUsingMutableMap(usingMutableMapBuilder);
TestMap usingMutableMap = usingMutableMapBuilder.build();
assertMapValuesSet(usingMutableMap);
TestMap.Builder usingAccessorsBuilder = TestMap.newBuilder();
setMapValuesUsingAccessors(usingAccessorsBuilder);
TestMap usingAccessors = usingAccessorsBuilder.build();
assertMapValuesSet(usingAccessors);
assertEquals(usingAccessors, usingMutableMap);
}
private void copyMapValues(TestMap source, TestMap.Builder destination) { private void copyMapValues(TestMap source, TestMap.Builder destination) {
destination destination
.putAllInt32ToInt32Field(source.getInt32ToInt32Field()) .putAllInt32ToInt32Field(source.getInt32ToInt32Field())
@ -120,7 +166,7 @@ public class MapTest extends TestCase {
assertEquals(33, message.getStringToInt32Field().get("3").intValue()); assertEquals(33, message.getStringToInt32Field().get("3").intValue());
} }
private void updateMapValues(TestMap.Builder builder) { private void updateMapValuesUsingMutableMap(TestMap.Builder builder) {
builder.getMutableInt32ToInt32Field().put(1, 111); builder.getMutableInt32ToInt32Field().put(1, 111);
builder.getMutableInt32ToInt32Field().remove(2); builder.getMutableInt32ToInt32Field().remove(2);
builder.getMutableInt32ToInt32Field().put(4, 44); builder.getMutableInt32ToInt32Field().put(4, 44);
@ -148,6 +194,58 @@ public class MapTest extends TestCase {
builder.getMutableStringToInt32Field().put("4", 44); builder.getMutableStringToInt32Field().put("4", 44);
} }
private void updateMapValuesUsingAccessors(TestMap.Builder builder) {
builder
.putInt32ToInt32Field(1, 111)
.removeInt32ToInt32Field(2)
.putInt32ToInt32Field(4, 44)
.putInt32ToStringField(1, "111")
.removeInt32ToStringField(2)
.putInt32ToStringField(4, "44")
.putInt32ToBytesField(1, TestUtil.toBytes("111"))
.removeInt32ToBytesField(2)
.putInt32ToBytesField(4, TestUtil.toBytes("44"))
.putInt32ToEnumField(1, TestMap.EnumValue.BAR)
.removeInt32ToEnumField(2)
.putInt32ToEnumField(4, TestMap.EnumValue.QUX)
.putInt32ToMessageField(1, MessageValue.newBuilder().setValue(111).build())
.removeInt32ToMessageField(2)
.putInt32ToMessageField(4, MessageValue.newBuilder().setValue(44).build())
.putStringToInt32Field("1", 111)
.removeStringToInt32Field("2")
.putStringToInt32Field("4", 44);
}
public void testUpdateMapValues() {
TestMap.Builder usingMutableMapBuilder = TestMap.newBuilder();
setMapValuesUsingMutableMap(usingMutableMapBuilder);
TestMap usingMutableMap = usingMutableMapBuilder.build();
assertMapValuesSet(usingMutableMap);
TestMap.Builder usingAccessorsBuilder = TestMap.newBuilder();
setMapValuesUsingAccessors(usingAccessorsBuilder);
TestMap usingAccessors = usingAccessorsBuilder.build();
assertMapValuesSet(usingAccessors);
assertEquals(usingAccessors, usingMutableMap);
usingMutableMapBuilder = usingMutableMap.toBuilder();
updateMapValuesUsingMutableMap(usingMutableMapBuilder);
usingMutableMap = usingMutableMapBuilder.build();
assertMapValuesUpdated(usingMutableMap);
usingAccessorsBuilder = usingAccessors.toBuilder();
updateMapValuesUsingAccessors(usingAccessorsBuilder);
usingAccessors = usingAccessorsBuilder.build();
assertMapValuesUpdated(usingAccessors);
assertEquals(usingAccessors, usingMutableMap);
}
private void assertMapValuesUpdated(TestMap message) { private void assertMapValuesUpdated(TestMap message) {
assertEquals(3, message.getInt32ToInt32Field().size()); assertEquals(3, message.getInt32ToInt32Field().size());
assertEquals(111, message.getInt32ToInt32Field().get(1).intValue()); assertEquals(111, message.getInt32ToInt32Field().get(1).intValue());
@ -180,13 +278,48 @@ public class MapTest extends TestCase {
assertEquals(44, message.getStringToInt32Field().get("4").intValue()); assertEquals(44, message.getStringToInt32Field().get("4").intValue());
} }
private void assertMapValuesCleared(TestMap message) { private void assertMapValuesCleared(TestMapOrBuilder testMapOrBuilder) {
assertEquals(0, message.getInt32ToInt32Field().size()); assertEquals(0, testMapOrBuilder.getInt32ToInt32Field().size());
assertEquals(0, message.getInt32ToStringField().size()); assertEquals(0, testMapOrBuilder.getInt32ToInt32FieldCount());
assertEquals(0, message.getInt32ToBytesField().size()); assertEquals(0, testMapOrBuilder.getInt32ToStringField().size());
assertEquals(0, message.getInt32ToEnumField().size()); assertEquals(0, testMapOrBuilder.getInt32ToStringFieldCount());
assertEquals(0, message.getInt32ToMessageField().size()); assertEquals(0, testMapOrBuilder.getInt32ToBytesField().size());
assertEquals(0, message.getStringToInt32Field().size()); assertEquals(0, testMapOrBuilder.getInt32ToBytesFieldCount());
assertEquals(0, testMapOrBuilder.getInt32ToEnumField().size());
assertEquals(0, testMapOrBuilder.getInt32ToEnumFieldCount());
assertEquals(0, testMapOrBuilder.getInt32ToMessageField().size());
assertEquals(0, testMapOrBuilder.getInt32ToMessageFieldCount());
assertEquals(0, testMapOrBuilder.getStringToInt32Field().size());
assertEquals(0, testMapOrBuilder.getStringToInt32FieldCount());
}
public void testGetMapIsImmutable() {
TestMap.Builder builder = TestMap.newBuilder();
assertMapsAreImmutable(builder);
assertMapsAreImmutable(builder.build());
setMapValuesUsingAccessors(builder);
assertMapsAreImmutable(builder);
assertMapsAreImmutable(builder.build());
}
private void assertMapsAreImmutable(TestMapOrBuilder testMapOrBuilder) {
assertImmutable(testMapOrBuilder.getInt32ToInt32Field(), 1, 2);
assertImmutable(testMapOrBuilder.getInt32ToStringField(), 1, "2");
assertImmutable(testMapOrBuilder.getInt32ToBytesField(), 1, TestUtil.toBytes("2"));
assertImmutable(testMapOrBuilder.getInt32ToEnumField(), 1, TestMap.EnumValue.FOO);
assertImmutable(
testMapOrBuilder.getInt32ToMessageField(), 1, MessageValue.getDefaultInstance());
assertImmutable(testMapOrBuilder.getStringToInt32Field(), "1", 2);
}
private <K, V> void assertImmutable(Map<K, V> map, K key, V value) {
try {
map.put(key, value);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
} }
public void testMutableMapLifecycle() { public void testMutableMapLifecycle() {
@ -305,25 +438,27 @@ public class MapTest extends TestCase {
assertMapValuesCleared(message); assertMapValuesCleared(message);
builder = message.toBuilder(); builder = message.toBuilder();
setMapValues(builder); setMapValuesUsingMutableMap(builder);
message = builder.build(); message = builder.build();
assertMapValuesSet(message); assertMapValuesSet(message);
builder = message.toBuilder(); builder = message.toBuilder();
updateMapValues(builder); updateMapValuesUsingMutableMap(builder);
message = builder.build(); message = builder.build();
assertMapValuesUpdated(message); assertMapValuesUpdated(message);
builder = message.toBuilder(); builder = message.toBuilder();
builder.clear(); builder.clear();
assertMapValuesCleared(builder);
message = builder.build(); message = builder.build();
assertMapValuesCleared(message); assertMapValuesCleared(message);
} }
public void testPutAll() throws Exception { public void testPutAll() throws Exception {
TestMap.Builder sourceBuilder = TestMap.newBuilder(); TestMap.Builder sourceBuilder = TestMap.newBuilder();
setMapValues(sourceBuilder); setMapValuesUsingMutableMap(sourceBuilder);
TestMap source = sourceBuilder.build(); TestMap source = sourceBuilder.build();
assertMapValuesSet(source);
TestMap.Builder destination = TestMap.newBuilder(); TestMap.Builder destination = TestMap.newBuilder();
copyMapValues(source, destination); copyMapValues(source, destination);
@ -344,18 +479,76 @@ public class MapTest extends TestCase {
assertEquals(0, destination.getInt32ToEnumFieldValue().get(0).intValue()); assertEquals(0, destination.getInt32ToEnumFieldValue().get(0).intValue());
assertEquals(1, destination.getInt32ToEnumFieldValue().get(1).intValue()); assertEquals(1, destination.getInt32ToEnumFieldValue().get(1).intValue());
assertEquals(1000, destination.getInt32ToEnumFieldValue().get(2).intValue()); assertEquals(1000, destination.getInt32ToEnumFieldValue().get(2).intValue());
assertEquals(3, destination.getInt32ToEnumFieldCount());
}
public void testPutForUnknownEnumValues() throws Exception {
TestMap.Builder builder = TestMap.newBuilder()
.putInt32ToEnumFieldValue(0, 0)
.putInt32ToEnumFieldValue(1, 1);
try {
builder.putInt32ToEnumFieldValue(2, 1000); // unknown value.
fail();
} catch (IllegalArgumentException e) {
// expected
}
TestMap message = builder.build();
assertEquals(0, message.getInt32ToEnumFieldValueOrThrow(0));
assertEquals(1, message.getInt32ToEnumFieldValueOrThrow(1));
assertEquals(2, message.getInt32ToEnumFieldCount());
}
public void testPutChecksNullKeysAndValues() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
try {
builder.putInt32ToStringField(1, null);
fail();
} catch (NullPointerException e) {
// expected.
}
try {
builder.putInt32ToBytesField(1, null);
fail();
} catch (NullPointerException e) {
// expected.
}
try {
builder.putInt32ToEnumField(1, null);
fail();
} catch (NullPointerException e) {
// expected.
}
try {
builder.putInt32ToMessageField(1, null);
fail();
} catch (NullPointerException e) {
// expected.
}
try {
builder.putStringToInt32Field(null, 1);
fail();
} catch (NullPointerException e) {
// expected.
}
} }
public void testSerializeAndParse() throws Exception { public void testSerializeAndParse() throws Exception {
TestMap.Builder builder = TestMap.newBuilder(); TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder); setMapValuesUsingMutableMap(builder);
TestMap message = builder.build(); TestMap message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size()); assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.parser().parseFrom(message.toByteString()); message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesSet(message); assertMapValuesSet(message);
builder = message.toBuilder(); builder = message.toBuilder();
updateMapValues(builder); updateMapValuesUsingMutableMap(builder);
message = builder.build(); message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size()); assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.parser().parseFrom(message.toByteString()); message = TestMap.parser().parseFrom(message.toByteString());
@ -369,9 +562,58 @@ public class MapTest extends TestCase {
assertMapValuesCleared(message); assertMapValuesCleared(message);
} }
private TestMap tryParseTestMap(BizarroTestMap bizarroMap) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
CodedOutputStream output = CodedOutputStream.newInstance(byteArrayOutputStream);
bizarroMap.writeTo(output);
output.flush();
return TestMap.parser().parseFrom(ByteString.copyFrom(byteArrayOutputStream.toByteArray()));
}
public void testParseError() throws Exception {
ByteString bytes = TestUtil.toBytes("SOME BYTES");
String stringKey = "a string key";
TestMap map = tryParseTestMap(BizarroTestMap.newBuilder()
.putInt32ToInt32Field(5, bytes)
.build());
assertEquals(map.getInt32ToInt32FieldOrDefault(5, -1), 0);
map = tryParseTestMap(BizarroTestMap.newBuilder()
.putInt32ToStringField(stringKey, 5)
.build());
assertEquals(map.getInt32ToStringFieldOrDefault(0, null), "");
map = tryParseTestMap(BizarroTestMap.newBuilder()
.putInt32ToBytesField(stringKey, 5)
.build());
assertEquals(map.getInt32ToBytesFieldOrDefault(0, null), ByteString.EMPTY);
map = tryParseTestMap(BizarroTestMap.newBuilder()
.putInt32ToEnumField(stringKey, bytes)
.build());
assertEquals(map.getInt32ToEnumFieldOrDefault(0, null), TestMap.EnumValue.FOO);
try {
tryParseTestMap(BizarroTestMap.newBuilder()
.putInt32ToMessageField(stringKey, bytes)
.build());
fail();
} catch (InvalidProtocolBufferException expected) {
assertTrue(expected.getUnfinishedMessage() instanceof TestMap);
map = (TestMap) expected.getUnfinishedMessage();
assertTrue(map.getInt32ToMessageField().isEmpty());
}
map = tryParseTestMap(BizarroTestMap.newBuilder()
.putStringToInt32Field(stringKey, bytes)
.build());
assertEquals(map.getStringToInt32FieldOrDefault(stringKey, -1), 0);
}
public void testMergeFrom() throws Exception { public void testMergeFrom() throws Exception {
TestMap.Builder builder = TestMap.newBuilder(); TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder); setMapValuesUsingMutableMap(builder);
TestMap message = builder.build(); TestMap message = builder.build();
TestMap.Builder other = TestMap.newBuilder(); TestMap.Builder other = TestMap.newBuilder();
@ -629,7 +871,7 @@ public class MapTest extends TestCase {
public void testTextFormat() throws Exception { public void testTextFormat() throws Exception {
TestMap.Builder builder = TestMap.newBuilder(); TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder); setMapValuesUsingMutableMap(builder);
TestMap message = builder.build(); TestMap message = builder.build();
String textData = TextFormat.printToString(message); String textData = TextFormat.printToString(message);
@ -643,7 +885,7 @@ public class MapTest extends TestCase {
public void testDynamicMessage() throws Exception { public void testDynamicMessage() throws Exception {
TestMap.Builder builder = TestMap.newBuilder(); TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder); setMapValuesUsingMutableMap(builder);
TestMap message = builder.build(); TestMap message = builder.build();
Message dynamicDefaultInstance = Message dynamicDefaultInstance =
@ -760,13 +1002,311 @@ public class MapTest extends TestCase {
public void testIterationOrder() throws Exception { public void testIterationOrder() throws Exception {
TestMap.Builder builder = TestMap.newBuilder(); TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder); setMapValuesUsingMutableMap(builder);
TestMap message = builder.build(); TestMap message = builder.build();
assertEquals(Arrays.asList("1", "2", "3"), assertEquals(Arrays.asList("1", "2", "3"),
new ArrayList<String>(message.getStringToInt32Field().keySet())); new ArrayList<String>(message.getStringToInt32Field().keySet()));
} }
public void testGetMap() {
TestMap.Builder builder = TestMap.newBuilder();
setMapValuesUsingMutableMap(builder);
TestMap message = builder.build();
assertEquals(
message.getStringToInt32Field(),
message.getStringToInt32FieldMap());
assertEquals(
message.getInt32ToBytesField(),
message.getInt32ToBytesFieldMap());
assertEquals(
message.getInt32ToEnumField(),
message.getInt32ToEnumFieldMap());
assertEquals(
message.getInt32ToEnumFieldValue(),
message.getInt32ToEnumFieldValueMap());
assertEquals(
message.getInt32ToMessageField(),
message.getInt32ToMessageFieldMap());
}
public void testContains() {
TestMap.Builder builder = TestMap.newBuilder();
setMapValuesUsingMutableMap(builder);
assertMapContainsSetValues(builder);
assertMapContainsSetValues(builder.build());
}
private void assertMapContainsSetValues(TestMapOrBuilder testMapOrBuilder) {
assertTrue(testMapOrBuilder.containsInt32ToInt32Field(1));
assertTrue(testMapOrBuilder.containsInt32ToInt32Field(2));
assertTrue(testMapOrBuilder.containsInt32ToInt32Field(3));
assertFalse(testMapOrBuilder.containsInt32ToInt32Field(-1));
assertTrue(testMapOrBuilder.containsInt32ToStringField(1));
assertTrue(testMapOrBuilder.containsInt32ToStringField(2));
assertTrue(testMapOrBuilder.containsInt32ToStringField(3));
assertFalse(testMapOrBuilder.containsInt32ToStringField(-1));
assertTrue(testMapOrBuilder.containsInt32ToBytesField(1));
assertTrue(testMapOrBuilder.containsInt32ToBytesField(2));
assertTrue(testMapOrBuilder.containsInt32ToBytesField(3));
assertFalse(testMapOrBuilder.containsInt32ToBytesField(-1));
assertTrue(testMapOrBuilder.containsInt32ToEnumField(1));
assertTrue(testMapOrBuilder.containsInt32ToEnumField(2));
assertTrue(testMapOrBuilder.containsInt32ToEnumField(3));
assertFalse(testMapOrBuilder.containsInt32ToEnumField(-1));
assertTrue(testMapOrBuilder.containsInt32ToMessageField(1));
assertTrue(testMapOrBuilder.containsInt32ToMessageField(2));
assertTrue(testMapOrBuilder.containsInt32ToMessageField(3));
assertFalse(testMapOrBuilder.containsInt32ToMessageField(-1));
assertTrue(testMapOrBuilder.containsStringToInt32Field("1"));
assertTrue(testMapOrBuilder.containsStringToInt32Field("2"));
assertTrue(testMapOrBuilder.containsStringToInt32Field("3"));
assertFalse(testMapOrBuilder.containsStringToInt32Field("-1"));
}
public void testCount() {
TestMap.Builder builder = TestMap.newBuilder();
assertMapCounts(0, builder);
setMapValuesUsingMutableMap(builder);
assertMapCounts(3, builder);
TestMap message = builder.build();
assertMapCounts(3, message);
builder = message.toBuilder().putInt32ToInt32Field(4, 44);
assertEquals(4, builder.getInt32ToInt32FieldCount());
assertEquals(4, builder.build().getInt32ToInt32FieldCount());
// already present - should be unchanged
builder.putInt32ToInt32Field(4, 44);
assertEquals(4, builder.getInt32ToInt32FieldCount());
}
private void assertMapCounts(int expectedCount, TestMapOrBuilder testMapOrBuilder) {
assertEquals(expectedCount, testMapOrBuilder.getInt32ToInt32FieldCount());
assertEquals(expectedCount, testMapOrBuilder.getInt32ToStringFieldCount());
assertEquals(expectedCount, testMapOrBuilder.getInt32ToBytesFieldCount());
assertEquals(expectedCount, testMapOrBuilder.getInt32ToEnumFieldCount());
assertEquals(expectedCount, testMapOrBuilder.getInt32ToMessageFieldCount());
assertEquals(expectedCount, testMapOrBuilder.getStringToInt32FieldCount());
}
public void testGetOrDefault() {
TestMap.Builder builder = TestMap.newBuilder();
assertMapCounts(0, builder);
setMapValuesUsingAccessors(builder);
doTestGetOrDefault(builder);
doTestGetOrDefault(builder.build());
}
public void doTestGetOrDefault(TestMapOrBuilder testMapOrBuilder) {
assertEquals(11, testMapOrBuilder.getInt32ToInt32FieldOrDefault(1, -11));
assertEquals(-11, testMapOrBuilder.getInt32ToInt32FieldOrDefault(-1, -11));
assertEquals("11", testMapOrBuilder.getInt32ToStringFieldOrDefault(1, "-11"));
assertNull("-11", testMapOrBuilder.getInt32ToStringFieldOrDefault(-1, null));
assertEquals(TestUtil.toBytes("11"), testMapOrBuilder.getInt32ToBytesFieldOrDefault(1, null));
assertNull(testMapOrBuilder.getInt32ToBytesFieldOrDefault(-1, null));
assertEquals(TestMap.EnumValue.FOO, testMapOrBuilder.getInt32ToEnumFieldOrDefault(1, null));
assertNull(testMapOrBuilder.getInt32ToEnumFieldOrDefault(-1, null));
assertEquals(
TestMap.EnumValue.BAR.getNumber(),
(int) testMapOrBuilder.getInt32ToEnumFieldValueOrDefault(2, -1));
assertEquals(-1, testMapOrBuilder.getInt32ToEnumFieldValueOrDefault(-1000, -1));
assertEquals(MessageValue.newBuilder().setValue(11).build(),
testMapOrBuilder.getInt32ToMessageFieldOrDefault(1, null));
assertNull(testMapOrBuilder.getInt32ToMessageFieldOrDefault(-1, null));
assertEquals(11, testMapOrBuilder.getStringToInt32FieldOrDefault("1", -11));
assertEquals(-11, testMapOrBuilder.getStringToInt32FieldOrDefault("-1", -11));
try {
testMapOrBuilder.getStringToInt32FieldOrDefault(null, -11);
fail();
} catch (NullPointerException e) {
// expected
}
}
public void testGetOrThrow() {
TestMap.Builder builder = TestMap.newBuilder();
assertMapCounts(0, builder);
setMapValuesUsingAccessors(builder);
doTestGetOrDefault(builder);
doTestGetOrDefault(builder.build());
}
public void doTestGetOrThrow(TestMapOrBuilder testMapOrBuilder) {
assertEquals(11, testMapOrBuilder.getInt32ToInt32FieldOrThrow(1));
try {
testMapOrBuilder.getInt32ToInt32FieldOrThrow(-1);
fail();
} catch (IllegalArgumentException e) {
// expected
}
assertEquals("11", testMapOrBuilder.getInt32ToStringFieldOrThrow(1));
try {
testMapOrBuilder.getInt32ToStringFieldOrThrow(-1);
fail();
} catch (IllegalArgumentException e) {
// expected
}
assertEquals(TestUtil.toBytes("11"), testMapOrBuilder.getInt32ToBytesFieldOrThrow(1));
try {
testMapOrBuilder.getInt32ToBytesFieldOrThrow(-1);
fail();
} catch (IllegalArgumentException e) {
// expected
}
assertEquals(TestMap.EnumValue.FOO, testMapOrBuilder.getInt32ToEnumFieldOrThrow(1));
try {
testMapOrBuilder.getInt32ToEnumFieldOrThrow(-1);
fail();
} catch (IllegalArgumentException e) {
// expected
}
assertEquals(
TestMap.EnumValue.BAR.getNumber(), testMapOrBuilder.getInt32ToEnumFieldValueOrThrow(2));
try {
testMapOrBuilder.getInt32ToEnumFieldValueOrThrow(-1);
fail();
} catch (IllegalArgumentException e) {
// expected
}
assertEquals(MessageValue.newBuilder().setValue(11).build(),
testMapOrBuilder.getInt32ToMessageFieldOrThrow(1));
try {
testMapOrBuilder.getInt32ToMessageFieldOrThrow(-1);
fail();
} catch (IllegalArgumentException e) {
// expected
}
assertEquals(11, testMapOrBuilder.getStringToInt32FieldOrThrow("1"));
try {
testMapOrBuilder.getStringToInt32FieldOrThrow("-1");
fail();
} catch (IllegalArgumentException e) {
// expected
}
try {
testMapOrBuilder.getStringToInt32FieldOrThrow(null);
fail();
} catch (NullPointerException e) {
// expected
}
}
public void testPut() {
TestMap.Builder builder = TestMap.newBuilder();
builder.putInt32ToInt32Field(1, 11);
assertEquals(11, builder.getInt32ToInt32FieldOrThrow(1));
builder.putInt32ToStringField(1, "a");
assertEquals("a", builder.getInt32ToStringFieldOrThrow(1));
try {
builder.putInt32ToStringField(1, null);
fail();
} catch (NullPointerException e) {
// expected
}
builder.putInt32ToBytesField(1, TestUtil.toBytes("11"));
assertEquals(TestUtil.toBytes("11"), builder.getInt32ToBytesFieldOrThrow(1));
try {
builder.putInt32ToBytesField(1, null);
fail();
} catch (NullPointerException e) {
// expected
}
builder.putInt32ToEnumField(1, TestMap.EnumValue.FOO);
assertEquals(TestMap.EnumValue.FOO, builder.getInt32ToEnumFieldOrThrow(1));
try {
builder.putInt32ToEnumField(1, null);
fail();
} catch (NullPointerException e) {
// expected
}
builder.putInt32ToEnumFieldValue(1, TestMap.EnumValue.BAR.getNumber());
assertEquals(
TestMap.EnumValue.BAR.getNumber(), builder.getInt32ToEnumFieldValueOrThrow(1));
try {
builder.putInt32ToEnumFieldValue(1, -1);
fail();
} catch (IllegalArgumentException e) {
// expected
}
builder.putStringToInt32Field("a", 1);
assertEquals(1, builder.getStringToInt32FieldOrThrow("a"));
try {
builder.putStringToInt32Field(null, -1);
} catch (NullPointerException e) {
// expected
}
}
public void testRemove() {
TestMap.Builder builder = TestMap.newBuilder();
setMapValuesUsingMutableMap(builder);
assertEquals(11, builder.getInt32ToInt32FieldOrThrow(1));
for (int times = 0; times < 2; times++) {
builder.removeInt32ToInt32Field(1);
assertEquals(-1, builder.getInt32ToInt32FieldOrDefault(1, -1));
}
assertEquals("11", builder.getInt32ToStringFieldOrThrow(1));
for (int times = 0; times < 2; times++) {
builder.removeInt32ToStringField(1);
assertNull(builder.getInt32ToStringFieldOrDefault(1, null));
}
assertEquals(TestUtil.toBytes("11"), builder.getInt32ToBytesFieldOrThrow(1));
for (int times = 0; times < 2; times++) {
builder.removeInt32ToBytesField(1);
assertNull(builder.getInt32ToBytesFieldOrDefault(1, null));
}
assertEquals(TestMap.EnumValue.FOO, builder.getInt32ToEnumFieldOrThrow(1));
for (int times = 0; times < 2; times++) {
builder.removeInt32ToEnumField(1);
assertNull(builder.getInt32ToEnumFieldOrDefault(1, null));
}
assertEquals(11, builder.getStringToInt32FieldOrThrow("1"));
for (int times = 0; times < 2; times++) {
builder.removeStringToInt32Field("1");
assertEquals(-1, builder.getStringToInt32FieldOrDefault("1", -1));
}
try {
builder.removeStringToInt32Field(null);
fail();
} catch (NullPointerException e) {
// expected
}
}
private static <K, V> Map<K, V> newMap(K key1, V value1) { private static <K, V> Map<K, V> newMap(K key1, V value1) {
Map<K, V> map = new HashMap<K, V>(); Map<K, V> map = new HashMap<K, V>();
map.put(key1, value1); map.put(key1, value1);

@ -0,0 +1,190 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder;
import junit.framework.TestCase;
import java.util.Collections;
import java.util.List;
/**
* Tests for {@link RepeatedFieldBuilderV3}. This tests basic functionality.
* More extensive testing is provided via other tests that exercise the
* builder.
*
* @author jonp@google.com (Jon Perlow)
*/
public class RepeatedFieldBuilderV3Test extends TestCase {
public void testBasicUse() {
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
RepeatedFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder> builder = newRepeatedFieldBuilderV3(mockParent);
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build());
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build());
assertEquals(0, builder.getMessage(0).getOptionalInt32());
assertEquals(1, builder.getMessage(1).getOptionalInt32());
List<TestAllTypes> list = builder.build();
assertEquals(2, list.size());
assertEquals(0, list.get(0).getOptionalInt32());
assertEquals(1, list.get(1).getOptionalInt32());
assertIsUnmodifiable(list);
// Make sure it doesn't change.
List<TestAllTypes> list2 = builder.build();
assertSame(list, list2);
assertEquals(0, mockParent.getInvalidationCount());
}
public void testGoingBackAndForth() {
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
RepeatedFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder> builder = newRepeatedFieldBuilderV3(mockParent);
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build());
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build());
assertEquals(0, builder.getMessage(0).getOptionalInt32());
assertEquals(1, builder.getMessage(1).getOptionalInt32());
// Convert to list
List<TestAllTypes> list = builder.build();
assertEquals(2, list.size());
assertEquals(0, list.get(0).getOptionalInt32());
assertEquals(1, list.get(1).getOptionalInt32());
assertIsUnmodifiable(list);
// Update 0th item
assertEquals(0, mockParent.getInvalidationCount());
builder.getBuilder(0).setOptionalString("foo");
assertEquals(1, mockParent.getInvalidationCount());
list = builder.build();
assertEquals(2, list.size());
assertEquals(0, list.get(0).getOptionalInt32());
assertEquals("foo", list.get(0).getOptionalString());
assertEquals(1, list.get(1).getOptionalInt32());
assertIsUnmodifiable(list);
assertEquals(1, mockParent.getInvalidationCount());
}
public void testVariousMethods() {
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
RepeatedFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder> builder = newRepeatedFieldBuilderV3(mockParent);
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build());
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(2).build());
builder.addBuilder(0, TestAllTypes.getDefaultInstance())
.setOptionalInt32(0);
builder.addBuilder(TestAllTypes.getDefaultInstance()).setOptionalInt32(3);
assertEquals(0, builder.getMessage(0).getOptionalInt32());
assertEquals(1, builder.getMessage(1).getOptionalInt32());
assertEquals(2, builder.getMessage(2).getOptionalInt32());
assertEquals(3, builder.getMessage(3).getOptionalInt32());
assertEquals(0, mockParent.getInvalidationCount());
List<TestAllTypes> messages = builder.build();
assertEquals(4, messages.size());
assertSame(messages, builder.build()); // expect same list
// Remove a message.
builder.remove(2);
assertEquals(1, mockParent.getInvalidationCount());
assertEquals(3, builder.getCount());
assertEquals(0, builder.getMessage(0).getOptionalInt32());
assertEquals(1, builder.getMessage(1).getOptionalInt32());
assertEquals(3, builder.getMessage(2).getOptionalInt32());
// Remove a builder.
builder.remove(0);
assertEquals(1, mockParent.getInvalidationCount());
assertEquals(2, builder.getCount());
assertEquals(1, builder.getMessage(0).getOptionalInt32());
assertEquals(3, builder.getMessage(1).getOptionalInt32());
// Test clear.
builder.clear();
assertEquals(1, mockParent.getInvalidationCount());
assertEquals(0, builder.getCount());
assertTrue(builder.isEmpty());
}
public void testLists() {
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
RepeatedFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder> builder = newRepeatedFieldBuilderV3(mockParent);
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build());
builder.addMessage(0,
TestAllTypes.newBuilder().setOptionalInt32(0).build());
assertEquals(0, builder.getMessage(0).getOptionalInt32());
assertEquals(1, builder.getMessage(1).getOptionalInt32());
// Use list of builders.
List<TestAllTypes.Builder> builders = builder.getBuilderList();
assertEquals(0, builders.get(0).getOptionalInt32());
assertEquals(1, builders.get(1).getOptionalInt32());
builders.get(0).setOptionalInt32(10);
builders.get(1).setOptionalInt32(11);
// Use list of protos
List<TestAllTypes> protos = builder.getMessageList();
assertEquals(10, protos.get(0).getOptionalInt32());
assertEquals(11, protos.get(1).getOptionalInt32());
// Add an item to the builders and verify it's updated in both
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(12).build());
assertEquals(3, builders.size());
assertEquals(3, protos.size());
}
private void assertIsUnmodifiable(List<?> list) {
if (list == Collections.emptyList()) {
// OKAY -- Need to check this b/c EmptyList allows you to call clear.
} else {
try {
list.clear();
fail("List wasn't immutable");
} catch (UnsupportedOperationException e) {
// good
}
}
}
private RepeatedFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder>
newRepeatedFieldBuilderV3(GeneratedMessage.BuilderParent parent) {
return new RepeatedFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder>(Collections.<TestAllTypes>emptyList(), false,
parent, false);
}
}

@ -0,0 +1,155 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder;
import junit.framework.TestCase;
/**
* Tests for {@link SingleFieldBuilderV3}. This tests basic functionality.
* More extensive testing is provided via other tests that exercise the
* builder.
*
* @author jonp@google.com (Jon Perlow)
*/
public class SingleFieldBuilderV3Test extends TestCase {
public void testBasicUseAndInvalidations() {
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder> builder =
new SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder>(
TestAllTypes.getDefaultInstance(),
mockParent,
false);
assertSame(TestAllTypes.getDefaultInstance(), builder.getMessage());
assertEquals(TestAllTypes.getDefaultInstance(),
builder.getBuilder().buildPartial());
assertEquals(0, mockParent.getInvalidationCount());
builder.getBuilder().setOptionalInt32(10);
assertEquals(0, mockParent.getInvalidationCount());
TestAllTypes message = builder.build();
assertEquals(10, message.getOptionalInt32());
// Test that we receive invalidations now that build has been called.
assertEquals(0, mockParent.getInvalidationCount());
builder.getBuilder().setOptionalInt32(20);
assertEquals(1, mockParent.getInvalidationCount());
// Test that we don't keep getting invalidations on every change
builder.getBuilder().setOptionalInt32(30);
assertEquals(1, mockParent.getInvalidationCount());
}
public void testSetMessage() {
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder> builder =
new SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder>(
TestAllTypes.getDefaultInstance(),
mockParent,
false);
builder.setMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build());
assertEquals(0, builder.getMessage().getOptionalInt32());
// Update message using the builder
builder.getBuilder().setOptionalInt32(1);
assertEquals(0, mockParent.getInvalidationCount());
assertEquals(1, builder.getBuilder().getOptionalInt32());
assertEquals(1, builder.getMessage().getOptionalInt32());
builder.build();
builder.getBuilder().setOptionalInt32(2);
assertEquals(2, builder.getBuilder().getOptionalInt32());
assertEquals(2, builder.getMessage().getOptionalInt32());
// Make sure message stays cached
assertSame(builder.getMessage(), builder.getMessage());
}
public void testClear() {
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder> builder =
new SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder>(
TestAllTypes.getDefaultInstance(),
mockParent,
false);
builder.setMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build());
assertNotSame(TestAllTypes.getDefaultInstance(), builder.getMessage());
builder.clear();
assertSame(TestAllTypes.getDefaultInstance(), builder.getMessage());
builder.getBuilder().setOptionalInt32(1);
assertNotSame(TestAllTypes.getDefaultInstance(), builder.getMessage());
builder.clear();
assertSame(TestAllTypes.getDefaultInstance(), builder.getMessage());
}
public void testMerge() {
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder> builder =
new SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder>(
TestAllTypes.getDefaultInstance(),
mockParent,
false);
// Merge into default field.
builder.mergeFrom(TestAllTypes.getDefaultInstance());
assertSame(TestAllTypes.getDefaultInstance(), builder.getMessage());
// Merge into non-default field on existing builder.
builder.getBuilder().setOptionalInt32(2);
builder.mergeFrom(TestAllTypes.newBuilder()
.setOptionalDouble(4.0)
.buildPartial());
assertEquals(2, builder.getMessage().getOptionalInt32());
assertEquals(4.0, builder.getMessage().getOptionalDouble());
// Merge into non-default field on existing message
builder.setMessage(TestAllTypes.newBuilder()
.setOptionalInt32(10)
.buildPartial());
builder.mergeFrom(TestAllTypes.newBuilder()
.setOptionalDouble(5.0)
.buildPartial());
assertEquals(10, builder.getMessage().getOptionalInt32());
assertEquals(5.0, builder.getMessage().getOptionalDouble());
}
}

@ -3764,7 +3764,8 @@ public final class TestUtil {
private static File getTestDataDir() { private static File getTestDataDir() {
// Search each parent directory looking for "src/google/protobuf". // Search each parent directory looking for "src/google/protobuf".
File ancestor = new File("."); File ancestor = new File(System.getProperty("protobuf.dir", "."));
String initialPath = ancestor.getAbsolutePath();
try { try {
ancestor = ancestor.getCanonicalFile(); ancestor = ancestor.getCanonicalFile();
} catch (IOException e) { } catch (IOException e) {
@ -3781,7 +3782,7 @@ public final class TestUtil {
throw new RuntimeException( throw new RuntimeException(
"Could not find golden files. This test must be run from within the " + "Could not find golden files. This test must be run from within the " +
"protobuf source package so that it can read test data files from the " + "protobuf source package so that it can read test data files from the " +
"C++ source tree."); "C++ source tree: " + initialPath);
} }
/** /**

@ -522,15 +522,16 @@ public class TextFormatTest extends TestCase {
"optional_string: \"ueoauaoe\n" + "optional_string: \"ueoauaoe\n" +
"optional_int32: 123"); "optional_int32: 123");
assertParseError( assertParseError(
"1:2: Extension \"nosuchext\" not found in the ExtensionRegistry.", "1:2: Input contains unknown fields and/or extensions:\n" +
"1:2:\tprotobuf_unittest.TestAllTypes.[nosuchext]",
"[nosuchext]: 123"); "[nosuchext]: 123");
assertParseError( assertParseError(
"1:20: Extension \"protobuf_unittest.optional_int32_extension\" does " + "1:20: Extension \"protobuf_unittest.optional_int32_extension\" does " +
"not extend message type \"protobuf_unittest.TestAllTypes\".", "not extend message type \"protobuf_unittest.TestAllTypes\".",
"[protobuf_unittest.optional_int32_extension]: 123"); "[protobuf_unittest.optional_int32_extension]: 123");
assertParseError( assertParseError(
"1:1: Message type \"protobuf_unittest.TestAllTypes\" has no field " + "1:1: Input contains unknown fields and/or extensions:\n" +
"named \"nosuchfield\".", "1:1:\tprotobuf_unittest.TestAllTypes.nosuchfield",
"nosuchfield: 123"); "nosuchfield: 123");
assertParseError( assertParseError(
"1:21: Expected \">\".", "1:21: Expected \">\".",

@ -54,6 +54,7 @@ message TestAllTypes {
NestedEnum optional_nested_enum = 4; NestedEnum optional_nested_enum = 4;
NestedMessage optional_nested_message = 5; NestedMessage optional_nested_message = 5;
protobuf_unittest.TestRequired optional_proto2_message = 6; protobuf_unittest.TestRequired optional_proto2_message = 6;
NestedMessage optional_lazy_message = 7 [lazy=true];
oneof oneof_field { oneof oneof_field {
int32 oneof_int32 = 11; int32 oneof_int32 = 11;
@ -81,6 +82,7 @@ message TestOptionalFieldsOnly {
TestAllTypes.NestedEnum optional_nested_enum = 4; TestAllTypes.NestedEnum optional_nested_enum = 4;
TestAllTypes.NestedMessage optional_nested_message = 5; TestAllTypes.NestedMessage optional_nested_message = 5;
protobuf_unittest.TestRequired optional_proto2_message = 6; protobuf_unittest.TestRequired optional_proto2_message = 6;
TestAllTypes.NestedMessage optional_lazy_message = 7 [lazy=true];
} }
message TestRepeatedFieldsOnly { message TestRepeatedFieldsOnly {

@ -70,6 +70,17 @@ message TestRecursiveMap {
optional int32 value = 1; optional int32 value = 1;
map<int32, TestRecursiveMap> recursive_map_field = 2; map<int32, TestRecursiveMap> recursive_map_field = 2;
} }
// a decoy of TestMap for testing parsing errors
message BizarroTestMap {
map<int32, bytes> int32_to_int32_field = 1; // same key type, different value
map<string, int32> int32_to_string_field = 2; // different key and value types
map<string, int32> int32_to_bytes_field = 3; // different key types, same value
map<string, bytes> int32_to_enum_field = 4; // different key and value types
map<string, bytes> int32_to_message_field = 5; // different key and value types
map<string, bytes> string_to_int32_field = 6; // same key type, different value
}
package map_for_proto2_lite_test; package map_for_proto2_lite_test;
option java_package = "map_lite_test"; option java_package = "map_lite_test";
option optimize_for = LITE_RUNTIME; option optimize_for = LITE_RUNTIME;

@ -72,3 +72,14 @@ message TestRecursiveMap {
optional int32 value = 1; optional int32 value = 1;
map<int32, TestRecursiveMap> recursive_map_field = 2; map<int32, TestRecursiveMap> recursive_map_field = 2;
} }
// a decoy of TestMap for testing parsing errors
message BizarroTestMap {
map<int32, bytes> int32_to_int32_field = 1; // same key type, different value
map<string, int32> int32_to_string_field = 2; // different key and value types
map<string, int32> int32_to_bytes_field = 3; // different key types, same value
map<string, bytes> int32_to_enum_field = 4; // different key and value types
map<string, bytes> int32_to_message_field = 5; // different key and value types
map<string, bytes> string_to_int32_field = 6; // same key type, different value
}

@ -55,9 +55,19 @@ message TestMap {
map<string, int32> string_to_int32_field = 6; map<string, int32> string_to_int32_field = 6;
} }
// Used to test that a nested bulider containing map fields will properly // Used to test that a nested builder containing map fields will properly
// propagate the onChange event and mark its parent dirty when a change // propagate the onChange event and mark its parent dirty when a change
// is made to a map field. // is made to a map field.
message TestOnChangeEventPropagation { message TestOnChangeEventPropagation {
TestMap optional_message = 1; TestMap optional_message = 1;
} }
// a decoy of TestMap for testing parsing errors
message BizarroTestMap {
map<int32, bytes> int32_to_int32_field = 1; // same key type, different value
map<string, int32> int32_to_string_field = 2; // different key and value types
map<string, int32> int32_to_bytes_field = 3; // different key types, same value
map<string, bytes> int32_to_enum_field = 4; // different key and value types
map<string, bytes> int32_to_message_field = 5; // different key and value types
map<string, bytes> string_to_int32_field = 6; // same key type, different value
}

@ -0,0 +1,256 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf.util;
import static com.google.protobuf.util.Timestamps.MICROS_PER_SECOND;
import static com.google.protobuf.util.Timestamps.MILLIS_PER_SECOND;
import static com.google.protobuf.util.Timestamps.NANOS_PER_MICROSECOND;
import static com.google.protobuf.util.Timestamps.NANOS_PER_MILLISECOND;
import static com.google.protobuf.util.Timestamps.NANOS_PER_SECOND;
import com.google.protobuf.Duration;
import java.text.ParseException;
/**
* Utilities to help create/manipulate {@code protobuf/duration.proto}.
*/
public final class Durations {
static final long DURATION_SECONDS_MIN = -315576000000L;
static final long DURATION_SECONDS_MAX = 315576000000L;
// TODO(kak): Do we want to expose Duration constants for MAX/MIN?
private Durations() {}
/**
* Returns true if the given {@link Duration} is valid. The {@code seconds} value must be in the
* range [-315,576,000,000, +315,576,000,000]. The {@code nanos} value must be in the range
* [-999,999,999, +999,999,999].
*
* <p>Note: Durations less than one second are represented with a 0 {@code seconds} field and a
* positive or negative {@code nanos} field. For durations of one second or more, a non-zero value
* for the {@code nanos} field must be of the same sign as the {@code seconds} field.
*/
public static boolean isValid(Duration duration) {
return isValid(duration.getSeconds(), duration.getNanos());
}
/**
* Returns true if the given number of seconds and nanos is a valid {@link Duration}. The
* {@code seconds} value must be in the range [-315,576,000,000, +315,576,000,000]. The
* {@code nanos} value must be in the range [-999,999,999, +999,999,999].
*
* <p>Note: Durations less than one second are represented with a 0 {@code seconds} field and a
* positive or negative {@code nanos} field. For durations of one second or more, a non-zero value
* for the {@code nanos} field must be of the same sign as the {@code seconds} field.
*/
public static boolean isValid(long seconds, long nanos) {
if (seconds < DURATION_SECONDS_MIN || seconds > DURATION_SECONDS_MAX) {
return false;
}
if (nanos < -999999999L || nanos >= NANOS_PER_SECOND) {
return false;
}
if (seconds < 0 || nanos < 0) {
if (seconds > 0 || nanos > 0) {
return false;
}
}
return true;
}
/**
* Throws an {@link IllegalArgumentException} if the given seconds/nanos are not
* a valid {@link Duration}.
*/
private static void checkValid(long seconds, int nanos) {
if (!isValid(seconds, nanos)) {
throw new IllegalArgumentException(String.format(
"Duration is not valid. See proto definition for valid values. "
+ "Seconds (%s) must be in range [-315,576,000,000, +315,576,000,000]."
+ "Nanos (%s) must be in range [-999,999,999, +999,999,999]. "
+ "Nanos must have the same sign as seconds", seconds, nanos));
}
}
/**
* Convert Duration to string format. The string format will contains 3, 6,
* or 9 fractional digits depending on the precision required to represent
* the exact Duration value. For example: "1s", "1.010s", "1.000000100s",
* "-3.100s" The range that can be represented by Duration is from
* -315,576,000,000 to +315,576,000,000 inclusive (in seconds).
*
* @return The string representation of the given duration.
* @throws IllegalArgumentException if the given duration is not in the valid
* range.
*/
public static String toString(Duration duration) {
long seconds = duration.getSeconds();
int nanos = duration.getNanos();
checkValid(seconds, nanos);
StringBuilder result = new StringBuilder();
if (seconds < 0 || nanos < 0) {
result.append("-");
seconds = -seconds;
nanos = -nanos;
}
result.append(seconds);
if (nanos != 0) {
result.append(".");
result.append(Timestamps.formatNanos(nanos));
}
result.append("s");
return result.toString();
}
/**
* Parse from a string to produce a duration.
*
* @return A Duration parsed from the string.
* @throws ParseException if parsing fails.
*/
public static Duration parse(String value) throws ParseException {
// Must ended with "s".
if (value.isEmpty() || value.charAt(value.length() - 1) != 's') {
throw new ParseException("Invalid duration string: " + value, 0);
}
boolean negative = false;
if (value.charAt(0) == '-') {
negative = true;
value = value.substring(1);
}
String secondValue = value.substring(0, value.length() - 1);
String nanoValue = "";
int pointPosition = secondValue.indexOf('.');
if (pointPosition != -1) {
nanoValue = secondValue.substring(pointPosition + 1);
secondValue = secondValue.substring(0, pointPosition);
}
long seconds = Long.parseLong(secondValue);
int nanos = nanoValue.isEmpty() ? 0 : Timestamps.parseNanos(nanoValue);
if (seconds < 0) {
throw new ParseException("Invalid duration string: " + value, 0);
}
if (negative) {
seconds = -seconds;
nanos = -nanos;
}
try {
return normalizedDuration(seconds, nanos);
} catch (IllegalArgumentException e) {
throw new ParseException("Duration value is out of range.", 0);
}
}
/**
* Create a Duration from the number of milliseconds.
*/
public static Duration fromMillis(long milliseconds) {
return normalizedDuration(
milliseconds / MILLIS_PER_SECOND,
(int) (milliseconds % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND));
}
/**
* Convert a Duration to the number of milliseconds.The result will be
* rounded towards 0 to the nearest millisecond. E.g., if the duration
* represents -1 nanosecond, it will be rounded to 0.
*/
public static long toMillis(Duration duration) {
return duration.getSeconds() * MILLIS_PER_SECOND + duration.getNanos() / NANOS_PER_MILLISECOND;
}
/**
* Create a Duration from the number of microseconds.
*/
public static Duration fromMicros(long microseconds) {
return normalizedDuration(
microseconds / MICROS_PER_SECOND,
(int) (microseconds % MICROS_PER_SECOND * NANOS_PER_MICROSECOND));
}
/**
* Convert a Duration to the number of microseconds.The result will be
* rounded towards 0 to the nearest microseconds. E.g., if the duration
* represents -1 nanosecond, it will be rounded to 0.
*/
public static long toMicros(Duration duration) {
return duration.getSeconds() * MICROS_PER_SECOND + duration.getNanos() / NANOS_PER_MICROSECOND;
}
/**
* Create a Duration from the number of nanoseconds.
*/
public static Duration fromNanos(long nanoseconds) {
return normalizedDuration(
nanoseconds / NANOS_PER_SECOND, (int) (nanoseconds % NANOS_PER_SECOND));
}
/**
* Convert a Duration to the number of nanoseconds.
*/
public static long toNanos(Duration duration) {
return duration.getSeconds() * NANOS_PER_SECOND + duration.getNanos();
}
/**
* Add two durations.
*/
public static Duration add(Duration d1, Duration d2) {
return normalizedDuration(d1.getSeconds() + d2.getSeconds(), d1.getNanos() + d2.getNanos());
}
/**
* Subtract a duration from another.
*/
public static Duration subtract(Duration d1, Duration d2) {
return normalizedDuration(d1.getSeconds() - d2.getSeconds(), d1.getNanos() - d2.getNanos());
}
static Duration normalizedDuration(long seconds, int nanos) {
if (nanos <= -NANOS_PER_SECOND || nanos >= NANOS_PER_SECOND) {
seconds += nanos / NANOS_PER_SECOND;
nanos %= NANOS_PER_SECOND;
}
if (seconds > 0 && nanos < 0) {
nanos += NANOS_PER_SECOND;
seconds -= 1;
}
if (seconds < 0 && nanos > 0) {
nanos -= NANOS_PER_SECOND;
seconds += 1;
}
checkValid(seconds, nanos);
return Duration.newBuilder().setSeconds(seconds).setNanos(nanos).build();
}
}

@ -38,6 +38,7 @@ import com.google.protobuf.Message;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -59,22 +60,26 @@ import java.util.logging.Logger;
* intersection to two FieldMasks and traverse all fields specified by the * intersection to two FieldMasks and traverse all fields specified by the
* FieldMask in a message tree. * FieldMask in a message tree.
*/ */
class FieldMaskTree { final class FieldMaskTree {
private static final Logger logger = Logger.getLogger(FieldMaskTree.class.getName()); private static final Logger logger = Logger.getLogger(FieldMaskTree.class.getName());
private static final String FIELD_PATH_SEPARATOR_REGEX = "\\."; private static final String FIELD_PATH_SEPARATOR_REGEX = "\\.";
private static class Node { private static final class Node {
public TreeMap<String, Node> children = new TreeMap<String, Node>(); final SortedMap<String, Node> children = new TreeMap<String, Node>();
} }
private final Node root = new Node(); private final Node root = new Node();
/** Creates an empty FieldMaskTree. */ /**
public FieldMaskTree() {} * Creates an empty FieldMaskTree.
*/
FieldMaskTree() {}
/** Creates a FieldMaskTree for a given FieldMask. */ /**
public FieldMaskTree(FieldMask mask) { * Creates a FieldMaskTree for a given FieldMask.
*/
FieldMaskTree(FieldMask mask) {
mergeFromFieldMask(mask); mergeFromFieldMask(mask);
} }
@ -93,7 +98,7 @@ class FieldMaskTree {
* Likewise, if the field path to add is a sub-path of an existing leaf node, * Likewise, if the field path to add is a sub-path of an existing leaf node,
* nothing will be changed in the tree. * nothing will be changed in the tree.
*/ */
public FieldMaskTree addFieldPath(String path) { FieldMaskTree addFieldPath(String path) {
String[] parts = path.split(FIELD_PATH_SEPARATOR_REGEX); String[] parts = path.split(FIELD_PATH_SEPARATOR_REGEX);
if (parts.length == 0) { if (parts.length == 0) {
return this; return this;
@ -124,15 +129,17 @@ class FieldMaskTree {
/** /**
* Merges all field paths in a FieldMask into this tree. * Merges all field paths in a FieldMask into this tree.
*/ */
public FieldMaskTree mergeFromFieldMask(FieldMask mask) { FieldMaskTree mergeFromFieldMask(FieldMask mask) {
for (String path : mask.getPathsList()) { for (String path : mask.getPathsList()) {
addFieldPath(path); addFieldPath(path);
} }
return this; return this;
} }
/** Converts this tree to a FieldMask. */ /**
public FieldMask toFieldMask() { * Converts this tree to a FieldMask.
*/
FieldMask toFieldMask() {
if (root.children.isEmpty()) { if (root.children.isEmpty()) {
return FieldMask.getDefaultInstance(); return FieldMask.getDefaultInstance();
} }
@ -141,7 +148,9 @@ class FieldMaskTree {
return FieldMask.newBuilder().addAllPaths(paths).build(); return FieldMask.newBuilder().addAllPaths(paths).build();
} }
/** Gathers all field paths in a sub-tree. */ /**
* Gathers all field paths in a sub-tree.
*/
private void getFieldPaths(Node node, String path, List<String> paths) { private void getFieldPaths(Node node, String path, List<String> paths) {
if (node.children.isEmpty()) { if (node.children.isEmpty()) {
paths.add(path); paths.add(path);
@ -154,10 +163,9 @@ class FieldMaskTree {
} }
/** /**
* Adds the intersection of this tree with the given {@code path} to * Adds the intersection of this tree with the given {@code path} to {@code output}.
* {@code output}.
*/ */
public void intersectFieldPath(String path, FieldMaskTree output) { void intersectFieldPath(String path, FieldMaskTree output) {
if (root.children.isEmpty()) { if (root.children.isEmpty()) {
return; return;
} }
@ -188,11 +196,9 @@ class FieldMaskTree {
} }
/** /**
* Merges all fields specified by this FieldMaskTree from {@code source} to * Merges all fields specified by this FieldMaskTree from {@code source} to {@code destination}.
* {@code destination}.
*/ */
public void merge( void merge(Message source, Message.Builder destination, FieldMaskUtil.MergeOptions options) {
Message source, Message.Builder destination, FieldMaskUtil.MergeOptions options) {
if (source.getDescriptorForType() != destination.getDescriptorForType()) { if (source.getDescriptorForType() != destination.getDescriptorForType()) {
throw new IllegalArgumentException("Cannot merge messages of different types."); throw new IllegalArgumentException("Cannot merge messages of different types.");
} }
@ -202,8 +208,8 @@ class FieldMaskTree {
merge(root, "", source, destination, options); merge(root, "", source, destination, options);
} }
/** Merges all fields specified by a sub-tree from {@code source} to /**
* {@code destination}. * Merges all fields specified by a sub-tree from {@code source} to {@code destination}.
*/ */
private void merge( private void merge(
Node node, Node node,

@ -32,6 +32,9 @@ package com.google.protobuf.util;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.base.CaseFormat;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.Descriptors.FieldDescriptor;
@ -39,7 +42,9 @@ import com.google.protobuf.FieldMask;
import com.google.protobuf.Internal; import com.google.protobuf.Internal;
import com.google.protobuf.Message; import com.google.protobuf.Message;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
/** /**
* Utility helper functions to work with {@link com.google.protobuf.FieldMask}. * Utility helper functions to work with {@link com.google.protobuf.FieldMask}.
@ -78,8 +83,7 @@ public class FieldMaskUtil {
*/ */
public static FieldMask fromString(String value) { public static FieldMask fromString(String value) {
// TODO(xiaofeng): Consider using com.google.common.base.Splitter here instead. // TODO(xiaofeng): Consider using com.google.common.base.Splitter here instead.
return fromStringList( return fromStringList(null, Arrays.asList(value.split(FIELD_PATH_SEPARATOR_REGEX)));
null, Arrays.asList(value.split(FIELD_PATH_SEPARATOR_REGEX)));
} }
/** /**
@ -89,8 +93,7 @@ public class FieldMaskUtil {
*/ */
public static FieldMask fromString(Class<? extends Message> type, String value) { public static FieldMask fromString(Class<? extends Message> type, String value) {
// TODO(xiaofeng): Consider using com.google.common.base.Splitter here instead. // TODO(xiaofeng): Consider using com.google.common.base.Splitter here instead.
return fromStringList( return fromStringList(type, Arrays.asList(value.split(FIELD_PATH_SEPARATOR_REGEX)));
type, Arrays.asList(value.split(FIELD_PATH_SEPARATOR_REGEX)));
} }
/** /**
@ -99,8 +102,7 @@ public class FieldMaskUtil {
* @throws IllegalArgumentException if any of the field path is not valid. * @throws IllegalArgumentException if any of the field path is not valid.
*/ */
// TODO(xiaofeng): Consider renaming fromStrings() // TODO(xiaofeng): Consider renaming fromStrings()
public static FieldMask fromStringList( public static FieldMask fromStringList(Class<? extends Message> type, Iterable<String> paths) {
Class<? extends Message> type, Iterable<String> paths) {
FieldMask.Builder builder = FieldMask.newBuilder(); FieldMask.Builder builder = FieldMask.newBuilder();
for (String path : paths) { for (String path : paths) {
if (path.isEmpty()) { if (path.isEmpty()) {
@ -108,8 +110,7 @@ public class FieldMaskUtil {
continue; continue;
} }
if (type != null && !isValid(type, path)) { if (type != null && !isValid(type, path)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(path + " is not a valid path for " + type);
path + " is not a valid path for " + type);
} }
builder.addPaths(path); builder.addPaths(path);
} }
@ -145,12 +146,42 @@ public class FieldMaskUtil {
return builder.build(); return builder.build();
} }
/**
* Converts a field mask to a Proto3 JSON string, that is converting from snake case to camel
* case and joining all paths into one string with commas.
*/
public static String toJsonString(FieldMask fieldMask) {
List<String> paths = new ArrayList<String>(fieldMask.getPathsCount());
for (String path : fieldMask.getPathsList()) {
if (path.isEmpty()) {
continue;
}
paths.add(CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, path));
}
return Joiner.on(FIELD_PATH_SEPARATOR).join(paths);
}
/**
* Converts a field mask from a Proto3 JSON string, that is splitting the paths along commas and
* converting from camel case to snake case.
*/
public static FieldMask fromJsonString(String value) {
Iterable<String> paths = Splitter.on(FIELD_PATH_SEPARATOR).split(value);
FieldMask.Builder builder = FieldMask.newBuilder();
for (String path : paths) {
if (path.isEmpty()) {
continue;
}
builder.addPaths(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, path));
}
return builder.build();
}
/** /**
* Checks whether paths in a given fields mask are valid. * Checks whether paths in a given fields mask are valid.
*/ */
public static boolean isValid(Class<? extends Message> type, FieldMask fieldMask) { public static boolean isValid(Class<? extends Message> type, FieldMask fieldMask) {
Descriptor descriptor = Descriptor descriptor = Internal.getDefaultInstance(type).getDescriptorForType();
Internal.getDefaultInstance(type).getDescriptorForType();
return isValid(descriptor, fieldMask); return isValid(descriptor, fieldMask);
} }
@ -171,8 +202,7 @@ public class FieldMaskUtil {
* Checks whether a given field path is valid. * Checks whether a given field path is valid.
*/ */
public static boolean isValid(Class<? extends Message> type, String path) { public static boolean isValid(Class<? extends Message> type, String path) {
Descriptor descriptor = Descriptor descriptor = Internal.getDefaultInstance(type).getDescriptorForType();
Internal.getDefaultInstance(type).getDescriptorForType();
return isValid(descriptor, path); return isValid(descriptor, path);
} }
@ -193,8 +223,7 @@ public class FieldMaskUtil {
if (field == null) { if (field == null) {
return false; return false;
} }
if (!field.isRepeated() if (!field.isRepeated() && field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
&& field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
descriptor = field.getMessageType(); descriptor = field.getMessageType();
} else { } else {
descriptor = null; descriptor = null;
@ -299,16 +328,15 @@ public class FieldMaskUtil {
* Merges fields specified by a FieldMask from one message to another with the * Merges fields specified by a FieldMask from one message to another with the
* specified merge options. * specified merge options.
*/ */
public static void merge(FieldMask mask, Message source, public static void merge(
Message.Builder destination, MergeOptions options) { FieldMask mask, Message source, Message.Builder destination, MergeOptions options) {
new FieldMaskTree(mask).merge(source, destination, options); new FieldMaskTree(mask).merge(source, destination, options);
} }
/** /**
* Merges fields specified by a FieldMask from one message to another. * Merges fields specified by a FieldMask from one message to another.
*/ */
public static void merge(FieldMask mask, Message source, public static void merge(FieldMask mask, Message source, Message.Builder destination) {
Message.Builder destination) {
merge(mask, source, destination, new MergeOptions()); merge(mask, source, destination, new MergeOptions());
} }
} }

@ -60,6 +60,7 @@ import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.ListValue; import com.google.protobuf.ListValue;
import com.google.protobuf.Message; import com.google.protobuf.Message;
import com.google.protobuf.MessageOrBuilder; import com.google.protobuf.MessageOrBuilder;
import com.google.protobuf.NullValue;
import com.google.protobuf.StringValue; import com.google.protobuf.StringValue;
import com.google.protobuf.Struct; import com.google.protobuf.Struct;
import com.google.protobuf.Timestamp; import com.google.protobuf.Timestamp;
@ -92,8 +93,7 @@ import java.util.logging.Logger;
* as well. * as well.
*/ */
public class JsonFormat { public class JsonFormat {
private static final Logger logger = private static final Logger logger = Logger.getLogger(JsonFormat.class.getName());
Logger.getLogger(JsonFormat.class.getName());
private JsonFormat() {} private JsonFormat() {}
@ -161,8 +161,7 @@ public class JsonFormat {
* that can't be resolved. * that can't be resolved.
* @throws IOException if writing to the output fails. * @throws IOException if writing to the output fails.
*/ */
public void appendTo(MessageOrBuilder message, Appendable output) public void appendTo(MessageOrBuilder message, Appendable output) throws IOException {
throws IOException {
// TODO(xiaofeng): Investigate the allocation overhead and optimize for // TODO(xiaofeng): Investigate the allocation overhead and optimize for
// mobile. // mobile.
new PrinterImpl(registry, includingDefaultValueFields, preservingProtoFieldNames, output) new PrinterImpl(registry, includingDefaultValueFields, preservingProtoFieldNames, output)
@ -173,8 +172,7 @@ public class JsonFormat {
* Converts a protobuf message to JSON format. Throws exceptions if there * Converts a protobuf message to JSON format. Throws exceptions if there
* are unknown Any types in the message. * are unknown Any types in the message.
*/ */
public String print(MessageOrBuilder message) public String print(MessageOrBuilder message) throws InvalidProtocolBufferException {
throws InvalidProtocolBufferException {
try { try {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
appendTo(message, builder); appendTo(message, builder);
@ -224,8 +222,7 @@ public class JsonFormat {
* @throws InvalidProtocolBufferException if the input is not valid JSON * @throws InvalidProtocolBufferException if the input is not valid JSON
* format or there are unknown fields in the input. * format or there are unknown fields in the input.
*/ */
public void merge(String json, Message.Builder builder) public void merge(String json, Message.Builder builder) throws InvalidProtocolBufferException {
throws InvalidProtocolBufferException {
// TODO(xiaofeng): Investigate the allocation overhead and optimize for // TODO(xiaofeng): Investigate the allocation overhead and optimize for
// mobile. // mobile.
new ParserImpl(registry).merge(json, builder); new ParserImpl(registry).merge(json, builder);
@ -238,8 +235,7 @@ public class JsonFormat {
* format or there are unknown fields in the input. * format or there are unknown fields in the input.
* @throws IOException if reading from the input throws. * @throws IOException if reading from the input throws.
*/ */
public void merge(Reader json, Message.Builder builder) public void merge(Reader json, Message.Builder builder) throws IOException {
throws IOException {
// TODO(xiaofeng): Investigate the allocation overhead and optimize for // TODO(xiaofeng): Investigate the allocation overhead and optimize for
// mobile. // mobile.
new ParserImpl(registry).merge(json, builder); new ParserImpl(registry).merge(json, builder);
@ -255,8 +251,8 @@ public class JsonFormat {
*/ */
public static class TypeRegistry { public static class TypeRegistry {
private static class EmptyTypeRegistryHolder { private static class EmptyTypeRegistryHolder {
private static final TypeRegistry EMPTY = new TypeRegistry( private static final TypeRegistry EMPTY =
Collections.<String, Descriptor>emptyMap()); new TypeRegistry(Collections.<String, Descriptor>emptyMap());
} }
public static TypeRegistry getEmptyTypeRegistry() { public static TypeRegistry getEmptyTypeRegistry() {
@ -293,8 +289,7 @@ public class JsonFormat {
*/ */
public Builder add(Descriptor messageType) { public Builder add(Descriptor messageType) {
if (types == null) { if (types == null) {
throw new IllegalStateException( throw new IllegalStateException("A TypeRegistry.Builer can only be used once.");
"A TypeRegistry.Builer can only be used once.");
} }
addFile(messageType.getFile()); addFile(messageType.getFile());
return this; return this;
@ -306,8 +301,7 @@ public class JsonFormat {
*/ */
public Builder add(Iterable<Descriptor> messageTypes) { public Builder add(Iterable<Descriptor> messageTypes) {
if (types == null) { if (types == null) {
throw new IllegalStateException( throw new IllegalStateException("A TypeRegistry.Builer can only be used once.");
"A TypeRegistry.Builer can only be used once.");
} }
for (Descriptor type : messageTypes) { for (Descriptor type : messageTypes) {
addFile(type.getFile()); addFile(type.getFile());
@ -345,8 +339,7 @@ public class JsonFormat {
} }
if (types.containsKey(message.getFullName())) { if (types.containsKey(message.getFullName())) {
logger.warning("Type " + message.getFullName() logger.warning("Type " + message.getFullName() + " is added multiple times.");
+ " is added multiple times.");
return; return;
} }
@ -354,8 +347,7 @@ public class JsonFormat {
} }
private final Set<String> files = new HashSet<String>(); private final Set<String> files = new HashSet<String>();
private Map<String, Descriptor> types = private Map<String, Descriptor> types = new HashMap<String, Descriptor>();
new HashMap<String, Descriptor>();
} }
} }
@ -387,8 +379,7 @@ public class JsonFormat {
public void outdent() { public void outdent() {
final int length = indent.length(); final int length = indent.length();
if (length < 2) { if (length < 2) {
throw new IllegalArgumentException( throw new IllegalArgumentException(" Outdent() without matching Indent().");
" Outdent() without matching Indent().");
} }
indent.delete(length - 2, length); indent.delete(length - 2, length);
} }
@ -450,8 +441,8 @@ public class JsonFormat {
} }
void print(MessageOrBuilder message) throws IOException { void print(MessageOrBuilder message) throws IOException {
WellKnownTypePrinter specialPrinter = wellKnownTypePrinters.get( WellKnownTypePrinter specialPrinter =
message.getDescriptorForType().getFullName()); wellKnownTypePrinters.get(message.getDescriptorForType().getFullName());
if (specialPrinter != null) { if (specialPrinter != null) {
specialPrinter.print(this, message); specialPrinter.print(this, message);
return; return;
@ -460,33 +451,29 @@ public class JsonFormat {
} }
private interface WellKnownTypePrinter { private interface WellKnownTypePrinter {
void print(PrinterImpl printer, MessageOrBuilder message) void print(PrinterImpl printer, MessageOrBuilder message) throws IOException;
throws IOException;
} }
private static final Map<String, WellKnownTypePrinter> private static final Map<String, WellKnownTypePrinter> wellKnownTypePrinters =
wellKnownTypePrinters = buildWellKnownTypePrinters(); buildWellKnownTypePrinters();
private static Map<String, WellKnownTypePrinter> private static Map<String, WellKnownTypePrinter> buildWellKnownTypePrinters() {
buildWellKnownTypePrinters() { Map<String, WellKnownTypePrinter> printers = new HashMap<String, WellKnownTypePrinter>();
Map<String, WellKnownTypePrinter> printers =
new HashMap<String, WellKnownTypePrinter>();
// Special-case Any. // Special-case Any.
printers.put(Any.getDescriptor().getFullName(), printers.put(
Any.getDescriptor().getFullName(),
new WellKnownTypePrinter() { new WellKnownTypePrinter() {
@Override @Override
public void print(PrinterImpl printer, MessageOrBuilder message) public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
throws IOException {
printer.printAny(message); printer.printAny(message);
} }
}); });
// Special-case wrapper types. // Special-case wrapper types.
WellKnownTypePrinter wrappersPrinter = new WellKnownTypePrinter() { WellKnownTypePrinter wrappersPrinter =
new WellKnownTypePrinter() {
@Override @Override
public void print(PrinterImpl printer, MessageOrBuilder message) public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
throws IOException {
printer.printWrapper(message); printer.printWrapper(message);
} }
}; };
printers.put(BoolValue.getDescriptor().getFullName(), wrappersPrinter); printers.put(BoolValue.getDescriptor().getFullName(), wrappersPrinter);
@ -499,56 +486,56 @@ public class JsonFormat {
printers.put(FloatValue.getDescriptor().getFullName(), wrappersPrinter); printers.put(FloatValue.getDescriptor().getFullName(), wrappersPrinter);
printers.put(DoubleValue.getDescriptor().getFullName(), wrappersPrinter); printers.put(DoubleValue.getDescriptor().getFullName(), wrappersPrinter);
// Special-case Timestamp. // Special-case Timestamp.
printers.put(Timestamp.getDescriptor().getFullName(), printers.put(
Timestamp.getDescriptor().getFullName(),
new WellKnownTypePrinter() { new WellKnownTypePrinter() {
@Override @Override
public void print(PrinterImpl printer, MessageOrBuilder message) public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
throws IOException {
printer.printTimestamp(message); printer.printTimestamp(message);
} }
}); });
// Special-case Duration. // Special-case Duration.
printers.put(Duration.getDescriptor().getFullName(), printers.put(
Duration.getDescriptor().getFullName(),
new WellKnownTypePrinter() { new WellKnownTypePrinter() {
@Override @Override
public void print(PrinterImpl printer, MessageOrBuilder message) public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
throws IOException {
printer.printDuration(message); printer.printDuration(message);
} }
}); });
// Special-case FieldMask. // Special-case FieldMask.
printers.put(FieldMask.getDescriptor().getFullName(), printers.put(
FieldMask.getDescriptor().getFullName(),
new WellKnownTypePrinter() { new WellKnownTypePrinter() {
@Override @Override
public void print(PrinterImpl printer, MessageOrBuilder message) public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
throws IOException {
printer.printFieldMask(message); printer.printFieldMask(message);
} }
}); });
// Special-case Struct. // Special-case Struct.
printers.put(Struct.getDescriptor().getFullName(), printers.put(
Struct.getDescriptor().getFullName(),
new WellKnownTypePrinter() { new WellKnownTypePrinter() {
@Override @Override
public void print(PrinterImpl printer, MessageOrBuilder message) public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
throws IOException {
printer.printStruct(message); printer.printStruct(message);
} }
}); });
// Special-case Value. // Special-case Value.
printers.put(Value.getDescriptor().getFullName(), printers.put(
Value.getDescriptor().getFullName(),
new WellKnownTypePrinter() { new WellKnownTypePrinter() {
@Override @Override
public void print(PrinterImpl printer, MessageOrBuilder message) public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
throws IOException {
printer.printValue(message); printer.printValue(message);
} }
}); });
// Special-case ListValue. // Special-case ListValue.
printers.put(ListValue.getDescriptor().getFullName(), printers.put(
ListValue.getDescriptor().getFullName(),
new WellKnownTypePrinter() { new WellKnownTypePrinter() {
@Override @Override
public void print(PrinterImpl printer, MessageOrBuilder message) public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
throws IOException {
printer.printListValue(message); printer.printListValue(message);
} }
}); });
@ -562,7 +549,8 @@ public class JsonFormat {
FieldDescriptor valueField = descriptor.findFieldByName("value"); FieldDescriptor valueField = descriptor.findFieldByName("value");
// Validates type of the message. Note that we can't just cast the message // Validates type of the message. Note that we can't just cast the message
// to com.google.protobuf.Any because it might be a DynamicMessage. // to com.google.protobuf.Any because it might be a DynamicMessage.
if (typeUrlField == null || valueField == null if (typeUrlField == null
|| valueField == null
|| typeUrlField.getType() != FieldDescriptor.Type.STRING || typeUrlField.getType() != FieldDescriptor.Type.STRING
|| valueField.getType() != FieldDescriptor.Type.BYTES) { || valueField.getType() != FieldDescriptor.Type.BYTES) {
throw new InvalidProtocolBufferException("Invalid Any type."); throw new InvalidProtocolBufferException("Invalid Any type.");
@ -571,12 +559,11 @@ public class JsonFormat {
String typeName = getTypeName(typeUrl); String typeName = getTypeName(typeUrl);
Descriptor type = registry.find(typeName); Descriptor type = registry.find(typeName);
if (type == null) { if (type == null) {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException("Cannot find type for url: " + typeUrl);
"Cannot find type for url: " + typeUrl);
} }
ByteString content = (ByteString) message.getField(valueField); ByteString content = (ByteString) message.getField(valueField);
Message contentMessage = DynamicMessage.getDefaultInstance(type) Message contentMessage =
.getParserForType().parseFrom(content); DynamicMessage.getDefaultInstance(type).getParserForType().parseFrom(content);
WellKnownTypePrinter printer = wellKnownTypePrinters.get(typeName); WellKnownTypePrinter printer = wellKnownTypePrinters.get(typeName);
if (printer != null) { if (printer != null) {
// If the type is one of the well-known types, we use a special // If the type is one of the well-known types, we use a special
@ -618,20 +605,19 @@ public class JsonFormat {
/** Prints google.protobuf.Timestamp */ /** Prints google.protobuf.Timestamp */
private void printTimestamp(MessageOrBuilder message) throws IOException { private void printTimestamp(MessageOrBuilder message) throws IOException {
Timestamp value = Timestamp.parseFrom(toByteString(message)); Timestamp value = Timestamp.parseFrom(toByteString(message));
generator.print("\"" + TimeUtil.toString(value) + "\""); generator.print("\"" + Timestamps.toString(value) + "\"");
} }
/** Prints google.protobuf.Duration */ /** Prints google.protobuf.Duration */
private void printDuration(MessageOrBuilder message) throws IOException { private void printDuration(MessageOrBuilder message) throws IOException {
Duration value = Duration.parseFrom(toByteString(message)); Duration value = Duration.parseFrom(toByteString(message));
generator.print("\"" + TimeUtil.toString(value) + "\""); generator.print("\"" + Durations.toString(value) + "\"");
} }
/** Prints google.protobuf.FieldMask */ /** Prints google.protobuf.FieldMask */
private void printFieldMask(MessageOrBuilder message) throws IOException { private void printFieldMask(MessageOrBuilder message) throws IOException {
FieldMask value = FieldMask.parseFrom(toByteString(message)); FieldMask value = FieldMask.parseFrom(toByteString(message));
generator.print("\"" + FieldMaskUtil.toString(value) + "\""); generator.print("\"" + FieldMaskUtil.toJsonString(value) + "\"");
} }
/** Prints google.protobuf.Struct */ /** Prints google.protobuf.Struct */
@ -675,8 +661,7 @@ public class JsonFormat {
} }
/** Prints a regular message with an optional type URL. */ /** Prints a regular message with an optional type URL. */
private void print(MessageOrBuilder message, String typeUrl) private void print(MessageOrBuilder message, String typeUrl) throws IOException {
throws IOException {
generator.print("{\n"); generator.print("{\n");
generator.indent(); generator.indent();
@ -719,8 +704,7 @@ public class JsonFormat {
generator.print("}"); generator.print("}");
} }
private void printField(FieldDescriptor field, Object value) private void printField(FieldDescriptor field, Object value) throws IOException {
throws IOException {
if (preservingProtoFieldNames) { if (preservingProtoFieldNames) {
generator.print("\"" + field.getName() + "\": "); generator.print("\"" + field.getName() + "\": ");
} else { } else {
@ -736,8 +720,7 @@ public class JsonFormat {
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
private void printRepeatedFieldValue(FieldDescriptor field, Object value) private void printRepeatedFieldValue(FieldDescriptor field, Object value) throws IOException {
throws IOException {
generator.print("["); generator.print("[");
boolean printedElement = false; boolean printedElement = false;
for (Object element : (List) value) { for (Object element : (List) value) {
@ -752,8 +735,7 @@ public class JsonFormat {
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
private void printMapFieldValue(FieldDescriptor field, Object value) private void printMapFieldValue(FieldDescriptor field, Object value) throws IOException {
throws IOException {
Descriptor type = field.getMessageType(); Descriptor type = field.getMessageType();
FieldDescriptor keyField = type.findFieldByName("key"); FieldDescriptor keyField = type.findFieldByName("key");
FieldDescriptor valueField = type.findFieldByName("value"); FieldDescriptor valueField = type.findFieldByName("value");
@ -784,8 +766,7 @@ public class JsonFormat {
generator.print("}"); generator.print("}");
} }
private void printSingleFieldValue(FieldDescriptor field, Object value) private void printSingleFieldValue(FieldDescriptor field, Object value) throws IOException {
throws IOException {
printSingleFieldValue(field, value, false); printSingleFieldValue(field, value, false);
} }
@ -796,8 +777,8 @@ public class JsonFormat {
* types. * types.
*/ */
private void printSingleFieldValue( private void printSingleFieldValue(
final FieldDescriptor field, final Object value, final FieldDescriptor field, final Object value, boolean alwaysWithQuotes)
boolean alwaysWithQuotes) throws IOException { throws IOException {
switch (field.getType()) { switch (field.getType()) {
case INT32: case INT32:
case SINT32: case SINT32:
@ -895,15 +876,13 @@ public class JsonFormat {
case BYTES: case BYTES:
generator.print("\""); generator.print("\"");
generator.print( generator.print(BaseEncoding.base64().encode(((ByteString) value).toByteArray()));
BaseEncoding.base64().encode(((ByteString) value).toByteArray()));
generator.print("\""); generator.print("\"");
break; break;
case ENUM: case ENUM:
// Special-case google.protobuf.NullValue (it's an Enum). // Special-case google.protobuf.NullValue (it's an Enum).
if (field.getEnumType().getFullName().equals( if (field.getEnumType().getFullName().equals("google.protobuf.NullValue")) {
"google.protobuf.NullValue")) {
// No matter what value it contains, we always print it as "null". // No matter what value it contains, we always print it as "null".
if (alwaysWithQuotes) { if (alwaysWithQuotes) {
generator.print("\""); generator.print("\"");
@ -914,11 +893,9 @@ public class JsonFormat {
} }
} else { } else {
if (((EnumValueDescriptor) value).getIndex() == -1) { if (((EnumValueDescriptor) value).getIndex() == -1) {
generator.print( generator.print(String.valueOf(((EnumValueDescriptor) value).getNumber()));
String.valueOf(((EnumValueDescriptor) value).getNumber()));
} else { } else {
generator.print( generator.print("\"" + ((EnumValueDescriptor) value).getName() + "\"");
"\"" + ((EnumValueDescriptor) value).getName() + "\"");
} }
} }
break; break;
@ -947,18 +924,14 @@ public class JsonFormat {
} else { } else {
// Pull off the most-significant bit so that BigInteger doesn't think // Pull off the most-significant bit so that BigInteger doesn't think
// the number is negative, then set it again using setBit(). // the number is negative, then set it again using setBit().
return BigInteger.valueOf(value & Long.MAX_VALUE) return BigInteger.valueOf(value & Long.MAX_VALUE).setBit(Long.SIZE - 1).toString();
.setBit(Long.SIZE - 1).toString();
} }
} }
private static String getTypeName(String typeUrl) throws InvalidProtocolBufferException {
private static String getTypeName(String typeUrl)
throws InvalidProtocolBufferException {
String[] parts = typeUrl.split("/"); String[] parts = typeUrl.split("/");
if (parts.length == 1) { if (parts.length == 1) {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException("Invalid type url found: " + typeUrl);
"Invalid type url found: " + typeUrl);
} }
return parts[parts.length - 1]; return parts[parts.length - 1];
} }
@ -972,15 +945,13 @@ public class JsonFormat {
this.jsonParser = new JsonParser(); this.jsonParser = new JsonParser();
} }
void merge(Reader json, Message.Builder builder) void merge(Reader json, Message.Builder builder) throws IOException {
throws IOException {
JsonReader reader = new JsonReader(json); JsonReader reader = new JsonReader(json);
reader.setLenient(false); reader.setLenient(false);
merge(jsonParser.parse(reader), builder); merge(jsonParser.parse(reader), builder);
} }
void merge(String json, Message.Builder builder) void merge(String json, Message.Builder builder) throws InvalidProtocolBufferException {
throws InvalidProtocolBufferException {
try { try {
JsonReader reader = new JsonReader(new StringReader(json)); JsonReader reader = new JsonReader(new StringReader(json));
reader.setLenient(false); reader.setLenient(false);
@ -1001,23 +972,24 @@ public class JsonFormat {
private static final Map<String, WellKnownTypeParser> wellKnownTypeParsers = private static final Map<String, WellKnownTypeParser> wellKnownTypeParsers =
buildWellKnownTypeParsers(); buildWellKnownTypeParsers();
private static Map<String, WellKnownTypeParser> private static Map<String, WellKnownTypeParser> buildWellKnownTypeParsers() {
buildWellKnownTypeParsers() { Map<String, WellKnownTypeParser> parsers = new HashMap<String, WellKnownTypeParser>();
Map<String, WellKnownTypeParser> parsers =
new HashMap<String, WellKnownTypeParser>();
// Special-case Any. // Special-case Any.
parsers.put(Any.getDescriptor().getFullName(), new WellKnownTypeParser() { parsers.put(
Any.getDescriptor().getFullName(),
new WellKnownTypeParser() {
@Override @Override
public void merge(ParserImpl parser, JsonElement json, public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
Message.Builder builder) throws InvalidProtocolBufferException { throws InvalidProtocolBufferException {
parser.mergeAny(json, builder); parser.mergeAny(json, builder);
} }
}); });
// Special-case wrapper types. // Special-case wrapper types.
WellKnownTypeParser wrappersPrinter = new WellKnownTypeParser() { WellKnownTypeParser wrappersPrinter =
new WellKnownTypeParser() {
@Override @Override
public void merge(ParserImpl parser, JsonElement json, public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
Message.Builder builder) throws InvalidProtocolBufferException { throws InvalidProtocolBufferException {
parser.mergeWrapper(json, builder); parser.mergeWrapper(json, builder);
} }
}; };
@ -1031,56 +1003,62 @@ public class JsonFormat {
parsers.put(FloatValue.getDescriptor().getFullName(), wrappersPrinter); parsers.put(FloatValue.getDescriptor().getFullName(), wrappersPrinter);
parsers.put(DoubleValue.getDescriptor().getFullName(), wrappersPrinter); parsers.put(DoubleValue.getDescriptor().getFullName(), wrappersPrinter);
// Special-case Timestamp. // Special-case Timestamp.
parsers.put(Timestamp.getDescriptor().getFullName(), parsers.put(
Timestamp.getDescriptor().getFullName(),
new WellKnownTypeParser() { new WellKnownTypeParser() {
@Override @Override
public void merge(ParserImpl parser, JsonElement json, public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
Message.Builder builder) throws InvalidProtocolBufferException { throws InvalidProtocolBufferException {
parser.mergeTimestamp(json, builder); parser.mergeTimestamp(json, builder);
} }
}); });
// Special-case Duration. // Special-case Duration.
parsers.put(Duration.getDescriptor().getFullName(), parsers.put(
Duration.getDescriptor().getFullName(),
new WellKnownTypeParser() { new WellKnownTypeParser() {
@Override @Override
public void merge(ParserImpl parser, JsonElement json, public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
Message.Builder builder) throws InvalidProtocolBufferException { throws InvalidProtocolBufferException {
parser.mergeDuration(json, builder); parser.mergeDuration(json, builder);
} }
}); });
// Special-case FieldMask. // Special-case FieldMask.
parsers.put(FieldMask.getDescriptor().getFullName(), parsers.put(
FieldMask.getDescriptor().getFullName(),
new WellKnownTypeParser() { new WellKnownTypeParser() {
@Override @Override
public void merge(ParserImpl parser, JsonElement json, public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
Message.Builder builder) throws InvalidProtocolBufferException { throws InvalidProtocolBufferException {
parser.mergeFieldMask(json, builder); parser.mergeFieldMask(json, builder);
} }
}); });
// Special-case Struct. // Special-case Struct.
parsers.put(Struct.getDescriptor().getFullName(), parsers.put(
Struct.getDescriptor().getFullName(),
new WellKnownTypeParser() { new WellKnownTypeParser() {
@Override @Override
public void merge(ParserImpl parser, JsonElement json, public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
Message.Builder builder) throws InvalidProtocolBufferException { throws InvalidProtocolBufferException {
parser.mergeStruct(json, builder); parser.mergeStruct(json, builder);
} }
}); });
// Special-case ListValue. // Special-case ListValue.
parsers.put(ListValue.getDescriptor().getFullName(), parsers.put(
ListValue.getDescriptor().getFullName(),
new WellKnownTypeParser() { new WellKnownTypeParser() {
@Override @Override
public void merge(ParserImpl parser, JsonElement json, public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
Message.Builder builder) throws InvalidProtocolBufferException { throws InvalidProtocolBufferException {
parser.mergeListValue(json, builder); parser.mergeListValue(json, builder);
} }
}); });
// Special-case Value. // Special-case Value.
parsers.put(Value.getDescriptor().getFullName(), parsers.put(
Value.getDescriptor().getFullName(),
new WellKnownTypeParser() { new WellKnownTypeParser() {
@Override @Override
public void merge(ParserImpl parser, JsonElement json, public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
Message.Builder builder) throws InvalidProtocolBufferException { throws InvalidProtocolBufferException {
parser.mergeValue(json, builder); parser.mergeValue(json, builder);
} }
}); });
@ -1089,8 +1067,8 @@ public class JsonFormat {
private void merge(JsonElement json, Message.Builder builder) private void merge(JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException { throws InvalidProtocolBufferException {
WellKnownTypeParser specialParser = wellKnownTypeParsers.get( WellKnownTypeParser specialParser =
builder.getDescriptorForType().getFullName()); wellKnownTypeParsers.get(builder.getDescriptorForType().getFullName());
if (specialParser != null) { if (specialParser != null) {
specialParser.merge(this, json, builder); specialParser.merge(this, json, builder);
return; return;
@ -1102,11 +1080,9 @@ public class JsonFormat {
private final Map<Descriptor, Map<String, FieldDescriptor>> fieldNameMaps = private final Map<Descriptor, Map<String, FieldDescriptor>> fieldNameMaps =
new HashMap<Descriptor, Map<String, FieldDescriptor>>(); new HashMap<Descriptor, Map<String, FieldDescriptor>>();
private Map<String, FieldDescriptor> getFieldNameMap( private Map<String, FieldDescriptor> getFieldNameMap(Descriptor descriptor) {
Descriptor descriptor) {
if (!fieldNameMaps.containsKey(descriptor)) { if (!fieldNameMaps.containsKey(descriptor)) {
Map<String, FieldDescriptor> fieldNameMap = Map<String, FieldDescriptor> fieldNameMap = new HashMap<String, FieldDescriptor>();
new HashMap<String, FieldDescriptor>();
for (FieldDescriptor field : descriptor.getFields()) { for (FieldDescriptor field : descriptor.getFields()) {
fieldNameMap.put(field.getName(), field); fieldNameMap.put(field.getName(), field);
fieldNameMap.put(field.getJsonName(), field); fieldNameMap.put(field.getJsonName(), field);
@ -1117,15 +1093,13 @@ public class JsonFormat {
return fieldNameMaps.get(descriptor); return fieldNameMaps.get(descriptor);
} }
private void mergeMessage(JsonElement json, Message.Builder builder, private void mergeMessage(JsonElement json, Message.Builder builder, boolean skipTypeUrl)
boolean skipTypeUrl) throws InvalidProtocolBufferException { throws InvalidProtocolBufferException {
if (!(json instanceof JsonObject)) { if (!(json instanceof JsonObject)) {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException("Expect message object but got: " + json);
"Expect message object but got: " + json);
} }
JsonObject object = (JsonObject) json; JsonObject object = (JsonObject) json;
Map<String, FieldDescriptor> fieldNameMap = Map<String, FieldDescriptor> fieldNameMap = getFieldNameMap(builder.getDescriptorForType());
getFieldNameMap(builder.getDescriptorForType());
for (Map.Entry<String, JsonElement> entry : object.entrySet()) { for (Map.Entry<String, JsonElement> entry : object.entrySet()) {
if (skipTypeUrl && entry.getKey().equals("@type")) { if (skipTypeUrl && entry.getKey().equals("@type")) {
continue; continue;
@ -1133,7 +1107,9 @@ public class JsonFormat {
FieldDescriptor field = fieldNameMap.get(entry.getKey()); FieldDescriptor field = fieldNameMap.get(entry.getKey());
if (field == null) { if (field == null) {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException(
"Cannot find field: " + entry.getKey() + " in message " "Cannot find field: "
+ entry.getKey()
+ " in message "
+ builder.getDescriptorForType().getFullName()); + builder.getDescriptorForType().getFullName());
} }
mergeField(field, entry.getValue(), builder); mergeField(field, entry.getValue(), builder);
@ -1147,33 +1123,30 @@ public class JsonFormat {
FieldDescriptor valueField = descriptor.findFieldByName("value"); FieldDescriptor valueField = descriptor.findFieldByName("value");
// Validates type of the message. Note that we can't just cast the message // Validates type of the message. Note that we can't just cast the message
// to com.google.protobuf.Any because it might be a DynamicMessage. // to com.google.protobuf.Any because it might be a DynamicMessage.
if (typeUrlField == null || valueField == null if (typeUrlField == null
|| valueField == null
|| typeUrlField.getType() != FieldDescriptor.Type.STRING || typeUrlField.getType() != FieldDescriptor.Type.STRING
|| valueField.getType() != FieldDescriptor.Type.BYTES) { || valueField.getType() != FieldDescriptor.Type.BYTES) {
throw new InvalidProtocolBufferException("Invalid Any type."); throw new InvalidProtocolBufferException("Invalid Any type.");
} }
if (!(json instanceof JsonObject)) { if (!(json instanceof JsonObject)) {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException("Expect message object but got: " + json);
"Expect message object but got: " + json);
} }
JsonObject object = (JsonObject) json; JsonObject object = (JsonObject) json;
JsonElement typeUrlElement = object.get("@type"); JsonElement typeUrlElement = object.get("@type");
if (typeUrlElement == null) { if (typeUrlElement == null) {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException("Missing type url when parsing: " + json);
"Missing type url when parsing: " + json);
} }
String typeUrl = typeUrlElement.getAsString(); String typeUrl = typeUrlElement.getAsString();
Descriptor contentType = registry.find(getTypeName(typeUrl)); Descriptor contentType = registry.find(getTypeName(typeUrl));
if (contentType == null) { if (contentType == null) {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException("Cannot resolve type: " + typeUrl);
"Cannot resolve type: " + typeUrl);
} }
builder.setField(typeUrlField, typeUrl); builder.setField(typeUrlField, typeUrl);
Message.Builder contentBuilder = Message.Builder contentBuilder =
DynamicMessage.getDefaultInstance(contentType).newBuilderForType(); DynamicMessage.getDefaultInstance(contentType).newBuilderForType();
WellKnownTypeParser specialParser = WellKnownTypeParser specialParser = wellKnownTypeParsers.get(contentType.getFullName());
wellKnownTypeParsers.get(contentType.getFullName());
if (specialParser != null) { if (specialParser != null) {
JsonElement value = object.get("value"); JsonElement value = object.get("value");
if (value != null) { if (value != null) {
@ -1187,29 +1160,27 @@ public class JsonFormat {
private void mergeFieldMask(JsonElement json, Message.Builder builder) private void mergeFieldMask(JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException { throws InvalidProtocolBufferException {
FieldMask value = FieldMaskUtil.fromString(json.getAsString()); FieldMask value = FieldMaskUtil.fromJsonString(json.getAsString());
builder.mergeFrom(value.toByteString()); builder.mergeFrom(value.toByteString());
} }
private void mergeTimestamp(JsonElement json, Message.Builder builder) private void mergeTimestamp(JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException { throws InvalidProtocolBufferException {
try { try {
Timestamp value = TimeUtil.parseTimestamp(json.getAsString()); Timestamp value = Timestamps.parse(json.getAsString());
builder.mergeFrom(value.toByteString()); builder.mergeFrom(value.toByteString());
} catch (ParseException e) { } catch (ParseException e) {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException("Failed to parse timestamp: " + json);
"Failed to parse timestamp: " + json);
} }
} }
private void mergeDuration(JsonElement json, Message.Builder builder) private void mergeDuration(JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException { throws InvalidProtocolBufferException {
try { try {
Duration value = TimeUtil.parseDuration(json.getAsString()); Duration value = Durations.parse(json.getAsString());
builder.mergeFrom(value.toByteString()); builder.mergeFrom(value.toByteString());
} catch (ParseException e) { } catch (ParseException e) {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException("Failed to parse duration: " + json);
"Failed to parse duration: " + json);
} }
} }
@ -1239,14 +1210,11 @@ public class JsonFormat {
if (json instanceof JsonPrimitive) { if (json instanceof JsonPrimitive) {
JsonPrimitive primitive = (JsonPrimitive) json; JsonPrimitive primitive = (JsonPrimitive) json;
if (primitive.isBoolean()) { if (primitive.isBoolean()) {
builder.setField(type.findFieldByName("bool_value"), builder.setField(type.findFieldByName("bool_value"), primitive.getAsBoolean());
primitive.getAsBoolean());
} else if (primitive.isNumber()) { } else if (primitive.isNumber()) {
builder.setField(type.findFieldByName("number_value"), builder.setField(type.findFieldByName("number_value"), primitive.getAsDouble());
primitive.getAsDouble());
} else { } else {
builder.setField(type.findFieldByName("string_value"), builder.setField(type.findFieldByName("string_value"), primitive.getAsString());
primitive.getAsString());
} }
} else if (json instanceof JsonObject) { } else if (json instanceof JsonObject) {
FieldDescriptor field = type.findFieldByName("struct_value"); FieldDescriptor field = type.findFieldByName("struct_value");
@ -1268,14 +1236,13 @@ public class JsonFormat {
Descriptor type = builder.getDescriptorForType(); Descriptor type = builder.getDescriptorForType();
FieldDescriptor field = type.findFieldByName("value"); FieldDescriptor field = type.findFieldByName("value");
if (field == null) { if (field == null) {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException("Invalid wrapper type: " + type.getFullName());
"Invalid wrapper type: " + type.getFullName());
} }
builder.setField(field, parseFieldValue(field, json, builder)); builder.setField(field, parseFieldValue(field, json, builder));
} }
private void mergeField(FieldDescriptor field, JsonElement json, private void mergeField(FieldDescriptor field, JsonElement json, Message.Builder builder)
Message.Builder builder) throws InvalidProtocolBufferException { throws InvalidProtocolBufferException {
if (field.isRepeated()) { if (field.isRepeated()) {
if (builder.getRepeatedFieldCount(field) > 0) { if (builder.getRepeatedFieldCount(field) > 0) {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException(
@ -1290,8 +1257,11 @@ public class JsonFormat {
&& builder.getOneofFieldDescriptor(field.getContainingOneof()) != null) { && builder.getOneofFieldDescriptor(field.getContainingOneof()) != null) {
FieldDescriptor other = builder.getOneofFieldDescriptor(field.getContainingOneof()); FieldDescriptor other = builder.getOneofFieldDescriptor(field.getContainingOneof());
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException(
"Cannot set field " + field.getFullName() + " because another field " "Cannot set field "
+ other.getFullName() + " belonging to the same oneof has already been set "); + field.getFullName()
+ " because another field "
+ other.getFullName()
+ " belonging to the same oneof has already been set ");
} }
} }
if (field.isRepeated() && json instanceof JsonNull) { if (field.isRepeated() && json instanceof JsonNull) {
@ -1311,29 +1281,24 @@ public class JsonFormat {
} }
} }
private void mergeMapField(FieldDescriptor field, JsonElement json, private void mergeMapField(FieldDescriptor field, JsonElement json, Message.Builder builder)
Message.Builder builder) throws InvalidProtocolBufferException { throws InvalidProtocolBufferException {
if (!(json instanceof JsonObject)) { if (!(json instanceof JsonObject)) {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException("Expect a map object but found: " + json);
"Expect a map object but found: " + json);
} }
Descriptor type = field.getMessageType(); Descriptor type = field.getMessageType();
FieldDescriptor keyField = type.findFieldByName("key"); FieldDescriptor keyField = type.findFieldByName("key");
FieldDescriptor valueField = type.findFieldByName("value"); FieldDescriptor valueField = type.findFieldByName("value");
if (keyField == null || valueField == null) { if (keyField == null || valueField == null) {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException("Invalid map field: " + field.getFullName());
"Invalid map field: " + field.getFullName());
} }
JsonObject object = (JsonObject) json; JsonObject object = (JsonObject) json;
for (Map.Entry<String, JsonElement> entry : object.entrySet()) { for (Map.Entry<String, JsonElement> entry : object.entrySet()) {
Message.Builder entryBuilder = builder.newBuilderForField(field); Message.Builder entryBuilder = builder.newBuilderForField(field);
Object key = parseFieldValue( Object key = parseFieldValue(keyField, new JsonPrimitive(entry.getKey()), entryBuilder);
keyField, new JsonPrimitive(entry.getKey()), entryBuilder); Object value = parseFieldValue(valueField, entry.getValue(), entryBuilder);
Object value = parseFieldValue(
valueField, entry.getValue(), entryBuilder);
if (value == null) { if (value == null) {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException("Map value cannot be null.");
"Map value cannot be null.");
} }
entryBuilder.setField(keyField, key); entryBuilder.setField(keyField, key);
entryBuilder.setField(valueField, value); entryBuilder.setField(valueField, value);
@ -1346,8 +1311,7 @@ public class JsonFormat {
* language defaults and ignore any default values set through the * language defaults and ignore any default values set through the
* proto "default" option. * proto "default" option.
*/ */
private Object getDefaultValue(FieldDescriptor field, private Object getDefaultValue(FieldDescriptor field, Message.Builder builder) {
Message.Builder builder) {
switch (field.getType()) { switch (field.getType()) {
case INT32: case INT32:
case SINT32: case SINT32:
@ -1377,30 +1341,27 @@ public class JsonFormat {
case GROUP: case GROUP:
return builder.newBuilderForField(field).getDefaultInstanceForType(); return builder.newBuilderForField(field).getDefaultInstanceForType();
default: default:
throw new IllegalStateException( throw new IllegalStateException("Invalid field type: " + field.getType());
"Invalid field type: " + field.getType());
} }
} }
private void mergeRepeatedField(FieldDescriptor field, JsonElement json, private void mergeRepeatedField(
Message.Builder builder) throws InvalidProtocolBufferException { FieldDescriptor field, JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
if (!(json instanceof JsonArray)) { if (!(json instanceof JsonArray)) {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException("Expect an array but found: " + json);
"Expect an array but found: " + json);
} }
JsonArray array = (JsonArray) json; JsonArray array = (JsonArray) json;
for (int i = 0; i < array.size(); ++i) { for (int i = 0; i < array.size(); ++i) {
Object value = parseFieldValue(field, array.get(i), builder); Object value = parseFieldValue(field, array.get(i), builder);
if (value == null) { if (value == null) {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException("Repeated field elements cannot be null");
"Repeated field elements cannot be null");
} }
builder.addRepeatedField(field, value); builder.addRepeatedField(field, value);
} }
} }
private int parseInt32(JsonElement json) private int parseInt32(JsonElement json) throws InvalidProtocolBufferException {
throws InvalidProtocolBufferException {
try { try {
return Integer.parseInt(json.getAsString()); return Integer.parseInt(json.getAsString());
} catch (Exception e) { } catch (Exception e) {
@ -1417,8 +1378,7 @@ public class JsonFormat {
} }
} }
private long parseInt64(JsonElement json) private long parseInt64(JsonElement json) throws InvalidProtocolBufferException {
throws InvalidProtocolBufferException {
try { try {
return Long.parseLong(json.getAsString()); return Long.parseLong(json.getAsString());
} catch (Exception e) { } catch (Exception e) {
@ -1435,13 +1395,11 @@ public class JsonFormat {
} }
} }
private int parseUint32(JsonElement json) private int parseUint32(JsonElement json) throws InvalidProtocolBufferException {
throws InvalidProtocolBufferException {
try { try {
long result = Long.parseLong(json.getAsString()); long result = Long.parseLong(json.getAsString());
if (result < 0 || result > 0xFFFFFFFFL) { if (result < 0 || result > 0xFFFFFFFFL) {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException("Out of range uint32 value: " + json);
"Out of range uint32 value: " + json);
} }
return (int) result; return (int) result;
} catch (InvalidProtocolBufferException e) { } catch (InvalidProtocolBufferException e) {
@ -1462,35 +1420,28 @@ public class JsonFormat {
} catch (InvalidProtocolBufferException e) { } catch (InvalidProtocolBufferException e) {
throw e; throw e;
} catch (Exception e) { } catch (Exception e) {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException("Not an uint32 value: " + json);
"Not an uint32 value: " + json);
} }
} }
private static final BigInteger MAX_UINT64 = private static final BigInteger MAX_UINT64 = new BigInteger("FFFFFFFFFFFFFFFF", 16);
new BigInteger("FFFFFFFFFFFFFFFF", 16);
private long parseUint64(JsonElement json) private long parseUint64(JsonElement json) throws InvalidProtocolBufferException {
throws InvalidProtocolBufferException {
try { try {
BigDecimal decimalValue = new BigDecimal(json.getAsString()); BigDecimal decimalValue = new BigDecimal(json.getAsString());
BigInteger value = decimalValue.toBigIntegerExact(); BigInteger value = decimalValue.toBigIntegerExact();
if (value.compareTo(BigInteger.ZERO) < 0 if (value.compareTo(BigInteger.ZERO) < 0 || value.compareTo(MAX_UINT64) > 0) {
|| value.compareTo(MAX_UINT64) > 0) { throw new InvalidProtocolBufferException("Out of range uint64 value: " + json);
throw new InvalidProtocolBufferException(
"Out of range uint64 value: " + json);
} }
return value.longValue(); return value.longValue();
} catch (InvalidProtocolBufferException e) { } catch (InvalidProtocolBufferException e) {
throw e; throw e;
} catch (Exception e) { } catch (Exception e) {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException("Not an uint64 value: " + json);
"Not an uint64 value: " + json);
} }
} }
private boolean parseBool(JsonElement json) private boolean parseBool(JsonElement json) throws InvalidProtocolBufferException {
throws InvalidProtocolBufferException {
if (json.getAsString().equals("true")) { if (json.getAsString().equals("true")) {
return true; return true;
} }
@ -1502,8 +1453,7 @@ public class JsonFormat {
private static final double EPSILON = 1e-6; private static final double EPSILON = 1e-6;
private float parseFloat(JsonElement json) private float parseFloat(JsonElement json) throws InvalidProtocolBufferException {
throws InvalidProtocolBufferException {
if (json.getAsString().equals("NaN")) { if (json.getAsString().equals("NaN")) {
return Float.NaN; return Float.NaN;
} else if (json.getAsString().equals("Infinity")) { } else if (json.getAsString().equals("Infinity")) {
@ -1521,8 +1471,7 @@ public class JsonFormat {
// of tolerance when checking whether the float value is in range. // of tolerance when checking whether the float value is in range.
if (value > Float.MAX_VALUE * (1.0 + EPSILON) if (value > Float.MAX_VALUE * (1.0 + EPSILON)
|| value < -Float.MAX_VALUE * (1.0 + EPSILON)) { || value < -Float.MAX_VALUE * (1.0 + EPSILON)) {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException("Out of range float value: " + json);
"Out of range float value: " + json);
} }
return (float) value; return (float) value;
} catch (InvalidProtocolBufferException e) { } catch (InvalidProtocolBufferException e) {
@ -1532,18 +1481,16 @@ public class JsonFormat {
} }
} }
private static final BigDecimal MORE_THAN_ONE = new BigDecimal( private static final BigDecimal MORE_THAN_ONE = new BigDecimal(String.valueOf(1.0 + EPSILON));
String.valueOf(1.0 + EPSILON));
// When a float value is printed, the printed value might be a little // When a float value is printed, the printed value might be a little
// larger or smaller due to precision loss. Here we need to add a bit // larger or smaller due to precision loss. Here we need to add a bit
// of tolerance when checking whether the float value is in range. // of tolerance when checking whether the float value is in range.
private static final BigDecimal MAX_DOUBLE = new BigDecimal( private static final BigDecimal MAX_DOUBLE =
String.valueOf(Double.MAX_VALUE)).multiply(MORE_THAN_ONE); new BigDecimal(String.valueOf(Double.MAX_VALUE)).multiply(MORE_THAN_ONE);
private static final BigDecimal MIN_DOUBLE = new BigDecimal( private static final BigDecimal MIN_DOUBLE =
String.valueOf(-Double.MAX_VALUE)).multiply(MORE_THAN_ONE); new BigDecimal(String.valueOf(-Double.MAX_VALUE)).multiply(MORE_THAN_ONE);
private double parseDouble(JsonElement json) private double parseDouble(JsonElement json) throws InvalidProtocolBufferException {
throws InvalidProtocolBufferException {
if (json.getAsString().equals("NaN")) { if (json.getAsString().equals("NaN")) {
return Double.NaN; return Double.NaN;
} else if (json.getAsString().equals("Infinity")) { } else if (json.getAsString().equals("Infinity")) {
@ -1556,17 +1503,14 @@ public class JsonFormat {
// accepts all values. Here we parse the value into a BigDecimal and do // accepts all values. Here we parse the value into a BigDecimal and do
// explicit range check on it. // explicit range check on it.
BigDecimal value = new BigDecimal(json.getAsString()); BigDecimal value = new BigDecimal(json.getAsString());
if (value.compareTo(MAX_DOUBLE) > 0 if (value.compareTo(MAX_DOUBLE) > 0 || value.compareTo(MIN_DOUBLE) < 0) {
|| value.compareTo(MIN_DOUBLE) < 0) { throw new InvalidProtocolBufferException("Out of range double value: " + json);
throw new InvalidProtocolBufferException(
"Out of range double value: " + json);
} }
return value.doubleValue(); return value.doubleValue();
} catch (InvalidProtocolBufferException e) { } catch (InvalidProtocolBufferException e) {
throw e; throw e;
} catch (Exception e) { } catch (Exception e) {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException("Not an double value: " + json);
"Not an double value: " + json);
} }
} }
@ -1580,12 +1524,11 @@ public class JsonFormat {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException(
"Bytes field is not encoded in standard BASE64 with paddings: " + encoded); "Bytes field is not encoded in standard BASE64 with paddings: " + encoded);
} }
return ByteString.copyFrom( return ByteString.copyFrom(BaseEncoding.base64().decode(json.getAsString()));
BaseEncoding.base64().decode(json.getAsString()));
} }
private EnumValueDescriptor parseEnum(EnumDescriptor enumDescriptor, private EnumValueDescriptor parseEnum(EnumDescriptor enumDescriptor, JsonElement json)
JsonElement json) throws InvalidProtocolBufferException { throws InvalidProtocolBufferException {
String value = json.getAsString(); String value = json.getAsString();
EnumValueDescriptor result = enumDescriptor.findValueByName(value); EnumValueDescriptor result = enumDescriptor.findValueByName(value);
if (result == null) { if (result == null) {
@ -1605,24 +1548,25 @@ public class JsonFormat {
if (result == null) { if (result == null) {
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException(
"Invalid enum value: " + value + " for enum type: " "Invalid enum value: " + value + " for enum type: " + enumDescriptor.getFullName());
+ enumDescriptor.getFullName());
} }
} }
return result; return result;
} }
private Object parseFieldValue(FieldDescriptor field, JsonElement json, private Object parseFieldValue(FieldDescriptor field, JsonElement json, Message.Builder builder)
Message.Builder builder) throws InvalidProtocolBufferException { throws InvalidProtocolBufferException {
if (json instanceof JsonNull) { if (json instanceof JsonNull) {
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE
&& field.getMessageType().getFullName().equals( && field.getMessageType().getFullName().equals(Value.getDescriptor().getFullName())) {
Value.getDescriptor().getFullName())) {
// For every other type, "null" means absence, but for the special // For every other type, "null" means absence, but for the special
// Value message, it means the "null_value" field has been set. // Value message, it means the "null_value" field has been set.
Value value = Value.newBuilder().setNullValueValue(0).build(); Value value = Value.newBuilder().setNullValueValue(0).build();
return builder.newBuilderForField(field).mergeFrom( return builder.newBuilderForField(field).mergeFrom(value.toByteString()).build();
value.toByteString()).build(); } else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM
&& field.getEnumType().getFullName().equals(NullValue.getDescriptor().getFullName())) {
// If the type of the field is a NullValue, then the value should be explicitly set.
return field.getEnumType().findValueByNumber(0);
} }
return null; return null;
} }
@ -1670,8 +1614,7 @@ public class JsonFormat {
return subBuilder.build(); return subBuilder.build();
default: default:
throw new InvalidProtocolBufferException( throw new InvalidProtocolBufferException("Invalid field type: " + field.getType());
"Invalid field type: " + field.getType());
} }
} }
} }

@ -35,15 +35,14 @@ import com.google.protobuf.Timestamp;
import java.math.BigInteger; import java.math.BigInteger;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
/** /**
* Utilities to help create/manipulate Timestamp/Duration * Utilities to help create/manipulate Timestamp/Duration
*
* @deprecated Use {@link Durations} and {@link Timestamps} instead.
*/ */
public class TimeUtil { @Deprecated
public final class TimeUtil {
// Timestamp for "0001-01-01T00:00:00Z" // Timestamp for "0001-01-01T00:00:00Z"
public static final long TIMESTAMP_SECONDS_MIN = -62135596800L; public static final long TIMESTAMP_SECONDS_MIN = -62135596800L;
@ -53,28 +52,6 @@ public class TimeUtil {
public static final long DURATION_SECONDS_MAX = 315576000000L; public static final long DURATION_SECONDS_MAX = 315576000000L;
private static final long NANOS_PER_SECOND = 1000000000; private static final long NANOS_PER_SECOND = 1000000000;
private static final long NANOS_PER_MILLISECOND = 1000000;
private static final long NANOS_PER_MICROSECOND = 1000;
private static final long MILLIS_PER_SECOND = 1000;
private static final long MICROS_PER_SECOND = 1000000;
private static final ThreadLocal<SimpleDateFormat> timestampFormat =
new ThreadLocal<SimpleDateFormat>() {
protected SimpleDateFormat initialValue() {
return createTimestampFormat();
}
};
private static SimpleDateFormat createTimestampFormat() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
GregorianCalendar calendar =
new GregorianCalendar(TimeZone.getTimeZone("UTC"));
// We use Proleptic Gregorian Calendar (i.e., Gregorian calendar extends
// backwards to year one) for timestamp formating.
calendar.setGregorianChange(new Date(Long.MIN_VALUE));
sdf.setCalendar(calendar);
return sdf;
}
private TimeUtil() {} private TimeUtil() {}
@ -90,27 +67,11 @@ public class TimeUtil {
* @return The string representation of the given timestamp. * @return The string representation of the given timestamp.
* @throws IllegalArgumentException if the given timestamp is not in the * @throws IllegalArgumentException if the given timestamp is not in the
* valid range. * valid range.
* @deprecated Use {@link Timestamps#toString} instead.
*/ */
public static String toString(Timestamp timestamp) @Deprecated
throws IllegalArgumentException { public static String toString(Timestamp timestamp) {
StringBuilder result = new StringBuilder(); return Timestamps.toString(timestamp);
// Format the seconds part.
if (timestamp.getSeconds() < TIMESTAMP_SECONDS_MIN
|| timestamp.getSeconds() > TIMESTAMP_SECONDS_MAX) {
throw new IllegalArgumentException("Timestamp is out of range.");
}
Date date = new Date(timestamp.getSeconds() * MILLIS_PER_SECOND);
result.append(timestampFormat.get().format(date));
// Format the nanos part.
if (timestamp.getNanos() < 0 || timestamp.getNanos() >= NANOS_PER_SECOND) {
throw new IllegalArgumentException("Timestamp has invalid nanos value.");
}
if (timestamp.getNanos() != 0) {
result.append(".");
result.append(formatNanos(timestamp.getNanos()));
}
result.append("Z");
return result.toString();
} }
/** /**
@ -123,59 +84,11 @@ public class TimeUtil {
* *
* @return A Timestamp parsed from the string. * @return A Timestamp parsed from the string.
* @throws ParseException if parsing fails. * @throws ParseException if parsing fails.
* @deprecated Use {@link Timestamps#parse} instead.
*/ */
@Deprecated
public static Timestamp parseTimestamp(String value) throws ParseException { public static Timestamp parseTimestamp(String value) throws ParseException {
int dayOffset = value.indexOf('T'); return Timestamps.parse(value);
if (dayOffset == -1) {
throw new ParseException(
"Failed to parse timestamp: invalid timestamp \"" + value + "\"", 0);
}
int timezoneOffsetPosition = value.indexOf('Z', dayOffset);
if (timezoneOffsetPosition == -1) {
timezoneOffsetPosition = value.indexOf('+', dayOffset);
}
if (timezoneOffsetPosition == -1) {
timezoneOffsetPosition = value.indexOf('-', dayOffset);
}
if (timezoneOffsetPosition == -1) {
throw new ParseException(
"Failed to parse timestamp: missing valid timezone offset.", 0);
}
// Parse seconds and nanos.
String timeValue = value.substring(0, timezoneOffsetPosition);
String secondValue = timeValue;
String nanoValue = "";
int pointPosition = timeValue.indexOf('.');
if (pointPosition != -1) {
secondValue = timeValue.substring(0, pointPosition);
nanoValue = timeValue.substring(pointPosition + 1);
}
Date date = timestampFormat.get().parse(secondValue);
long seconds = date.getTime() / MILLIS_PER_SECOND;
int nanos = nanoValue.isEmpty() ? 0 : parseNanos(nanoValue);
// Parse timezone offsets.
if (value.charAt(timezoneOffsetPosition) == 'Z') {
if (value.length() != timezoneOffsetPosition + 1) {
throw new ParseException(
"Failed to parse timestamp: invalid trailing data \""
+ value.substring(timezoneOffsetPosition) + "\"", 0);
}
} else {
String offsetValue = value.substring(timezoneOffsetPosition + 1);
long offset = parseTimezoneOffset(offsetValue);
if (value.charAt(timezoneOffsetPosition) == '+') {
seconds -= offset;
} else {
seconds += offset;
}
}
try {
return normalizedTimestamp(seconds, nanos);
} catch (IllegalArgumentException e) {
throw new ParseException(
"Failed to parse timestmap: timestamp is out of range.", 0);
}
} }
/** /**
@ -188,33 +101,11 @@ public class TimeUtil {
* @return The string representation of the given duration. * @return The string representation of the given duration.
* @throws IllegalArgumentException if the given duration is not in the valid * @throws IllegalArgumentException if the given duration is not in the valid
* range. * range.
* @deprecated Use {@link Durations#toString} instead.
*/ */
public static String toString(Duration duration) @Deprecated
throws IllegalArgumentException { public static String toString(Duration duration) {
if (duration.getSeconds() < DURATION_SECONDS_MIN return Durations.toString(duration);
|| duration.getSeconds() > DURATION_SECONDS_MAX) {
throw new IllegalArgumentException("Duration is out of valid range.");
}
StringBuilder result = new StringBuilder();
long seconds = duration.getSeconds();
int nanos = duration.getNanos();
if (seconds < 0 || nanos < 0) {
if (seconds > 0 || nanos > 0) {
throw new IllegalArgumentException(
"Invalid duration: seconds value and nanos value must have the same"
+ "sign.");
}
result.append("-");
seconds = -seconds;
nanos = -nanos;
}
result.append(seconds);
if (nanos != 0) {
result.append(".");
result.append(formatNanos(nanos));
}
result.append("s");
return result.toString();
} }
/** /**
@ -222,54 +113,31 @@ public class TimeUtil {
* *
* @return A Duration parsed from the string. * @return A Duration parsed from the string.
* @throws ParseException if parsing fails. * @throws ParseException if parsing fails.
* @deprecated Use {@link Durations#parse} instead.
*/ */
@Deprecated
public static Duration parseDuration(String value) throws ParseException { public static Duration parseDuration(String value) throws ParseException {
// Must ended with "s". return Durations.parse(value);
if (value.isEmpty() || value.charAt(value.length() - 1) != 's') {
throw new ParseException("Invalid duration string: " + value, 0);
}
boolean negative = false;
if (value.charAt(0) == '-') {
negative = true;
value = value.substring(1);
}
String secondValue = value.substring(0, value.length() - 1);
String nanoValue = "";
int pointPosition = secondValue.indexOf('.');
if (pointPosition != -1) {
nanoValue = secondValue.substring(pointPosition + 1);
secondValue = secondValue.substring(0, pointPosition);
}
long seconds = Long.parseLong(secondValue);
int nanos = nanoValue.isEmpty() ? 0 : parseNanos(nanoValue);
if (seconds < 0) {
throw new ParseException("Invalid duration string: " + value, 0);
}
if (negative) {
seconds = -seconds;
nanos = -nanos;
}
try {
return normalizedDuration(seconds, nanos);
} catch (IllegalArgumentException e) {
throw new ParseException("Duration value is out of range.", 0);
}
} }
/** /**
* Create a Timestamp from the number of milliseconds elapsed from the epoch. * Create a Timestamp from the number of milliseconds elapsed from the epoch.
*
* @deprecated Use {@link Timestamps#fromMillis} instead.
*/ */
@Deprecated
public static Timestamp createTimestampFromMillis(long milliseconds) { public static Timestamp createTimestampFromMillis(long milliseconds) {
return normalizedTimestamp(milliseconds / MILLIS_PER_SECOND, return Timestamps.fromMillis(milliseconds);
(int) (milliseconds % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND));
} }
/** /**
* Create a Duration from the number of milliseconds. * Create a Duration from the number of milliseconds.
*
* @deprecated Use {@link Durations#fromMillis} instead.
*/ */
@Deprecated
public static Duration createDurationFromMillis(long milliseconds) { public static Duration createDurationFromMillis(long milliseconds) {
return normalizedDuration(milliseconds / MILLIS_PER_SECOND, return Durations.fromMillis(milliseconds);
(int) (milliseconds % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND));
} }
/** /**
@ -278,36 +146,44 @@ public class TimeUtil {
* <p>The result will be rounded down to the nearest millisecond. E.g., if the * <p>The result will be rounded down to the nearest millisecond. E.g., if the
* timestamp represents "1969-12-31T23:59:59.999999999Z", it will be rounded * timestamp represents "1969-12-31T23:59:59.999999999Z", it will be rounded
* to -1 millisecond. * to -1 millisecond.
*
* @deprecated Use {@link Timestamps#toMillis} instead.
*/ */
@Deprecated
public static long toMillis(Timestamp timestamp) { public static long toMillis(Timestamp timestamp) {
return timestamp.getSeconds() * MILLIS_PER_SECOND + timestamp.getNanos() return Timestamps.toMillis(timestamp);
/ NANOS_PER_MILLISECOND;
} }
/** /**
* Convert a Duration to the number of milliseconds.The result will be * Convert a Duration to the number of milliseconds.The result will be
* rounded towards 0 to the nearest millisecond. E.g., if the duration * rounded towards 0 to the nearest millisecond. E.g., if the duration
* represents -1 nanosecond, it will be rounded to 0. * represents -1 nanosecond, it will be rounded to 0.
*
* @deprecated Use {@link Durations#toMillis} instead.
*/ */
@Deprecated
public static long toMillis(Duration duration) { public static long toMillis(Duration duration) {
return duration.getSeconds() * MILLIS_PER_SECOND + duration.getNanos() return Durations.toMillis(duration);
/ NANOS_PER_MILLISECOND;
} }
/** /**
* Create a Timestamp from the number of microseconds elapsed from the epoch. * Create a Timestamp from the number of microseconds elapsed from the epoch.
*
* @deprecated Use {@link Timestamps#fromMicros} instead.
*/ */
@Deprecated
public static Timestamp createTimestampFromMicros(long microseconds) { public static Timestamp createTimestampFromMicros(long microseconds) {
return normalizedTimestamp(microseconds / MICROS_PER_SECOND, return Timestamps.fromMicros(microseconds);
(int) (microseconds % MICROS_PER_SECOND * NANOS_PER_MICROSECOND));
} }
/** /**
* Create a Duration from the number of microseconds. * Create a Duration from the number of microseconds.
*
* @deprecated Use {@link Durations#fromMicros} instead.
*/ */
@Deprecated
public static Duration createDurationFromMicros(long microseconds) { public static Duration createDurationFromMicros(long microseconds) {
return normalizedDuration(microseconds / MICROS_PER_SECOND, return Durations.fromMicros(microseconds);
(int) (microseconds % MICROS_PER_SECOND * NANOS_PER_MICROSECOND));
} }
/** /**
@ -316,111 +192,141 @@ public class TimeUtil {
* <p>The result will be rounded down to the nearest microsecond. E.g., if the * <p>The result will be rounded down to the nearest microsecond. E.g., if the
* timestamp represents "1969-12-31T23:59:59.999999999Z", it will be rounded * timestamp represents "1969-12-31T23:59:59.999999999Z", it will be rounded
* to -1 millisecond. * to -1 millisecond.
*
* @deprecated Use {@link Timestamps#toMicros} instead.
*/ */
@Deprecated
public static long toMicros(Timestamp timestamp) { public static long toMicros(Timestamp timestamp) {
return timestamp.getSeconds() * MICROS_PER_SECOND + timestamp.getNanos() return Timestamps.toMicros(timestamp);
/ NANOS_PER_MICROSECOND;
} }
/** /**
* Convert a Duration to the number of microseconds.The result will be * Convert a Duration to the number of microseconds.The result will be
* rounded towards 0 to the nearest microseconds. E.g., if the duration * rounded towards 0 to the nearest microseconds. E.g., if the duration
* represents -1 nanosecond, it will be rounded to 0. * represents -1 nanosecond, it will be rounded to 0.
*
* @deprecated Use {@link Durations#toMicros} instead.
*/ */
@Deprecated
public static long toMicros(Duration duration) { public static long toMicros(Duration duration) {
return duration.getSeconds() * MICROS_PER_SECOND + duration.getNanos() return Durations.toMicros(duration);
/ NANOS_PER_MICROSECOND;
} }
/** /**
* Create a Timestamp from the number of nanoseconds elapsed from the epoch. * Create a Timestamp from the number of nanoseconds elapsed from the epoch.
*
* @deprecated Use {@link Timestamps#fromNanos} instead.
*/ */
@Deprecated
public static Timestamp createTimestampFromNanos(long nanoseconds) { public static Timestamp createTimestampFromNanos(long nanoseconds) {
return normalizedTimestamp(nanoseconds / NANOS_PER_SECOND, return Timestamps.fromNanos(nanoseconds);
(int) (nanoseconds % NANOS_PER_SECOND));
} }
/** /**
* Create a Duration from the number of nanoseconds. * Create a Duration from the number of nanoseconds.
*
* @deprecated Use {@link Durations#fromNanos} instead.
*/ */
@Deprecated
public static Duration createDurationFromNanos(long nanoseconds) { public static Duration createDurationFromNanos(long nanoseconds) {
return normalizedDuration(nanoseconds / NANOS_PER_SECOND, return Durations.fromNanos(nanoseconds);
(int) (nanoseconds % NANOS_PER_SECOND));
} }
/** /**
* Convert a Timestamp to the number of nanoseconds elapsed from the epoch. * Convert a Timestamp to the number of nanoseconds elapsed from the epoch.
*
* @deprecated Use {@link Timestamps#toNanos} instead.
*/ */
@Deprecated
public static long toNanos(Timestamp timestamp) { public static long toNanos(Timestamp timestamp) {
return timestamp.getSeconds() * NANOS_PER_SECOND + timestamp.getNanos(); return Timestamps.toNanos(timestamp);
} }
/** /**
* Convert a Duration to the number of nanoseconds. * Convert a Duration to the number of nanoseconds.
*
* @deprecated Use {@link Durations#toNanos} instead.
*/ */
@Deprecated
public static long toNanos(Duration duration) { public static long toNanos(Duration duration) {
return duration.getSeconds() * NANOS_PER_SECOND + duration.getNanos(); return Durations.toNanos(duration);
} }
/** /**
* Get the current time. * Get the current time.
*
* @deprecated Use {@code Timestamps.fromMillis(System.currentTimeMillis())} instead.
*/ */
@Deprecated
public static Timestamp getCurrentTime() { public static Timestamp getCurrentTime() {
return createTimestampFromMillis(System.currentTimeMillis()); return Timestamps.fromMillis(System.currentTimeMillis());
} }
/** /**
* Get the epoch. * Get the epoch.
*
* @deprecated Use {@code Timestamps.fromMillis(0)} instead.
*/ */
@Deprecated
public static Timestamp getEpoch() { public static Timestamp getEpoch() {
return Timestamp.getDefaultInstance(); return Timestamp.getDefaultInstance();
} }
/** /**
* Calculate the difference between two timestamps. * Calculate the difference between two timestamps.
*
* @deprecated Use {@link Timestamps#between} instead.
*/ */
@Deprecated
public static Duration distance(Timestamp from, Timestamp to) { public static Duration distance(Timestamp from, Timestamp to) {
return normalizedDuration(to.getSeconds() - from.getSeconds(), return Timestamps.between(from, to);
to.getNanos() - from.getNanos());
} }
/** /**
* Add a duration to a timestamp. * Add a duration to a timestamp.
*
* @deprecated Use {@link Timestamps#add} instead.
*/ */
@Deprecated
public static Timestamp add(Timestamp start, Duration length) { public static Timestamp add(Timestamp start, Duration length) {
return normalizedTimestamp(start.getSeconds() + length.getSeconds(), return Timestamps.add(start, length);
start.getNanos() + length.getNanos());
} }
/** /**
* Subtract a duration from a timestamp. * Subtract a duration from a timestamp.
*
* @deprecated Use {@link Timestamps#subtract} instead.
*/ */
@Deprecated
public static Timestamp subtract(Timestamp start, Duration length) { public static Timestamp subtract(Timestamp start, Duration length) {
return normalizedTimestamp(start.getSeconds() - length.getSeconds(), return Timestamps.subtract(start, length);
start.getNanos() - length.getNanos());
} }
/** /**
* Add two durations. * Add two durations.
*
* @deprecated Use {@link Durations#add} instead.
*/ */
@Deprecated
public static Duration add(Duration d1, Duration d2) { public static Duration add(Duration d1, Duration d2) {
return normalizedDuration(d1.getSeconds() + d2.getSeconds(), return Durations.add(d1, d2);
d1.getNanos() + d2.getNanos());
} }
/** /**
* Subtract a duration from another. * Subtract a duration from another.
*
* @deprecated Use {@link Durations#subtract} instead.
*/ */
@Deprecated
public static Duration subtract(Duration d1, Duration d2) { public static Duration subtract(Duration d1, Duration d2) {
return normalizedDuration(d1.getSeconds() - d2.getSeconds(), return Durations.subtract(d1, d2);
d1.getNanos() - d2.getNanos());
} }
// Multiplications and divisions. // Multiplications and divisions.
// TODO(kak): Delete this.
public static Duration multiply(Duration duration, double times) { public static Duration multiply(Duration duration, double times) {
double result = duration.getSeconds() * times + duration.getNanos() * times double result = duration.getSeconds() * times + duration.getNanos() * times / 1000000000.0;
/ 1000000000.0;
if (result < Long.MIN_VALUE || result > Long.MAX_VALUE) { if (result < Long.MIN_VALUE || result > Long.MAX_VALUE) {
throw new IllegalArgumentException("Result is out of valid range."); throw new IllegalArgumentException("Result is out of valid range.");
} }
@ -429,27 +335,29 @@ public class TimeUtil {
return normalizedDuration(seconds, nanos); return normalizedDuration(seconds, nanos);
} }
// TODO(kak): Delete this.
public static Duration divide(Duration duration, double value) { public static Duration divide(Duration duration, double value) {
return multiply(duration, 1.0 / value); return multiply(duration, 1.0 / value);
} }
// TODO(kak): Delete this.
public static Duration multiply(Duration duration, long times) { public static Duration multiply(Duration duration, long times) {
return createDurationFromBigInteger( return createDurationFromBigInteger(toBigInteger(duration).multiply(toBigInteger(times)));
toBigInteger(duration).multiply(toBigInteger(times)));
} }
// TODO(kak): Delete this.
public static Duration divide(Duration duration, long times) { public static Duration divide(Duration duration, long times) {
return createDurationFromBigInteger( return createDurationFromBigInteger(toBigInteger(duration).divide(toBigInteger(times)));
toBigInteger(duration).divide(toBigInteger(times)));
} }
// TODO(kak): Delete this.
public static long divide(Duration d1, Duration d2) { public static long divide(Duration d1, Duration d2) {
return toBigInteger(d1).divide(toBigInteger(d2)).longValue(); return toBigInteger(d1).divide(toBigInteger(d2)).longValue();
} }
// TODO(kak): Delete this.
public static Duration remainder(Duration d1, Duration d2) { public static Duration remainder(Duration d1, Duration d2) {
return createDurationFromBigInteger( return createDurationFromBigInteger(toBigInteger(d1).remainder(toBigInteger(d2)));
toBigInteger(d1).remainder(toBigInteger(d2)));
} }
private static final BigInteger NANOS_PER_SECOND_BIG_INTEGER = private static final BigInteger NANOS_PER_SECOND_BIG_INTEGER =
@ -466,12 +374,9 @@ public class TimeUtil {
} }
private static Duration createDurationFromBigInteger(BigInteger value) { private static Duration createDurationFromBigInteger(BigInteger value) {
long seconds = value.divide( long seconds = value.divide(new BigInteger(String.valueOf(NANOS_PER_SECOND))).longValue();
new BigInteger(String.valueOf(NANOS_PER_SECOND))).longValue(); int nanos = value.remainder(new BigInteger(String.valueOf(NANOS_PER_SECOND))).intValue();
int nanos = value.remainder(
new BigInteger(String.valueOf(NANOS_PER_SECOND))).intValue();
return normalizedDuration(seconds, nanos); return normalizedDuration(seconds, nanos);
} }
private static Duration normalizedDuration(long seconds, int nanos) { private static Duration normalizedDuration(long seconds, int nanos) {
@ -492,58 +397,4 @@ public class TimeUtil {
} }
return Duration.newBuilder().setSeconds(seconds).setNanos(nanos).build(); return Duration.newBuilder().setSeconds(seconds).setNanos(nanos).build();
} }
private static Timestamp normalizedTimestamp(long seconds, int nanos) {
if (nanos <= -NANOS_PER_SECOND || nanos >= NANOS_PER_SECOND) {
seconds += nanos / NANOS_PER_SECOND;
nanos %= NANOS_PER_SECOND;
}
if (nanos < 0) {
nanos += NANOS_PER_SECOND;
seconds -= 1;
}
if (seconds < TIMESTAMP_SECONDS_MIN || seconds > TIMESTAMP_SECONDS_MAX) {
throw new IllegalArgumentException("Timestamp is out of valid range.");
}
return Timestamp.newBuilder().setSeconds(seconds).setNanos(nanos).build();
}
/**
* Format the nano part of a timestamp or a duration.
*/
private static String formatNanos(int nanos) {
assert nanos >= 1 && nanos <= 999999999;
// Determine whether to use 3, 6, or 9 digits for the nano part.
if (nanos % NANOS_PER_MILLISECOND == 0) {
return String.format("%1$03d", nanos / NANOS_PER_MILLISECOND);
} else if (nanos % NANOS_PER_MICROSECOND == 0) {
return String.format("%1$06d", nanos / NANOS_PER_MICROSECOND);
} else {
return String.format("%1$09d", nanos);
}
}
private static int parseNanos(String value) throws ParseException {
int result = 0;
for (int i = 0; i < 9; ++i) {
result = result * 10;
if (i < value.length()) {
if (value.charAt(i) < '0' || value.charAt(i) > '9') {
throw new ParseException("Invalid nanosecnds.", 0);
}
result += value.charAt(i) - '0';
}
}
return result;
}
private static long parseTimezoneOffset(String value) throws ParseException {
int pos = value.indexOf(':');
if (pos == -1) {
throw new ParseException("Invalid offset value: " + value, 0);
}
String hours = value.substring(0, pos);
String minutes = value.substring(pos + 1);
return (Long.parseLong(hours) * 60 + Long.parseLong(minutes)) * 60;
}
} }

@ -0,0 +1,349 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf.util;
import com.google.protobuf.Duration;
import com.google.protobuf.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
/**
* Utilities to help create/manipulate {@code protobuf/timestamp.proto}.
*/
public final class Timestamps {
// Timestamp for "0001-01-01T00:00:00Z"
static final long TIMESTAMP_SECONDS_MIN = -62135596800L;
// Timestamp for "9999-12-31T23:59:59Z"
static final long TIMESTAMP_SECONDS_MAX = 253402300799L;
static final long NANOS_PER_SECOND = 1000000000;
static final long NANOS_PER_MILLISECOND = 1000000;
static final long NANOS_PER_MICROSECOND = 1000;
static final long MILLIS_PER_SECOND = 1000;
static final long MICROS_PER_SECOND = 1000000;
// TODO(kak): Do we want to expose Timestamp constants for MAX/MIN?
private static final ThreadLocal<SimpleDateFormat> timestampFormat =
new ThreadLocal<SimpleDateFormat>() {
protected SimpleDateFormat initialValue() {
return createTimestampFormat();
}
};
private static SimpleDateFormat createTimestampFormat() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
// We use Proleptic Gregorian Calendar (i.e., Gregorian calendar extends
// backwards to year one) for timestamp formating.
calendar.setGregorianChange(new Date(Long.MIN_VALUE));
sdf.setCalendar(calendar);
return sdf;
}
private Timestamps() {}
/**
* Returns true if the given {@link Timestamp} is valid. The {@code seconds} value must be in the
* range [-62,135,596,800, +253,402,300,799] (i.e., between 0001-01-01T00:00:00Z and
* 9999-12-31T23:59:59Z). The {@code nanos} value must be in the range [0, +999,999,999].
*
* <p>Note: Negative second values with fractions must still have non-negative nanos value that
* counts forward in time.
*/
public static boolean isValid(Timestamp timestamp) {
return isValid(timestamp.getSeconds(), timestamp.getNanos());
}
/**
* Returns true if the given number of seconds and nanos is a valid {@link Timestamp}. The
* {@code seconds} value must be in the range [-62,135,596,800, +253,402,300,799] (i.e., between
* 0001-01-01T00:00:00Z and 9999-12-31T23:59:59Z). The {@code nanos} value must be in the range
* [0, +999,999,999].
*
* <p>Note: Negative second values with fractions must still have non-negative nanos value that
* counts forward in time.
*/
public static boolean isValid(long seconds, long nanos) {
if (seconds < TIMESTAMP_SECONDS_MIN || seconds > TIMESTAMP_SECONDS_MAX) {
return false;
}
if (nanos < 0 || nanos >= NANOS_PER_SECOND) {
return false;
}
return true;
}
/**
* Throws an {@link IllegalArgumentException} if the given seconds/nanos are not
* a valid {@link Timestamp}.
*/
private static void checkValid(long seconds, int nanos) {
if (!isValid(seconds, nanos)) {
throw new IllegalArgumentException(String.format(
"Timestamp is not valid. See proto definition for valid values. "
+ "Seconds (%s) must be in range [-62,135,596,800, +253,402,300,799]."
+ "Nanos (%s) must be in range [0, +999,999,999].",
seconds, nanos));
}
}
/**
* Convert Timestamp to RFC 3339 date string format. The output will always
* be Z-normalized and uses 3, 6 or 9 fractional digits as required to
* represent the exact value. Note that Timestamp can only represent time
* from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. See
* https://www.ietf.org/rfc/rfc3339.txt
*
* <p>Example of generated format: "1972-01-01T10:00:20.021Z"
*
* @return The string representation of the given timestamp.
* @throws IllegalArgumentException if the given timestamp is not in the
* valid range.
*/
public static String toString(Timestamp timestamp) {
long seconds = timestamp.getSeconds();
int nanos = timestamp.getNanos();
checkValid(seconds, nanos);
StringBuilder result = new StringBuilder();
// Format the seconds part.
Date date = new Date(seconds * MILLIS_PER_SECOND);
result.append(timestampFormat.get().format(date));
// Format the nanos part.
if (nanos != 0) {
result.append(".");
result.append(formatNanos(nanos));
}
result.append("Z");
return result.toString();
}
/**
* Parse from RFC 3339 date string to Timestamp. This method accepts all
* outputs of {@link #toString(Timestamp)} and it also accepts any fractional
* digits (or none) and any offset as long as they fit into nano-seconds
* precision.
*
* <p>Example of accepted format: "1972-01-01T10:00:20.021-05:00"
*
* @return A Timestamp parsed from the string.
* @throws ParseException if parsing fails.
*/
public static Timestamp parse(String value) throws ParseException {
int dayOffset = value.indexOf('T');
if (dayOffset == -1) {
throw new ParseException("Failed to parse timestamp: invalid timestamp \"" + value + "\"", 0);
}
int timezoneOffsetPosition = value.indexOf('Z', dayOffset);
if (timezoneOffsetPosition == -1) {
timezoneOffsetPosition = value.indexOf('+', dayOffset);
}
if (timezoneOffsetPosition == -1) {
timezoneOffsetPosition = value.indexOf('-', dayOffset);
}
if (timezoneOffsetPosition == -1) {
throw new ParseException("Failed to parse timestamp: missing valid timezone offset.", 0);
}
// Parse seconds and nanos.
String timeValue = value.substring(0, timezoneOffsetPosition);
String secondValue = timeValue;
String nanoValue = "";
int pointPosition = timeValue.indexOf('.');
if (pointPosition != -1) {
secondValue = timeValue.substring(0, pointPosition);
nanoValue = timeValue.substring(pointPosition + 1);
}
Date date = timestampFormat.get().parse(secondValue);
long seconds = date.getTime() / MILLIS_PER_SECOND;
int nanos = nanoValue.isEmpty() ? 0 : parseNanos(nanoValue);
// Parse timezone offsets.
if (value.charAt(timezoneOffsetPosition) == 'Z') {
if (value.length() != timezoneOffsetPosition + 1) {
throw new ParseException(
"Failed to parse timestamp: invalid trailing data \""
+ value.substring(timezoneOffsetPosition)
+ "\"",
0);
}
} else {
String offsetValue = value.substring(timezoneOffsetPosition + 1);
long offset = parseTimezoneOffset(offsetValue);
if (value.charAt(timezoneOffsetPosition) == '+') {
seconds -= offset;
} else {
seconds += offset;
}
}
try {
return normalizedTimestamp(seconds, nanos);
} catch (IllegalArgumentException e) {
throw new ParseException("Failed to parse timestmap: timestamp is out of range.", 0);
}
}
/**
* Create a Timestamp from the number of milliseconds elapsed from the epoch.
*/
public static Timestamp fromMillis(long milliseconds) {
return normalizedTimestamp(
milliseconds / MILLIS_PER_SECOND,
(int) (milliseconds % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND));
}
/**
* Convert a Timestamp to the number of milliseconds elapsed from the epoch.
*
* <p>The result will be rounded down to the nearest millisecond. E.g., if the
* timestamp represents "1969-12-31T23:59:59.999999999Z", it will be rounded
* to -1 millisecond.
*/
public static long toMillis(Timestamp timestamp) {
return timestamp.getSeconds() * MILLIS_PER_SECOND
+ timestamp.getNanos() / NANOS_PER_MILLISECOND;
}
/**
* Create a Timestamp from the number of microseconds elapsed from the epoch.
*/
public static Timestamp fromMicros(long microseconds) {
return normalizedTimestamp(
microseconds / MICROS_PER_SECOND,
(int) (microseconds % MICROS_PER_SECOND * NANOS_PER_MICROSECOND));
}
/**
* Convert a Timestamp to the number of microseconds elapsed from the epoch.
*
* <p>The result will be rounded down to the nearest microsecond. E.g., if the
* timestamp represents "1969-12-31T23:59:59.999999999Z", it will be rounded
* to -1 millisecond.
*/
public static long toMicros(Timestamp timestamp) {
return timestamp.getSeconds() * MICROS_PER_SECOND
+ timestamp.getNanos() / NANOS_PER_MICROSECOND;
}
/**
* Create a Timestamp from the number of nanoseconds elapsed from the epoch.
*/
public static Timestamp fromNanos(long nanoseconds) {
return normalizedTimestamp(
nanoseconds / NANOS_PER_SECOND, (int) (nanoseconds % NANOS_PER_SECOND));
}
/**
* Convert a Timestamp to the number of nanoseconds elapsed from the epoch.
*/
public static long toNanos(Timestamp timestamp) {
return timestamp.getSeconds() * NANOS_PER_SECOND + timestamp.getNanos();
}
/**
* Calculate the difference between two timestamps.
*/
public static Duration between(Timestamp from, Timestamp to) {
return Durations.normalizedDuration(
to.getSeconds() - from.getSeconds(), to.getNanos() - from.getNanos());
}
/**
* Add a duration to a timestamp.
*/
public static Timestamp add(Timestamp start, Duration length) {
return normalizedTimestamp(
start.getSeconds() + length.getSeconds(), start.getNanos() + length.getNanos());
}
/**
* Subtract a duration from a timestamp.
*/
public static Timestamp subtract(Timestamp start, Duration length) {
return normalizedTimestamp(
start.getSeconds() - length.getSeconds(), start.getNanos() - length.getNanos());
}
private static Timestamp normalizedTimestamp(long seconds, int nanos) {
if (nanos <= -NANOS_PER_SECOND || nanos >= NANOS_PER_SECOND) {
seconds += nanos / NANOS_PER_SECOND;
nanos %= NANOS_PER_SECOND;
}
if (nanos < 0) {
nanos += NANOS_PER_SECOND;
seconds -= 1;
}
checkValid(seconds, nanos);
return Timestamp.newBuilder().setSeconds(seconds).setNanos(nanos).build();
}
private static long parseTimezoneOffset(String value) throws ParseException {
int pos = value.indexOf(':');
if (pos == -1) {
throw new ParseException("Invalid offset value: " + value, 0);
}
String hours = value.substring(0, pos);
String minutes = value.substring(pos + 1);
return (Long.parseLong(hours) * 60 + Long.parseLong(minutes)) * 60;
}
static int parseNanos(String value) throws ParseException {
int result = 0;
for (int i = 0; i < 9; ++i) {
result = result * 10;
if (i < value.length()) {
if (value.charAt(i) < '0' || value.charAt(i) > '9') {
throw new ParseException("Invalid nanosecnds.", 0);
}
result += value.charAt(i) - '0';
}
}
return result;
}
/**
* Format the nano part of a timestamp or a duration.
*/
static String formatNanos(int nanos) {
assert nanos >= 1 && nanos <= 999999999;
// Determine whether to use 3, 6, or 9 digits for the nano part.
if (nanos % NANOS_PER_MILLISECOND == 0) {
return String.format("%1$03d", nanos / NANOS_PER_MILLISECOND);
} else if (nanos % NANOS_PER_MICROSECOND == 0) {
return String.format("%1$06d", nanos / NANOS_PER_MICROSECOND);
} else {
return String.format("%1$09d", nanos);
}
}
}

@ -41,40 +41,37 @@ public class FieldMaskUtilTest extends TestCase {
public void testIsValid() throws Exception { public void testIsValid() throws Exception {
assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload")); assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload"));
assertFalse(FieldMaskUtil.isValid(NestedTestAllTypes.class, "nonexist")); assertFalse(FieldMaskUtil.isValid(NestedTestAllTypes.class, "nonexist"));
assertTrue(FieldMaskUtil.isValid( assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.optional_int32"));
NestedTestAllTypes.class, "payload.optional_int32")); assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.repeated_int32"));
assertTrue(FieldMaskUtil.isValid( assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.optional_nested_message"));
NestedTestAllTypes.class, "payload.repeated_int32")); assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.repeated_nested_message"));
assertTrue(FieldMaskUtil.isValid( assertFalse(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.nonexist"));
NestedTestAllTypes.class, "payload.optional_nested_message"));
assertTrue(FieldMaskUtil.isValid( assertTrue(
NestedTestAllTypes.class, "payload.repeated_nested_message")); FieldMaskUtil.isValid(NestedTestAllTypes.class, FieldMaskUtil.fromString("payload")));
assertFalse(FieldMaskUtil.isValid( assertFalse(
NestedTestAllTypes.class, "payload.nonexist")); FieldMaskUtil.isValid(NestedTestAllTypes.class, FieldMaskUtil.fromString("nonexist")));
assertFalse(
assertTrue(FieldMaskUtil.isValid( FieldMaskUtil.isValid(
NestedTestAllTypes.class, FieldMaskUtil.fromString("payload")));
assertFalse(FieldMaskUtil.isValid(
NestedTestAllTypes.class, FieldMaskUtil.fromString("nonexist")));
assertFalse(FieldMaskUtil.isValid(
NestedTestAllTypes.class, FieldMaskUtil.fromString("payload,nonexist"))); NestedTestAllTypes.class, FieldMaskUtil.fromString("payload,nonexist")));
assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.getDescriptor(), "payload")); assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.getDescriptor(), "payload"));
assertFalse(FieldMaskUtil.isValid(NestedTestAllTypes.getDescriptor(), "nonexist")); assertFalse(FieldMaskUtil.isValid(NestedTestAllTypes.getDescriptor(), "nonexist"));
assertTrue(FieldMaskUtil.isValid( assertTrue(
FieldMaskUtil.isValid(
NestedTestAllTypes.getDescriptor(), FieldMaskUtil.fromString("payload"))); NestedTestAllTypes.getDescriptor(), FieldMaskUtil.fromString("payload")));
assertFalse(FieldMaskUtil.isValid( assertFalse(
FieldMaskUtil.isValid(
NestedTestAllTypes.getDescriptor(), FieldMaskUtil.fromString("nonexist"))); NestedTestAllTypes.getDescriptor(), FieldMaskUtil.fromString("nonexist")));
assertTrue(FieldMaskUtil.isValid( assertTrue(
NestedTestAllTypes.class, "payload.optional_nested_message.bb")); FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.optional_nested_message.bb"));
// Repeated fields cannot have sub-paths. // Repeated fields cannot have sub-paths.
assertFalse(FieldMaskUtil.isValid( assertFalse(
NestedTestAllTypes.class, "payload.repeated_nested_message.bb")); FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.repeated_nested_message.bb"));
// Non-message fields cannot have sub-paths. // Non-message fields cannot have sub-paths.
assertFalse(FieldMaskUtil.isValid( assertFalse(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.optional_int32.bb"));
NestedTestAllTypes.class, "payload.optional_int32.bb"));
} }
public void testToString() throws Exception { public void testToString() throws Exception {
@ -85,8 +82,14 @@ public class FieldMaskUtilTest extends TestCase {
assertEquals("foo,bar", FieldMaskUtil.toString(mask)); assertEquals("foo,bar", FieldMaskUtil.toString(mask));
// Empty field paths are ignored. // Empty field paths are ignored.
mask = FieldMask.newBuilder().addPaths("").addPaths("foo").addPaths(""). mask =
addPaths("bar").addPaths("").build(); FieldMask.newBuilder()
.addPaths("")
.addPaths("foo")
.addPaths("")
.addPaths("bar")
.addPaths("")
.build();
assertEquals("foo,bar", FieldMaskUtil.toString(mask)); assertEquals("foo,bar", FieldMaskUtil.toString(mask));
} }
@ -111,8 +114,7 @@ public class FieldMaskUtilTest extends TestCase {
mask = FieldMaskUtil.fromString(NestedTestAllTypes.class, ",payload"); mask = FieldMaskUtil.fromString(NestedTestAllTypes.class, ",payload");
try { try {
mask = FieldMaskUtil.fromString( mask = FieldMaskUtil.fromString(NestedTestAllTypes.class, "payload,nonexist");
NestedTestAllTypes.class, "payload,nonexist");
fail("Exception is expected."); fail("Exception is expected.");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
// Expected. // Expected.
@ -144,6 +146,32 @@ public class FieldMaskUtilTest extends TestCase {
} }
} }
public void testToJsonString() throws Exception {
FieldMask mask = FieldMask.getDefaultInstance();
assertEquals("", FieldMaskUtil.toJsonString(mask));
mask = FieldMask.newBuilder().addPaths("foo").build();
assertEquals("foo", FieldMaskUtil.toJsonString(mask));
mask = FieldMask.newBuilder().addPaths("foo.bar_baz").addPaths("").build();
assertEquals("foo.barBaz", FieldMaskUtil.toJsonString(mask));
mask = FieldMask.newBuilder().addPaths("foo").addPaths("bar_baz").build();
assertEquals("foo,barBaz", FieldMaskUtil.toJsonString(mask));
}
public void testFromJsonString() throws Exception {
FieldMask mask = FieldMaskUtil.fromJsonString("");
assertEquals(0, mask.getPathsCount());
mask = FieldMaskUtil.fromJsonString("foo");
assertEquals(1, mask.getPathsCount());
assertEquals("foo", mask.getPaths(0));
mask = FieldMaskUtil.fromJsonString("foo.barBaz");
assertEquals(1, mask.getPathsCount());
assertEquals("foo.bar_baz", mask.getPaths(0));
mask = FieldMaskUtil.fromJsonString("foo,barBaz");
assertEquals(2, mask.getPathsCount());
assertEquals("foo", mask.getPaths(0));
assertEquals("bar_baz", mask.getPaths(1));
}
public void testUnion() throws Exception { public void testUnion() throws Exception {
// Only test a simple case here and expect // Only test a simple case here and expect
// {@link FieldMaskTreeTest#testAddFieldPath} to cover all scenarios. // {@link FieldMaskTreeTest#testAddFieldPath} to cover all scenarios.
@ -174,7 +202,8 @@ public class FieldMaskUtilTest extends TestCase {
public void testMerge() throws Exception { public void testMerge() throws Exception {
// Only test a simple case here and expect // Only test a simple case here and expect
// {@link FieldMaskTreeTest#testMerge} to cover all scenarios. // {@link FieldMaskTreeTest#testMerge} to cover all scenarios.
NestedTestAllTypes source = NestedTestAllTypes.newBuilder() NestedTestAllTypes source =
NestedTestAllTypes.newBuilder()
.setPayload(TestAllTypes.newBuilder().setOptionalInt32(1234)) .setPayload(TestAllTypes.newBuilder().setOptionalInt32(1234))
.build(); .build();
NestedTestAllTypes.Builder builder = NestedTestAllTypes.newBuilder(); NestedTestAllTypes.Builder builder = NestedTestAllTypes.newBuilder();

@ -41,6 +41,7 @@ import com.google.protobuf.Int64Value;
import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.ListValue; import com.google.protobuf.ListValue;
import com.google.protobuf.Message; import com.google.protobuf.Message;
import com.google.protobuf.NullValue;
import com.google.protobuf.StringValue; import com.google.protobuf.StringValue;
import com.google.protobuf.Struct; import com.google.protobuf.Struct;
import com.google.protobuf.UInt32Value; import com.google.protobuf.UInt32Value;
@ -198,7 +199,8 @@ public class JsonFormatTest extends TestCase {
} }
public void testUnknownEnumValues() throws Exception { public void testUnknownEnumValues() throws Exception {
TestAllTypes message = TestAllTypes.newBuilder() TestAllTypes message =
TestAllTypes.newBuilder()
.setOptionalNestedEnumValue(12345) .setOptionalNestedEnumValue(12345)
.addRepeatedNestedEnumValue(12345) .addRepeatedNestedEnumValue(12345)
.addRepeatedNestedEnumValue(0) .addRepeatedNestedEnumValue(0)
@ -207,11 +209,12 @@ public class JsonFormatTest extends TestCase {
"{\n" "{\n"
+ " \"optionalNestedEnum\": 12345,\n" + " \"optionalNestedEnum\": 12345,\n"
+ " \"repeatedNestedEnum\": [12345, \"FOO\"]\n" + " \"repeatedNestedEnum\": [12345, \"FOO\"]\n"
+ "}", toJsonString(message)); + "}",
toJsonString(message));
assertRoundTripEquals(message); assertRoundTripEquals(message);
TestMap.Builder mapBuilder = TestMap.newBuilder(); TestMap.Builder mapBuilder = TestMap.newBuilder();
mapBuilder.getMutableInt32ToEnumMapValue().put(1, 0); mapBuilder.putInt32ToEnumMapValue(1, 0);
mapBuilder.getMutableInt32ToEnumMapValue().put(2, 12345); mapBuilder.getMutableInt32ToEnumMapValue().put(2, 12345);
TestMap mapMessage = mapBuilder.build(); TestMap mapMessage = mapBuilder.build();
assertEquals( assertEquals(
@ -226,7 +229,8 @@ public class JsonFormatTest extends TestCase {
} }
public void testSpecialFloatValues() throws Exception { public void testSpecialFloatValues() throws Exception {
TestAllTypes message = TestAllTypes.newBuilder() TestAllTypes message =
TestAllTypes.newBuilder()
.addRepeatedFloat(Float.NaN) .addRepeatedFloat(Float.NaN)
.addRepeatedFloat(Float.POSITIVE_INFINITY) .addRepeatedFloat(Float.POSITIVE_INFINITY)
.addRepeatedFloat(Float.NEGATIVE_INFINITY) .addRepeatedFloat(Float.NEGATIVE_INFINITY)
@ -238,7 +242,8 @@ public class JsonFormatTest extends TestCase {
"{\n" "{\n"
+ " \"repeatedFloat\": [\"NaN\", \"Infinity\", \"-Infinity\"],\n" + " \"repeatedFloat\": [\"NaN\", \"Infinity\", \"-Infinity\"],\n"
+ " \"repeatedDouble\": [\"NaN\", \"Infinity\", \"-Infinity\"]\n" + " \"repeatedDouble\": [\"NaN\", \"Infinity\", \"-Infinity\"]\n"
+ "}", toJsonString(message)); + "}",
toJsonString(message));
assertRoundTripEquals(message); assertRoundTripEquals(message);
} }
@ -255,7 +260,8 @@ public class JsonFormatTest extends TestCase {
+ " \"optionalFloat\": \"1.5\",\n" + " \"optionalFloat\": \"1.5\",\n"
+ " \"optionalDouble\": \"1.25\",\n" + " \"optionalDouble\": \"1.25\",\n"
+ " \"optionalBool\": \"true\"\n" + " \"optionalBool\": \"true\"\n"
+ "}", builder); + "}",
builder);
TestAllTypes message = builder.build(); TestAllTypes message = builder.build();
assertEquals(1234, message.getOptionalInt32()); assertEquals(1234, message.getOptionalInt32());
assertEquals(5678, message.getOptionalUint32()); assertEquals(5678, message.getOptionalUint32());
@ -276,7 +282,8 @@ public class JsonFormatTest extends TestCase {
+ " \"repeatedUint32\": [1.000, 1e5, \"1.000\", \"1e5\"],\n" + " \"repeatedUint32\": [1.000, 1e5, \"1.000\", \"1e5\"],\n"
+ " \"repeatedInt64\": [1.000, 1e5, \"1.000\", \"1e5\"],\n" + " \"repeatedInt64\": [1.000, 1e5, \"1.000\", \"1e5\"],\n"
+ " \"repeatedUint64\": [1.000, 1e5, \"1.000\", \"1e5\"]\n" + " \"repeatedUint64\": [1.000, 1e5, \"1.000\", \"1e5\"]\n"
+ "}", builder); + "}",
builder);
int[] expectedValues = new int[] {1, 100000, 1, 100000}; int[] expectedValues = new int[] {1, 100000, 1, 100000};
assertEquals(4, builder.getRepeatedInt32Count()); assertEquals(4, builder.getRepeatedInt32Count());
assertEquals(4, builder.getRepeatedUint32Count()); assertEquals(4, builder.getRepeatedUint32Count());
@ -400,17 +407,15 @@ public class JsonFormatTest extends TestCase {
+ " \"repeatedBytes\": null,\n" + " \"repeatedBytes\": null,\n"
+ " \"repeatedNestedMessage\": null,\n" + " \"repeatedNestedMessage\": null,\n"
+ " \"repeatedNestedEnum\": null\n" + " \"repeatedNestedEnum\": null\n"
+ "}", builder); + "}",
builder);
TestAllTypes message = builder.build(); TestAllTypes message = builder.build();
assertEquals(TestAllTypes.getDefaultInstance(), message); assertEquals(TestAllTypes.getDefaultInstance(), message);
// Repeated field elements cannot be null. // Repeated field elements cannot be null.
try { try {
builder = TestAllTypes.newBuilder(); builder = TestAllTypes.newBuilder();
mergeFromJson( mergeFromJson("{\n" + " \"repeatedInt32\": [null, null],\n" + "}", builder);
"{\n"
+ " \"repeatedInt32\": [null, null],\n"
+ "}", builder);
fail(); fail();
} catch (InvalidProtocolBufferException e) { } catch (InvalidProtocolBufferException e) {
// Exception expected. // Exception expected.
@ -418,16 +423,21 @@ public class JsonFormatTest extends TestCase {
try { try {
builder = TestAllTypes.newBuilder(); builder = TestAllTypes.newBuilder();
mergeFromJson( mergeFromJson("{\n" + " \"repeatedNestedMessage\": [null, null],\n" + "}", builder);
"{\n"
+ " \"repeatedNestedMessage\": [null, null],\n"
+ "}", builder);
fail(); fail();
} catch (InvalidProtocolBufferException e) { } catch (InvalidProtocolBufferException e) {
// Exception expected. // Exception expected.
} }
} }
public void testNullInOneof() throws Exception {
TestOneof.Builder builder = TestOneof.newBuilder();
mergeFromJson("{\n" + " \"oneofNullValue\": null \n" + "}", builder);
TestOneof message = builder.build();
assertEquals(TestOneof.OneofFieldCase.ONEOF_NULL_VALUE, message.getOneofFieldCase());
assertEquals(NullValue.NULL_VALUE, message.getOneofNullValue());
}
public void testParserRejectDuplicatedFields() throws Exception { public void testParserRejectDuplicatedFields() throws Exception {
// TODO(xiaofeng): The parser we are currently using (GSON) will accept and keep the last // TODO(xiaofeng): The parser we are currently using (GSON) will accept and keep the last
// one if multiple entries have the same name. This is not the desired behavior but it can // one if multiple entries have the same name. This is not the desired behavior but it can
@ -441,7 +451,8 @@ public class JsonFormatTest extends TestCase {
"{\n" "{\n"
+ " \"optionalNestedMessage\": {},\n" + " \"optionalNestedMessage\": {},\n"
+ " \"optional_nested_message\": {}\n" + " \"optional_nested_message\": {}\n"
+ "}", builder); + "}",
builder);
fail(); fail();
} catch (InvalidProtocolBufferException e) { } catch (InvalidProtocolBufferException e) {
// Exception expected. // Exception expected.
@ -454,7 +465,8 @@ public class JsonFormatTest extends TestCase {
"{\n" "{\n"
+ " \"repeatedNestedMessage\": [null, null],\n" + " \"repeatedNestedMessage\": [null, null],\n"
+ " \"repeated_nested_message\": [null, null]\n" + " \"repeated_nested_message\": [null, null]\n"
+ "}", builder); + "}",
builder);
fail(); fail();
} catch (InvalidProtocolBufferException e) { } catch (InvalidProtocolBufferException e) {
// Exception expected. // Exception expected.
@ -463,11 +475,7 @@ public class JsonFormatTest extends TestCase {
// Duplicated oneof fields. // Duplicated oneof fields.
try { try {
TestOneof.Builder builder = TestOneof.newBuilder(); TestOneof.Builder builder = TestOneof.newBuilder();
mergeFromJson( mergeFromJson("{\n" + " \"oneofInt32\": 1,\n" + " \"oneof_int32\": 2\n" + "}", builder);
"{\n"
+ " \"oneofInt32\": 1,\n"
+ " \"oneof_int32\": 2\n"
+ "}", builder);
fail(); fail();
} catch (InvalidProtocolBufferException e) { } catch (InvalidProtocolBufferException e) {
// Exception expected. // Exception expected.
@ -476,37 +484,35 @@ public class JsonFormatTest extends TestCase {
public void testMapFields() throws Exception { public void testMapFields() throws Exception {
TestMap.Builder builder = TestMap.newBuilder(); TestMap.Builder builder = TestMap.newBuilder();
builder.getMutableInt32ToInt32Map().put(1, 10); builder.putInt32ToInt32Map(1, 10);
builder.getMutableInt64ToInt32Map().put(1234567890123456789L, 10); builder.putInt64ToInt32Map(1234567890123456789L, 10);
builder.getMutableUint32ToInt32Map().put(2, 20); builder.putUint32ToInt32Map(2, 20);
builder.getMutableUint64ToInt32Map().put(2234567890123456789L, 20); builder.putUint64ToInt32Map(2234567890123456789L, 20);
builder.getMutableSint32ToInt32Map().put(3, 30); builder.putSint32ToInt32Map(3, 30);
builder.getMutableSint64ToInt32Map().put(3234567890123456789L, 30); builder.putSint64ToInt32Map(3234567890123456789L, 30);
builder.getMutableFixed32ToInt32Map().put(4, 40); builder.putFixed32ToInt32Map(4, 40);
builder.getMutableFixed64ToInt32Map().put(4234567890123456789L, 40); builder.putFixed64ToInt32Map(4234567890123456789L, 40);
builder.getMutableSfixed32ToInt32Map().put(5, 50); builder.putSfixed32ToInt32Map(5, 50);
builder.getMutableSfixed64ToInt32Map().put(5234567890123456789L, 50); builder.putSfixed64ToInt32Map(5234567890123456789L, 50);
builder.getMutableBoolToInt32Map().put(false, 6); builder.putBoolToInt32Map(false, 6);
builder.getMutableStringToInt32Map().put("Hello", 10); builder.putStringToInt32Map("Hello", 10);
builder.getMutableInt32ToInt64Map().put(1, 1234567890123456789L); builder.putInt32ToInt64Map(1, 1234567890123456789L);
builder.getMutableInt32ToUint32Map().put(2, 20); builder.putInt32ToUint32Map(2, 20);
builder.getMutableInt32ToUint64Map().put(2, 2234567890123456789L); builder.putInt32ToUint64Map(2, 2234567890123456789L);
builder.getMutableInt32ToSint32Map().put(3, 30); builder.putInt32ToSint32Map(3, 30);
builder.getMutableInt32ToSint64Map().put(3, 3234567890123456789L); builder.putInt32ToSint64Map(3, 3234567890123456789L);
builder.getMutableInt32ToFixed32Map().put(4, 40); builder.putInt32ToFixed32Map(4, 40);
builder.getMutableInt32ToFixed64Map().put(4, 4234567890123456789L); builder.putInt32ToFixed64Map(4, 4234567890123456789L);
builder.getMutableInt32ToSfixed32Map().put(5, 50); builder.putInt32ToSfixed32Map(5, 50);
builder.getMutableInt32ToSfixed64Map().put(5, 5234567890123456789L); builder.putInt32ToSfixed64Map(5, 5234567890123456789L);
builder.getMutableInt32ToFloatMap().put(6, 1.5f); builder.putInt32ToFloatMap(6, 1.5f);
builder.getMutableInt32ToDoubleMap().put(6, 1.25); builder.putInt32ToDoubleMap(6, 1.25);
builder.getMutableInt32ToBoolMap().put(7, false); builder.putInt32ToBoolMap(7, false);
builder.getMutableInt32ToStringMap().put(7, "World"); builder.putInt32ToStringMap(7, "World");
builder.getMutableInt32ToBytesMap().put( builder.putInt32ToBytesMap(8, ByteString.copyFrom(new byte[] {1, 2, 3}));
8, ByteString.copyFrom(new byte[]{1, 2, 3})); builder.putInt32ToMessageMap(8, NestedMessage.newBuilder().setValue(1234).build());
builder.getMutableInt32ToMessageMap().put( builder.putInt32ToEnumMap(9, NestedEnum.BAR);
8, NestedMessage.newBuilder().setValue(1234).build());
builder.getMutableInt32ToEnumMap().put(9, NestedEnum.BAR);
TestMap message = builder.build(); TestMap message = builder.build();
assertEquals( assertEquals(
@ -597,22 +603,19 @@ public class JsonFormatTest extends TestCase {
+ " \"int32ToEnumMap\": {\n" + " \"int32ToEnumMap\": {\n"
+ " \"9\": \"BAR\"\n" + " \"9\": \"BAR\"\n"
+ " }\n" + " }\n"
+ "}", toJsonString(message)); + "}",
toJsonString(message));
assertRoundTripEquals(message); assertRoundTripEquals(message);
// Test multiple entries. // Test multiple entries.
builder = TestMap.newBuilder(); builder = TestMap.newBuilder();
builder.getMutableInt32ToInt32Map().put(1, 2); builder.putInt32ToInt32Map(1, 2);
builder.getMutableInt32ToInt32Map().put(3, 4); builder.putInt32ToInt32Map(3, 4);
message = builder.build(); message = builder.build();
assertEquals( assertEquals(
"{\n" "{\n" + " \"int32ToInt32Map\": {\n" + " \"1\": 2,\n" + " \"3\": 4\n" + " }\n" + "}",
+ " \"int32ToInt32Map\": {\n" toJsonString(message));
+ " \"1\": 2,\n"
+ " \"3\": 4\n"
+ " }\n"
+ "}", toJsonString(message));
assertRoundTripEquals(message); assertRoundTripEquals(message);
} }
@ -623,7 +626,8 @@ public class JsonFormatTest extends TestCase {
"{\n" "{\n"
+ " \"int32ToInt32Map\": {null: 1},\n" + " \"int32ToInt32Map\": {null: 1},\n"
+ " \"int32ToMessageMap\": {null: 2}\n" + " \"int32ToMessageMap\": {null: 2}\n"
+ "}", builder); + "}",
builder);
fail(); fail();
} catch (InvalidProtocolBufferException e) { } catch (InvalidProtocolBufferException e) {
// Exception expected. // Exception expected.
@ -635,7 +639,8 @@ public class JsonFormatTest extends TestCase {
"{\n" "{\n"
+ " \"int32ToInt32Map\": {\"1\": null},\n" + " \"int32ToInt32Map\": {\"1\": null},\n"
+ " \"int32ToMessageMap\": {\"2\": null}\n" + " \"int32ToMessageMap\": {\"2\": null}\n"
+ "}", builder); + "}",
builder);
fail(); fail();
} catch (InvalidProtocolBufferException e) { } catch (InvalidProtocolBufferException e) {
// Exception expected. // Exception expected.
@ -645,10 +650,7 @@ public class JsonFormatTest extends TestCase {
public void testParserAcceptNonQuotedObjectKey() throws Exception { public void testParserAcceptNonQuotedObjectKey() throws Exception {
TestMap.Builder builder = TestMap.newBuilder(); TestMap.Builder builder = TestMap.newBuilder();
mergeFromJson( mergeFromJson(
"{\n" "{\n" + " int32ToInt32Map: {1: 2},\n" + " stringToInt32Map: {hello: 3}\n" + "}", builder);
+ " int32ToInt32Map: {1: 2},\n"
+ " stringToInt32Map: {hello: 3}\n"
+ "}", builder);
TestMap message = builder.build(); TestMap message = builder.build();
assertEquals(2, message.getInt32ToInt32Map().get(1).intValue()); assertEquals(2, message.getInt32ToInt32Map().get(1).intValue());
assertEquals(3, message.getStringToInt32Map().get("hello").intValue()); assertEquals(3, message.getStringToInt32Map().get("hello").intValue());
@ -678,7 +680,8 @@ public class JsonFormatTest extends TestCase {
+ " \"boolValue\": false,\n" + " \"boolValue\": false,\n"
+ " \"stringValue\": \"\",\n" + " \"stringValue\": \"\",\n"
+ " \"bytesValue\": \"\"\n" + " \"bytesValue\": \"\"\n"
+ "}", toJsonString(message)); + "}",
toJsonString(message));
assertRoundTripEquals(message); assertRoundTripEquals(message);
builder = TestWrappers.newBuilder(); builder = TestWrappers.newBuilder();
@ -704,43 +707,38 @@ public class JsonFormatTest extends TestCase {
+ " \"boolValue\": true,\n" + " \"boolValue\": true,\n"
+ " \"stringValue\": \"7\",\n" + " \"stringValue\": \"7\",\n"
+ " \"bytesValue\": \"CA==\"\n" + " \"bytesValue\": \"CA==\"\n"
+ "}", toJsonString(message)); + "}",
toJsonString(message));
assertRoundTripEquals(message); assertRoundTripEquals(message);
} }
public void testTimestamp() throws Exception { public void testTimestamp() throws Exception {
TestTimestamp message = TestTimestamp.newBuilder() TestTimestamp message =
.setTimestampValue(TimeUtil.parseTimestamp("1970-01-01T00:00:00Z")) TestTimestamp.newBuilder()
.setTimestampValue(Timestamps.parse("1970-01-01T00:00:00Z"))
.build(); .build();
assertEquals( assertEquals(
"{\n" "{\n" + " \"timestampValue\": \"1970-01-01T00:00:00Z\"\n" + "}", toJsonString(message));
+ " \"timestampValue\": \"1970-01-01T00:00:00Z\"\n"
+ "}", toJsonString(message));
assertRoundTripEquals(message); assertRoundTripEquals(message);
} }
public void testDuration() throws Exception { public void testDuration() throws Exception {
TestDuration message = TestDuration.newBuilder() TestDuration message =
.setDurationValue(TimeUtil.parseDuration("12345s")) TestDuration.newBuilder().setDurationValue(Durations.parse("12345s")).build();
.build();
assertEquals( assertEquals("{\n" + " \"durationValue\": \"12345s\"\n" + "}", toJsonString(message));
"{\n"
+ " \"durationValue\": \"12345s\"\n"
+ "}", toJsonString(message));
assertRoundTripEquals(message); assertRoundTripEquals(message);
} }
public void testFieldMask() throws Exception { public void testFieldMask() throws Exception {
TestFieldMask message = TestFieldMask.newBuilder() TestFieldMask message =
.setFieldMaskValue(FieldMaskUtil.fromString("foo.bar,baz")) TestFieldMask.newBuilder()
.setFieldMaskValue(FieldMaskUtil.fromString("foo.bar,baz,foo_bar.baz"))
.build(); .build();
assertEquals( assertEquals(
"{\n" "{\n" + " \"fieldMaskValue\": \"foo.bar,baz,fooBar.baz\"\n" + "}", toJsonString(message));
+ " \"fieldMaskValue\": \"foo.bar,baz\"\n"
+ "}", toJsonString(message));
assertRoundTripEquals(message); assertRoundTripEquals(message);
} }
@ -748,21 +746,17 @@ public class JsonFormatTest extends TestCase {
// Build a struct with all possible values. // Build a struct with all possible values.
TestStruct.Builder builder = TestStruct.newBuilder(); TestStruct.Builder builder = TestStruct.newBuilder();
Struct.Builder structBuilder = builder.getStructValueBuilder(); Struct.Builder structBuilder = builder.getStructValueBuilder();
structBuilder.getMutableFields().put( structBuilder.putFields("null_value", Value.newBuilder().setNullValueValue(0).build());
"null_value", Value.newBuilder().setNullValueValue(0).build()); structBuilder.putFields("number_value", Value.newBuilder().setNumberValue(1.25).build());
structBuilder.getMutableFields().put( structBuilder.putFields("string_value", Value.newBuilder().setStringValue("hello").build());
"number_value", Value.newBuilder().setNumberValue(1.25).build());
structBuilder.getMutableFields().put(
"string_value", Value.newBuilder().setStringValue("hello").build());
Struct.Builder subStructBuilder = Struct.newBuilder(); Struct.Builder subStructBuilder = Struct.newBuilder();
subStructBuilder.getMutableFields().put( subStructBuilder.putFields("number_value", Value.newBuilder().setNumberValue(1234).build());
"number_value", Value.newBuilder().setNumberValue(1234).build()); structBuilder.putFields(
structBuilder.getMutableFields().put(
"struct_value", Value.newBuilder().setStructValue(subStructBuilder.build()).build()); "struct_value", Value.newBuilder().setStructValue(subStructBuilder.build()).build());
ListValue.Builder listBuilder = ListValue.newBuilder(); ListValue.Builder listBuilder = ListValue.newBuilder();
listBuilder.addValues(Value.newBuilder().setNumberValue(1.125).build()); listBuilder.addValues(Value.newBuilder().setNumberValue(1.125).build());
listBuilder.addValues(Value.newBuilder().setNullValueValue(0).build()); listBuilder.addValues(Value.newBuilder().setNullValueValue(0).build());
structBuilder.getMutableFields().put( structBuilder.putFields(
"list_value", Value.newBuilder().setListValue(listBuilder.build()).build()); "list_value", Value.newBuilder().setListValue(listBuilder.build()).build());
TestStruct message = builder.build(); TestStruct message = builder.build();
@ -777,16 +771,14 @@ public class JsonFormatTest extends TestCase {
+ " },\n" + " },\n"
+ " \"list_value\": [1.125, null]\n" + " \"list_value\": [1.125, null]\n"
+ " }\n" + " }\n"
+ "}", toJsonString(message)); + "}",
toJsonString(message));
assertRoundTripEquals(message); assertRoundTripEquals(message);
builder = TestStruct.newBuilder(); builder = TestStruct.newBuilder();
builder.setValue(Value.newBuilder().setNullValueValue(0).build()); builder.setValue(Value.newBuilder().setNullValueValue(0).build());
message = builder.build(); message = builder.build();
assertEquals( assertEquals("{\n" + " \"value\": null\n" + "}", toJsonString(message));
"{\n"
+ " \"value\": null\n"
+ "}", toJsonString(message));
assertRoundTripEquals(message); assertRoundTripEquals(message);
builder = TestStruct.newBuilder(); builder = TestStruct.newBuilder();
@ -794,10 +786,7 @@ public class JsonFormatTest extends TestCase {
listBuilder.addValues(Value.newBuilder().setNumberValue(31831.125).build()); listBuilder.addValues(Value.newBuilder().setNumberValue(31831.125).build());
listBuilder.addValues(Value.newBuilder().setNullValueValue(0).build()); listBuilder.addValues(Value.newBuilder().setNullValueValue(0).build());
message = builder.build(); message = builder.build();
assertEquals( assertEquals("{\n" + " \"listValue\": [31831.125, null]\n" + "}", toJsonString(message));
"{\n"
+ " \"listValue\": [31831.125, null]\n"
+ "}", toJsonString(message));
assertRoundTripEquals(message); assertRoundTripEquals(message);
} }
@ -813,8 +802,8 @@ public class JsonFormatTest extends TestCase {
// Expected. // Expected.
} }
JsonFormat.TypeRegistry registry = JsonFormat.TypeRegistry.newBuilder() JsonFormat.TypeRegistry registry =
.add(TestAllTypes.getDescriptor()).build(); JsonFormat.TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build();
JsonFormat.Printer printer = JsonFormat.printer().usingTypeRegistry(registry); JsonFormat.Printer printer = JsonFormat.printer().usingTypeRegistry(registry);
assertEquals( assertEquals(
@ -823,10 +812,10 @@ public class JsonFormatTest extends TestCase {
+ " \"@type\": \"type.googleapis.com/json_test.TestAllTypes\",\n" + " \"@type\": \"type.googleapis.com/json_test.TestAllTypes\",\n"
+ " \"optionalInt32\": 1234\n" + " \"optionalInt32\": 1234\n"
+ " }\n" + " }\n"
+ "}" , printer.print(message)); + "}",
printer.print(message));
assertRoundTripEquals(message, registry); assertRoundTripEquals(message, registry);
// Well-known types have a special formatting when embedded in Any. // Well-known types have a special formatting when embedded in Any.
// //
// 1. Any in Any. // 1. Any in Any.
@ -838,7 +827,8 @@ public class JsonFormatTest extends TestCase {
+ " \"@type\": \"type.googleapis.com/json_test.TestAllTypes\",\n" + " \"@type\": \"type.googleapis.com/json_test.TestAllTypes\",\n"
+ " \"optionalInt32\": 1234\n" + " \"optionalInt32\": 1234\n"
+ " }\n" + " }\n"
+ "}", printer.print(anyMessage)); + "}",
printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry); assertRoundTripEquals(anyMessage, registry);
// 2. Wrappers in Any. // 2. Wrappers in Any.
@ -847,82 +837,93 @@ public class JsonFormatTest extends TestCase {
"{\n" "{\n"
+ " \"@type\": \"type.googleapis.com/google.protobuf.Int32Value\",\n" + " \"@type\": \"type.googleapis.com/google.protobuf.Int32Value\",\n"
+ " \"value\": 12345\n" + " \"value\": 12345\n"
+ "}", printer.print(anyMessage)); + "}",
printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry); assertRoundTripEquals(anyMessage, registry);
anyMessage = Any.pack(UInt32Value.newBuilder().setValue(12345).build()); anyMessage = Any.pack(UInt32Value.newBuilder().setValue(12345).build());
assertEquals( assertEquals(
"{\n" "{\n"
+ " \"@type\": \"type.googleapis.com/google.protobuf.UInt32Value\",\n" + " \"@type\": \"type.googleapis.com/google.protobuf.UInt32Value\",\n"
+ " \"value\": 12345\n" + " \"value\": 12345\n"
+ "}", printer.print(anyMessage)); + "}",
printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry); assertRoundTripEquals(anyMessage, registry);
anyMessage = Any.pack(Int64Value.newBuilder().setValue(12345).build()); anyMessage = Any.pack(Int64Value.newBuilder().setValue(12345).build());
assertEquals( assertEquals(
"{\n" "{\n"
+ " \"@type\": \"type.googleapis.com/google.protobuf.Int64Value\",\n" + " \"@type\": \"type.googleapis.com/google.protobuf.Int64Value\",\n"
+ " \"value\": \"12345\"\n" + " \"value\": \"12345\"\n"
+ "}", printer.print(anyMessage)); + "}",
printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry); assertRoundTripEquals(anyMessage, registry);
anyMessage = Any.pack(UInt64Value.newBuilder().setValue(12345).build()); anyMessage = Any.pack(UInt64Value.newBuilder().setValue(12345).build());
assertEquals( assertEquals(
"{\n" "{\n"
+ " \"@type\": \"type.googleapis.com/google.protobuf.UInt64Value\",\n" + " \"@type\": \"type.googleapis.com/google.protobuf.UInt64Value\",\n"
+ " \"value\": \"12345\"\n" + " \"value\": \"12345\"\n"
+ "}", printer.print(anyMessage)); + "}",
printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry); assertRoundTripEquals(anyMessage, registry);
anyMessage = Any.pack(FloatValue.newBuilder().setValue(12345).build()); anyMessage = Any.pack(FloatValue.newBuilder().setValue(12345).build());
assertEquals( assertEquals(
"{\n" "{\n"
+ " \"@type\": \"type.googleapis.com/google.protobuf.FloatValue\",\n" + " \"@type\": \"type.googleapis.com/google.protobuf.FloatValue\",\n"
+ " \"value\": 12345.0\n" + " \"value\": 12345.0\n"
+ "}", printer.print(anyMessage)); + "}",
printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry); assertRoundTripEquals(anyMessage, registry);
anyMessage = Any.pack(DoubleValue.newBuilder().setValue(12345).build()); anyMessage = Any.pack(DoubleValue.newBuilder().setValue(12345).build());
assertEquals( assertEquals(
"{\n" "{\n"
+ " \"@type\": \"type.googleapis.com/google.protobuf.DoubleValue\",\n" + " \"@type\": \"type.googleapis.com/google.protobuf.DoubleValue\",\n"
+ " \"value\": 12345.0\n" + " \"value\": 12345.0\n"
+ "}", printer.print(anyMessage)); + "}",
printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry); assertRoundTripEquals(anyMessage, registry);
anyMessage = Any.pack(BoolValue.newBuilder().setValue(true).build()); anyMessage = Any.pack(BoolValue.newBuilder().setValue(true).build());
assertEquals( assertEquals(
"{\n" "{\n"
+ " \"@type\": \"type.googleapis.com/google.protobuf.BoolValue\",\n" + " \"@type\": \"type.googleapis.com/google.protobuf.BoolValue\",\n"
+ " \"value\": true\n" + " \"value\": true\n"
+ "}", printer.print(anyMessage)); + "}",
printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry); assertRoundTripEquals(anyMessage, registry);
anyMessage = Any.pack(StringValue.newBuilder().setValue("Hello").build()); anyMessage = Any.pack(StringValue.newBuilder().setValue("Hello").build());
assertEquals( assertEquals(
"{\n" "{\n"
+ " \"@type\": \"type.googleapis.com/google.protobuf.StringValue\",\n" + " \"@type\": \"type.googleapis.com/google.protobuf.StringValue\",\n"
+ " \"value\": \"Hello\"\n" + " \"value\": \"Hello\"\n"
+ "}", printer.print(anyMessage)); + "}",
printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry); assertRoundTripEquals(anyMessage, registry);
anyMessage = Any.pack(BytesValue.newBuilder().setValue( anyMessage =
ByteString.copyFrom(new byte[]{1, 2})).build()); Any.pack(BytesValue.newBuilder().setValue(ByteString.copyFrom(new byte[] {1, 2})).build());
assertEquals( assertEquals(
"{\n" "{\n"
+ " \"@type\": \"type.googleapis.com/google.protobuf.BytesValue\",\n" + " \"@type\": \"type.googleapis.com/google.protobuf.BytesValue\",\n"
+ " \"value\": \"AQI=\"\n" + " \"value\": \"AQI=\"\n"
+ "}", printer.print(anyMessage)); + "}",
printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry); assertRoundTripEquals(anyMessage, registry);
// 3. Timestamp in Any. // 3. Timestamp in Any.
anyMessage = Any.pack(TimeUtil.parseTimestamp("1969-12-31T23:59:59Z")); anyMessage = Any.pack(Timestamps.parse("1969-12-31T23:59:59Z"));
assertEquals( assertEquals(
"{\n" "{\n"
+ " \"@type\": \"type.googleapis.com/google.protobuf.Timestamp\",\n" + " \"@type\": \"type.googleapis.com/google.protobuf.Timestamp\",\n"
+ " \"value\": \"1969-12-31T23:59:59Z\"\n" + " \"value\": \"1969-12-31T23:59:59Z\"\n"
+ "}", printer.print(anyMessage)); + "}",
printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry); assertRoundTripEquals(anyMessage, registry);
// 4. Duration in Any // 4. Duration in Any
anyMessage = Any.pack(TimeUtil.parseDuration("12345.10s")); anyMessage = Any.pack(Durations.parse("12345.10s"));
assertEquals( assertEquals(
"{\n" "{\n"
+ " \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n" + " \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n"
+ " \"value\": \"12345.100s\"\n" + " \"value\": \"12345.100s\"\n"
+ "}", printer.print(anyMessage)); + "}",
printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry); assertRoundTripEquals(anyMessage, registry);
// 5. FieldMask in Any // 5. FieldMask in Any
@ -931,13 +932,13 @@ public class JsonFormatTest extends TestCase {
"{\n" "{\n"
+ " \"@type\": \"type.googleapis.com/google.protobuf.FieldMask\",\n" + " \"@type\": \"type.googleapis.com/google.protobuf.FieldMask\",\n"
+ " \"value\": \"foo.bar,baz\"\n" + " \"value\": \"foo.bar,baz\"\n"
+ "}", printer.print(anyMessage)); + "}",
printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry); assertRoundTripEquals(anyMessage, registry);
// 6. Struct in Any // 6. Struct in Any
Struct.Builder structBuilder = Struct.newBuilder(); Struct.Builder structBuilder = Struct.newBuilder();
structBuilder.getMutableFields().put( structBuilder.putFields("number", Value.newBuilder().setNumberValue(1.125).build());
"number", Value.newBuilder().setNumberValue(1.125).build());
anyMessage = Any.pack(structBuilder.build()); anyMessage = Any.pack(structBuilder.build());
assertEquals( assertEquals(
"{\n" "{\n"
@ -945,7 +946,8 @@ public class JsonFormatTest extends TestCase {
+ " \"value\": {\n" + " \"value\": {\n"
+ " \"number\": 1.125\n" + " \"number\": 1.125\n"
+ " }\n" + " }\n"
+ "}", printer.print(anyMessage)); + "}",
printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry); assertRoundTripEquals(anyMessage, registry);
Value.Builder valueBuilder = Value.newBuilder(); Value.Builder valueBuilder = Value.newBuilder();
valueBuilder.setNumberValue(1); valueBuilder.setNumberValue(1);
@ -954,17 +956,15 @@ public class JsonFormatTest extends TestCase {
"{\n" "{\n"
+ " \"@type\": \"type.googleapis.com/google.protobuf.Value\",\n" + " \"@type\": \"type.googleapis.com/google.protobuf.Value\",\n"
+ " \"value\": 1.0\n" + " \"value\": 1.0\n"
+ "}", printer.print(anyMessage)); + "}",
printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry); assertRoundTripEquals(anyMessage, registry);
} }
public void testParserMissingTypeUrl() throws Exception { public void testParserMissingTypeUrl() throws Exception {
try { try {
Any.Builder builder = Any.newBuilder(); Any.Builder builder = Any.newBuilder();
mergeFromJson( mergeFromJson("{\n" + " \"optionalInt32\": 1234\n" + "}", builder);
"{\n"
+ " \"optionalInt32\": 1234\n"
+ "}", builder);
fail("Exception is expected."); fail("Exception is expected.");
} catch (IOException e) { } catch (IOException e) {
// Expected. // Expected.
@ -978,7 +978,8 @@ public class JsonFormatTest extends TestCase {
"{\n" "{\n"
+ " \"@type\": \"type.googleapis.com/json_test.TestAllTypes\",\n" + " \"@type\": \"type.googleapis.com/json_test.TestAllTypes\",\n"
+ " \"optionalInt32\": 12345\n" + " \"optionalInt32\": 12345\n"
+ "}", builder); + "}",
builder);
fail("Exception is expected."); fail("Exception is expected.");
} catch (IOException e) { } catch (IOException e) {
// Expected. // Expected.
@ -988,10 +989,7 @@ public class JsonFormatTest extends TestCase {
public void testParserRejectTrailingComma() throws Exception { public void testParserRejectTrailingComma() throws Exception {
try { try {
TestAllTypes.Builder builder = TestAllTypes.newBuilder(); TestAllTypes.Builder builder = TestAllTypes.newBuilder();
mergeFromJson( mergeFromJson("{\n" + " \"optionalInt32\": 12345,\n" + "}", builder);
"{\n"
+ " \"optionalInt32\": 12345,\n"
+ "}", builder);
fail("Exception is expected."); fail("Exception is expected.");
} catch (IOException e) { } catch (IOException e) {
// Expected. // Expected.
@ -1022,10 +1020,7 @@ public class JsonFormatTest extends TestCase {
public void testParserRejectInvalidEnumValue() throws Exception { public void testParserRejectInvalidEnumValue() throws Exception {
try { try {
TestAllTypes.Builder builder = TestAllTypes.newBuilder(); TestAllTypes.Builder builder = TestAllTypes.newBuilder();
mergeFromJson( mergeFromJson("{\n" + " \"optionalNestedEnum\": \"XXX\"\n" + "}", builder);
"{\n"
+ " \"optionalNestedEnum\": \"XXX\"\n"
+ "}", builder);
fail("Exception is expected."); fail("Exception is expected.");
} catch (InvalidProtocolBufferException e) { } catch (InvalidProtocolBufferException e) {
// Expected. // Expected.

@ -84,6 +84,7 @@ public class TimeUtilTest extends TestCase {
private class ParseTimestampThread extends Thread { private class ParseTimestampThread extends Thread {
private final String[] strings; private final String[] strings;
private final Timestamp[] values; private final Timestamp[] values;
public ParseTimestampThread(String[] strings, Timestamp[] values) { public ParseTimestampThread(String[] strings, Timestamp[] values) {
this.strings = strings; this.strings = strings;
this.values = values; this.values = values;
@ -102,8 +103,8 @@ public class TimeUtilTest extends TestCase {
} }
if (result.getSeconds() != values[index].getSeconds() if (result.getSeconds() != values[index].getSeconds()
|| result.getNanos() != values[index].getNanos()) { || result.getNanos() != values[index].getNanos()) {
errorMessage = "Actual result: " + result.toString() + ", expected: " errorMessage =
+ values[index].toString(); "Actual result: " + result.toString() + ", expected: " + values[index].toString();
break; break;
} }
index = (index + 1) % strings.length; index = (index + 1) % strings.length;
@ -112,7 +113,8 @@ public class TimeUtilTest extends TestCase {
} }
public void testTimestampConcurrentParsing() throws Exception { public void testTimestampConcurrentParsing() throws Exception {
String[] timestampStrings = new String[]{ String[] timestampStrings =
new String[] {
"0001-01-01T00:00:00Z", "0001-01-01T00:00:00Z",
"9999-12-31T23:59:59.999999999Z", "9999-12-31T23:59:59.999999999Z",
"1970-01-01T00:00:00Z", "1970-01-01T00:00:00Z",
@ -130,8 +132,7 @@ public class TimeUtilTest extends TestCase {
stopParsingThreads = false; stopParsingThreads = false;
errorMessage = ""; errorMessage = "";
for (int i = 0; i < THREAD_COUNT; i++) { for (int i = 0; i < THREAD_COUNT; i++) {
Thread thread = new ParseTimestampThread( Thread thread = new ParseTimestampThread(timestampStrings, timestampValues);
timestampStrings, timestampValues);
thread.start(); thread.start();
threads.add(thread); threads.add(thread);
} }
@ -146,8 +147,8 @@ public class TimeUtilTest extends TestCase {
public void testTimetampInvalidFormat() throws Exception { public void testTimetampInvalidFormat() throws Exception {
try { try {
// Value too small. // Value too small.
Timestamp value = Timestamp.newBuilder() Timestamp value =
.setSeconds(TimeUtil.TIMESTAMP_SECONDS_MIN - 1).build(); Timestamp.newBuilder().setSeconds(TimeUtil.TIMESTAMP_SECONDS_MIN - 1).build();
TimeUtil.toString(value); TimeUtil.toString(value);
Assert.fail("Exception is expected."); Assert.fail("Exception is expected.");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
@ -156,8 +157,8 @@ public class TimeUtilTest extends TestCase {
try { try {
// Value too large. // Value too large.
Timestamp value = Timestamp.newBuilder() Timestamp value =
.setSeconds(TimeUtil.TIMESTAMP_SECONDS_MAX + 1).build(); Timestamp.newBuilder().setSeconds(TimeUtil.TIMESTAMP_SECONDS_MAX + 1).build();
TimeUtil.toString(value); TimeUtil.toString(value);
Assert.fail("Exception is expected."); Assert.fail("Exception is expected.");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
@ -279,8 +280,7 @@ public class TimeUtilTest extends TestCase {
public void testDurationInvalidFormat() throws Exception { public void testDurationInvalidFormat() throws Exception {
try { try {
// Value too small. // Value too small.
Duration value = Duration.newBuilder() Duration value = Duration.newBuilder().setSeconds(TimeUtil.DURATION_SECONDS_MIN - 1).build();
.setSeconds(TimeUtil.DURATION_SECONDS_MIN - 1).build();
TimeUtil.toString(value); TimeUtil.toString(value);
Assert.fail("Exception is expected."); Assert.fail("Exception is expected.");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
@ -289,8 +289,7 @@ public class TimeUtilTest extends TestCase {
try { try {
// Value too large. // Value too large.
Duration value = Duration.newBuilder() Duration value = Duration.newBuilder().setSeconds(TimeUtil.DURATION_SECONDS_MAX + 1).build();
.setSeconds(TimeUtil.DURATION_SECONDS_MAX + 1).build();
TimeUtil.toString(value); TimeUtil.toString(value);
Assert.fail("Exception is expected."); Assert.fail("Exception is expected.");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
@ -299,8 +298,7 @@ public class TimeUtilTest extends TestCase {
try { try {
// Invalid nanos value. // Invalid nanos value.
Duration value = Duration.newBuilder().setSeconds(1).setNanos(-1) Duration value = Duration.newBuilder().setSeconds(1).setNanos(-1).build();
.build();
TimeUtil.toString(value); TimeUtil.toString(value);
Assert.fail("Exception is expected."); Assert.fail("Exception is expected.");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
@ -309,8 +307,7 @@ public class TimeUtilTest extends TestCase {
try { try {
// Invalid nanos value. // Invalid nanos value.
Duration value = Duration.newBuilder().setSeconds(-1).setNanos(1) Duration value = Duration.newBuilder().setSeconds(-1).setNanos(1).build();
.build();
TimeUtil.toString(value); TimeUtil.toString(value);
Assert.fail("Exception is expected."); Assert.fail("Exception is expected.");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
@ -367,8 +364,7 @@ public class TimeUtilTest extends TestCase {
} }
public void testTimestampConversion() throws Exception { public void testTimestampConversion() throws Exception {
Timestamp timestamp = Timestamp timestamp = TimeUtil.parseTimestamp("1970-01-01T00:00:01.111111111Z");
TimeUtil.parseTimestamp("1970-01-01T00:00:01.111111111Z");
assertEquals(1111111111, TimeUtil.toNanos(timestamp)); assertEquals(1111111111, TimeUtil.toNanos(timestamp));
assertEquals(1111111, TimeUtil.toMicros(timestamp)); assertEquals(1111111, TimeUtil.toMicros(timestamp));
assertEquals(1111, TimeUtil.toMillis(timestamp)); assertEquals(1111, TimeUtil.toMillis(timestamp));
@ -465,13 +461,13 @@ public class TimeUtilTest extends TestCase {
// Multiplications (with results larger than Long.MAX_VALUE in nanoseconds). // Multiplications (with results larger than Long.MAX_VALUE in nanoseconds).
duration = TimeUtil.parseDuration("0.999999999s"); duration = TimeUtil.parseDuration("0.999999999s");
assertEquals("315575999684.424s", assertEquals(
TimeUtil.toString(TimeUtil.multiply(duration, 315576000000L))); "315575999684.424s", TimeUtil.toString(TimeUtil.multiply(duration, 315576000000L)));
duration = TimeUtil.parseDuration("-0.999999999s"); duration = TimeUtil.parseDuration("-0.999999999s");
assertEquals("-315575999684.424s", assertEquals(
TimeUtil.toString(TimeUtil.multiply(duration, 315576000000L))); "-315575999684.424s", TimeUtil.toString(TimeUtil.multiply(duration, 315576000000L)));
assertEquals("315575999684.424s", assertEquals(
TimeUtil.toString(TimeUtil.multiply(duration, -315576000000L))); "315575999684.424s", TimeUtil.toString(TimeUtil.multiply(duration, -315576000000L)));
// Divisions (with values larger than Long.MAX_VALUE in nanoseconds). // Divisions (with values larger than Long.MAX_VALUE in nanoseconds).
Duration d1 = TimeUtil.parseDuration("315576000000s"); Duration d1 = TimeUtil.parseDuration("315576000000s");
@ -479,8 +475,7 @@ public class TimeUtilTest extends TestCase {
assertEquals(1, TimeUtil.divide(d1, d2)); assertEquals(1, TimeUtil.divide(d1, d2));
assertEquals(0, TimeUtil.divide(d2, d1)); assertEquals(0, TimeUtil.divide(d2, d1));
assertEquals("0.000000001s", TimeUtil.toString(TimeUtil.remainder(d1, d2))); assertEquals("0.000000001s", TimeUtil.toString(TimeUtil.remainder(d1, d2)));
assertEquals("315575999999.999999999s", assertEquals("315575999999.999999999s", TimeUtil.toString(TimeUtil.remainder(d2, d1)));
TimeUtil.toString(TimeUtil.remainder(d2, d1)));
// Divisions involving negative values. // Divisions involving negative values.
// //

@ -124,6 +124,7 @@ message TestOneof {
oneof oneof_field { oneof oneof_field {
int32 oneof_int32 = 1; int32 oneof_int32 = 1;
TestAllTypes.NestedMessage oneof_nested_message = 2; TestAllTypes.NestedMessage oneof_nested_message = 2;
google.protobuf.NullValue oneof_null_value = 3;
} }
} }

@ -141,8 +141,8 @@ jspb.ReaderFunction;
/** /**
* A writer function serializes a message to a BinaryWriter. * A writer function serializes a message to a BinaryWriter.
* @typedef {!function(!jspb.Message, !jspb.BinaryWriter):void | * @typedef {function((!jspb.Message|!jspb.ConstBinaryMessage),
* !function(!jspb.ConstBinaryMessage, !jspb.BinaryWriter):void} * !jspb.BinaryWriter):void}
*/ */
jspb.WriterFunction; jspb.WriterFunction;

@ -147,9 +147,8 @@ function doTestSignedValue(readValue,
describe('binaryDecoderTest', function() { describe('binaryDecoderTest', function() {
/** /**
* Tests the decoder instance cache. * Tests the decoder instance cache.
* @suppress {visibility}
*/ */
it('testInstanceCache', function() { it('testInstanceCache', /** @suppress {visibility} */ function() {
// Empty the instance caches. // Empty the instance caches.
jspb.BinaryDecoder.instanceCache_ = []; jspb.BinaryDecoder.instanceCache_ = [];

@ -52,9 +52,8 @@ goog.require('jspb.BinaryWriter');
describe('binaryReaderTest', function() { describe('binaryReaderTest', function() {
/** /**
* Tests the reader instance cache. * Tests the reader instance cache.
* @suppress {visibility}
*/ */
it('testInstanceCaches', function() { it('testInstanceCaches', /** @suppress {visibility} */ function() {
var writer = new jspb.BinaryWriter(); var writer = new jspb.BinaryWriter();
var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
writer.writeMessage(1, dummyMessage, goog.nullFunction); writer.writeMessage(1, dummyMessage, goog.nullFunction);
@ -131,9 +130,8 @@ describe('binaryReaderTest', function() {
/** /**
* Verifies that misuse of the reader class triggers assertions. * Verifies that misuse of the reader class triggers assertions.
* @suppress {checkTypes|visibility}
*/ */
it('testReadErrors', function() { it('testReadErrors', /** @suppress {checkTypes|visibility} */ function() {
// Calling readMessage on a non-delimited field should trigger an // Calling readMessage on a non-delimited field should trigger an
// assertion. // assertion.
var reader = jspb.BinaryReader.alloc([8, 1]); var reader = jspb.BinaryReader.alloc([8, 1]);
@ -200,7 +198,7 @@ describe('binaryReaderTest', function() {
* @private * @private
* @suppress {missingProperties} * @suppress {missingProperties}
*/ */
function doTestUnsignedField_(readField, var doTestUnsignedField_ = function(readField,
writeField, epsilon, upperLimit, filter) { writeField, epsilon, upperLimit, filter) {
assertNotNull(readField); assertNotNull(readField);
assertNotNull(writeField); assertNotNull(writeField);
@ -252,7 +250,7 @@ describe('binaryReaderTest', function() {
* @private * @private
* @suppress {missingProperties} * @suppress {missingProperties}
*/ */
function doTestSignedField_(readField, var doTestSignedField_ = function(readField,
writeField, epsilon, lowerLimit, upperLimit, filter) { writeField, epsilon, lowerLimit, upperLimit, filter) {
var writer = new jspb.BinaryWriter(); var writer = new jspb.BinaryWriter();
@ -321,12 +319,12 @@ describe('binaryReaderTest', function() {
* Tests fields that use varint encoding. * Tests fields that use varint encoding.
*/ */
it('testVarintFields', function() { it('testVarintFields', function() {
assertNotNull(jspb.BinaryReader.prototype.readUint32); assertNotUndefined(jspb.BinaryReader.prototype.readUint32);
assertNotNull(jspb.BinaryReader.prototype.writeUint32); assertNotUndefined(jspb.BinaryWriter.prototype.writeUint32);
assertNotNull(jspb.BinaryReader.prototype.readUint64); assertNotUndefined(jspb.BinaryReader.prototype.readUint64);
assertNotNull(jspb.BinaryReader.prototype.writeUint64); assertNotUndefined(jspb.BinaryWriter.prototype.writeUint64);
assertNotNull(jspb.BinaryReader.prototype.readBool); assertNotUndefined(jspb.BinaryReader.prototype.readBool);
assertNotNull(jspb.BinaryReader.prototype.writeBool); assertNotUndefined(jspb.BinaryWriter.prototype.writeBool);
doTestUnsignedField_( doTestUnsignedField_(
jspb.BinaryReader.prototype.readUint32, jspb.BinaryReader.prototype.readUint32,
jspb.BinaryWriter.prototype.writeUint32, jspb.BinaryWriter.prototype.writeUint32,
@ -369,8 +367,7 @@ describe('binaryReaderTest', function() {
var bytesCount = (hexString.length + 1) / 3; var bytesCount = (hexString.length + 1) / 3;
var bytes = new Uint8Array(bytesCount); var bytes = new Uint8Array(bytesCount);
for (var i = 0; i < bytesCount; i++) { for (var i = 0; i < bytesCount; i++) {
byte = parseInt(hexString.substring(i * 3, i * 3 + 2), 16); bytes[i] = parseInt(hexString.substring(i * 3, i * 3 + 2), 16);
bytes[i] = byte;
} }
var reader = jspb.BinaryReader.alloc(bytes); var reader = jspb.BinaryReader.alloc(bytes);
reader.nextField(); reader.nextField();

@ -717,11 +717,19 @@ jspb.BinaryWriter.prototype.writeBytes = function(field, value) {
/** /**
* Writes a message to the buffer. * Writes a message to the buffer.
* @template MessageType
* @param {number} field The field number. * @param {number} field The field number.
* @param {?MessageType} value The message to write. * @param {?MessageType} value The message to write.
* @param {!jspb.WriterFunction} writerCallback Will be invoked with the value * @param {function(MessageTypeNonNull, !jspb.BinaryWriter)} writerCallback
* to write and the writer to write it with. * Will be invoked with the value to write and the writer to write it with.
* @template MessageType
* Use go/closure-ttl to declare a non-nullable version of MessageType. Replace
* the null in blah|null with none. This is necessary because the compiler will
* infer MessageType to be nullable if the value parameter is nullable.
* @template MessageTypeNonNull :=
* cond(isUnknown(MessageType), unknown(),
* mapunion(MessageType, (X) =>
* cond(eq(X, 'null'), none(), X)))
* =:
*/ */
jspb.BinaryWriter.prototype.writeMessage = function( jspb.BinaryWriter.prototype.writeMessage = function(
field, value, writerCallback) { field, value, writerCallback) {
@ -735,12 +743,20 @@ jspb.BinaryWriter.prototype.writeMessage = function(
/** /**
* Writes a group message to the buffer. * Writes a group message to the buffer.
* *
* @template MessageType
* @param {number} field The field number. * @param {number} field The field number.
* @param {?MessageType} value The message to write, wrapped with START_GROUP / * @param {?MessageType} value The message to write, wrapped with START_GROUP /
* END_GROUP tags. Will be a no-op if 'value' is null. * END_GROUP tags. Will be a no-op if 'value' is null.
* @param {!jspb.WriterFunction} writerCallback Will be invoked with the value * @param {function(MessageTypeNonNull, !jspb.BinaryWriter)} writerCallback
* to write and the writer to write it with. * Will be invoked with the value to write and the writer to write it with.
* @template MessageType
* Use go/closure-ttl to declare a non-nullable version of MessageType. Replace
* the null in blah|null with none. This is necessary because the compiler will
* infer MessageType to be nullable if the value parameter is nullable.
* @template MessageTypeNonNull :=
* cond(isUnknown(MessageType), unknown(),
* mapunion(MessageType, (X) =>
* cond(eq(X, 'null'), none(), X)))
* =:
*/ */
jspb.BinaryWriter.prototype.writeGroup = function( jspb.BinaryWriter.prototype.writeGroup = function(
field, value, writerCallback) { field, value, writerCallback) {
@ -1122,8 +1138,8 @@ jspb.BinaryWriter.prototype.writeRepeatedBytes = function(field, value) {
* @param {number} field The field number. * @param {number} field The field number.
* @param {?Array.<MessageType>} value The array of messages to * @param {?Array.<MessageType>} value The array of messages to
* write. * write.
* @param {!jspb.WriterFunction} writerCallback Will be invoked with the value * @param {function(MessageType, !jspb.BinaryWriter)} writerCallback
* to write and the writer to write it with. * Will be invoked with the value to write and the writer to write it with.
*/ */
jspb.BinaryWriter.prototype.writeRepeatedMessage = function( jspb.BinaryWriter.prototype.writeRepeatedMessage = function(
field, value, writerCallback) { field, value, writerCallback) {
@ -1142,8 +1158,8 @@ jspb.BinaryWriter.prototype.writeRepeatedMessage = function(
* @param {number} field The field number. * @param {number} field The field number.
* @param {?Array.<MessageType>} value The array of messages to * @param {?Array.<MessageType>} value The array of messages to
* write. * write.
* @param {!jspb.WriterFunction} writerCallback Will be invoked with the value * @param {function(MessageType, !jspb.BinaryWriter)} writerCallback
* to write and the writer to write it with. * Will be invoked with the value to write and the writer to write it with.
*/ */
jspb.BinaryWriter.prototype.writeRepeatedGroup = function( jspb.BinaryWriter.prototype.writeRepeatedGroup = function(
field, value, writerCallback) { field, value, writerCallback) {

@ -41,6 +41,7 @@ goog.require('goog.array');
goog.require('goog.asserts'); goog.require('goog.asserts');
goog.require('goog.crypt.base64'); goog.require('goog.crypt.base64');
goog.require('goog.json'); goog.require('goog.json');
goog.require('jspb.Map');
// Not needed in compilation units that have no protos with xids. // Not needed in compilation units that have no protos with xids.
goog.forwardDeclare('xid.String'); goog.forwardDeclare('xid.String');
@ -371,7 +372,8 @@ jspb.Message.materializeExtensionObject_ = function(msg, suggestedPivot) {
// the object is not an array, since arrays are valid field values. // the object is not an array, since arrays are valid field values.
// NOTE(lukestebbing): We avoid looking at .length to avoid a JIT bug // NOTE(lukestebbing): We avoid looking at .length to avoid a JIT bug
// in Safari on iOS 8. See the description of CL/86511464 for details. // in Safari on iOS 8. See the description of CL/86511464 for details.
if (obj && typeof obj == 'object' && !goog.isArray(obj)) { if (obj && typeof obj == 'object' && !goog.isArray(obj) &&
!(jspb.Message.SUPPORTS_UINT8ARRAY_ && obj instanceof Uint8Array)) {
msg.pivot_ = foundIndex - msg.arrayIndexOffset_; msg.pivot_ = foundIndex - msg.arrayIndexOffset_;
msg.extensionObject_ = obj; msg.extensionObject_ = obj;
return; return;
@ -737,6 +739,62 @@ jspb.Message.getFieldProto3 = function(msg, fieldNumber, defaultValue) {
}; };
/**
* Gets the value of a map field, lazily creating the map container if
* necessary.
*
* This should only be called from generated code, because it requires knowledge
* of serialization/parsing callbacks (which are required by the map at
* construction time, and the map may be constructed here).
*
* The below callbacks are used to allow the map to serialize and parse its
* binary wire format data. Their purposes are described in more detail in
* `jspb.Map`'s constructor documentation.
*
* @template K, V
* @param {!jspb.Message} msg
* @param {number} fieldNumber
* @param {boolean|undefined} noLazyCreate
* @param {?=} opt_valueCtor
* @param {function(number,K)=} opt_keyWriterFn
* @param {function():K=} opt_keyReaderFn
* @param {function(number,V)|function(number,V,?)|
* function(number,V,?,?,?,?)=} opt_valueWriterFn
* @param {function():V|
* function(V,function(?,?))=} opt_valueReaderFn
* @param {function(?,?)|function(?,?,?,?,?)=} opt_valueWriterCallback
* @param {function(?,?)=} opt_valueReaderCallback
* @return {!jspb.Map<K, V>|undefined}
* @protected
*/
jspb.Message.getMapField = function(msg, fieldNumber, noLazyCreate,
opt_valueCtor, opt_keyWriterFn, opt_keyReaderFn, opt_valueWriterFn,
opt_valueReaderFn, opt_valueWriterCallback, opt_valueReaderCallback) {
if (!msg.wrappers_) {
msg.wrappers_ = {};
}
// If we already have a map in the map wrappers, return that.
if (fieldNumber in msg.wrappers_) {
return msg.wrappers_[fieldNumber];
} else if (noLazyCreate) {
return undefined;
} else {
// Wrap the underlying elements array with a Map.
var arr = jspb.Message.getField(msg, fieldNumber);
if (!arr) {
arr = [];
jspb.Message.setField(msg, fieldNumber, arr);
}
return msg.wrappers_[fieldNumber] =
new jspb.Map(
/** @type {!Array<!Array<!Object>>} */ (arr),
opt_keyWriterFn, opt_keyReaderFn, opt_valueWriterFn,
opt_valueReaderFn, opt_valueCtor, opt_valueWriterCallback,
opt_valueReaderCallback);
}
};
/** /**
* Sets the value of a non-extension field. * Sets the value of a non-extension field.
* @param {!jspb.Message} msg A jspb proto. * @param {!jspb.Message} msg A jspb proto.
@ -952,6 +1010,38 @@ jspb.Message.toMap = function(
}; };
/**
* Syncs all map fields' contents back to their underlying arrays.
* @private
*/
jspb.Message.prototype.syncMapFields_ = function() {
// This iterates over submessage, map, and repeated fields, which is intended.
// Submessages can contain maps which also need to be synced.
//
// There is a lot of opportunity for optimization here. For example we could
// statically determine that some messages have no submessages with maps and
// optimize this method away for those just by generating one extra static
// boolean per message type.
if (this.wrappers_) {
for (var fieldNumber in this.wrappers_) {
var val = this.wrappers_[fieldNumber];
if (goog.isArray(val)) {
for (var i = 0; i < val.length; i++) {
if (val[i]) {
val[i].toArray();
}
}
} else {
// Works for submessages and maps.
if (val) {
val.toArray();
}
}
}
}
};
/** /**
* Returns the internal array of this proto. * Returns the internal array of this proto.
* <p>Note: If you use this array to construct a second proto, the content * <p>Note: If you use this array to construct a second proto, the content
@ -959,6 +1049,7 @@ jspb.Message.toMap = function(
* @return {!Array} The proto represented as an array. * @return {!Array} The proto represented as an array.
*/ */
jspb.Message.prototype.toArray = function() { jspb.Message.prototype.toArray = function() {
this.syncMapFields_();
return this.array; return this.array;
}; };
@ -972,6 +1063,7 @@ jspb.Message.prototype.toArray = function() {
* @override * @override
*/ */
jspb.Message.prototype.toString = function() { jspb.Message.prototype.toString = function() {
this.syncMapFields_();
return this.array.toString(); return this.array.toString();
}; };
@ -1293,6 +1385,9 @@ jspb.Message.clone_ = function(obj) {
} }
return clonedArray; return clonedArray;
} }
if (jspb.Message.SUPPORTS_UINT8ARRAY_ && obj instanceof Uint8Array) {
return new Uint8Array(obj);
}
var clone = {}; var clone = {};
for (var key in obj) { for (var key in obj) {
if ((o = obj[key]) != null) { if ((o = obj[key]) != null) {

@ -34,6 +34,7 @@ goog.setTestOnly();
goog.require('goog.json'); goog.require('goog.json');
goog.require('goog.testing.asserts'); goog.require('goog.testing.asserts');
goog.require('goog.userAgent');
// CommonJS-LoadFromFile: google-protobuf jspb // CommonJS-LoadFromFile: google-protobuf jspb
goog.require('jspb.Message'); goog.require('jspb.Message');
@ -66,6 +67,7 @@ goog.require('proto.jspb.test.Simple1');
goog.require('proto.jspb.test.Simple2'); goog.require('proto.jspb.test.Simple2');
goog.require('proto.jspb.test.SpecialCases'); goog.require('proto.jspb.test.SpecialCases');
goog.require('proto.jspb.test.TestClone'); goog.require('proto.jspb.test.TestClone');
goog.require('proto.jspb.test.TestEndsWithBytes');
goog.require('proto.jspb.test.TestGroup'); goog.require('proto.jspb.test.TestGroup');
goog.require('proto.jspb.test.TestGroup1'); goog.require('proto.jspb.test.TestGroup1');
goog.require('proto.jspb.test.TestMessageWithOneof'); goog.require('proto.jspb.test.TestMessageWithOneof');
@ -438,6 +440,8 @@ describe('Message test suite', function() {
}); });
it('testClone', function() { it('testClone', function() {
var supportsUint8Array =
!goog.userAgent.IE || goog.userAgent.isVersionOrHigher('10');
var original = new proto.jspb.test.TestClone(); var original = new proto.jspb.test.TestClone();
original.setStr('v1'); original.setStr('v1');
var simple1 = new proto.jspb.test.Simple1(['x1', ['y1', 'z1']]); var simple1 = new proto.jspb.test.Simple1(['x1', ['y1', 'z1']]);
@ -445,12 +449,14 @@ describe('Message test suite', function() {
var simple3 = new proto.jspb.test.Simple1(['x3', ['y3', 'z3']]); var simple3 = new proto.jspb.test.Simple1(['x3', ['y3', 'z3']]);
original.setSimple1(simple1); original.setSimple1(simple1);
original.setSimple2List([simple2, simple3]); original.setSimple2List([simple2, simple3]);
var bytes1 = supportsUint8Array ? new Uint8Array([1, 2, 3]) : '123';
original.setBytesField(bytes1);
var extension = new proto.jspb.test.CloneExtension(); var extension = new proto.jspb.test.CloneExtension();
extension.setExt('e1'); extension.setExt('e1');
original.setExtension(proto.jspb.test.IsExtension.extField, extension); original.setExtension(proto.jspb.test.IsExtension.extField, extension);
var clone = original.cloneMessage(); var clone = original.cloneMessage();
assertArrayEquals(['v1',, ['x1', ['y1', 'z1']],, assertArrayEquals(['v1',, ['x1', ['y1', 'z1']],,
[['x2', ['y2', 'z2']], ['x3', ['y3', 'z3']]],,, { 100: [, 'e1'] }], [['x2', ['y2', 'z2']], ['x3', ['y3', 'z3']]], bytes1,, { 100: [, 'e1'] }],
clone.toArray()); clone.toArray());
clone.setStr('v2'); clone.setStr('v2');
var simple4 = new proto.jspb.test.Simple1(['a1', ['b1', 'c1']]); var simple4 = new proto.jspb.test.Simple1(['a1', ['b1', 'c1']]);
@ -458,18 +464,26 @@ describe('Message test suite', function() {
var simple6 = new proto.jspb.test.Simple1(['a3', ['b3', 'c3']]); var simple6 = new proto.jspb.test.Simple1(['a3', ['b3', 'c3']]);
clone.setSimple1(simple4); clone.setSimple1(simple4);
clone.setSimple2List([simple5, simple6]); clone.setSimple2List([simple5, simple6]);
if (supportsUint8Array) {
clone.getBytesField()[0] = 4;
assertObjectEquals(bytes1, original.getBytesField());
}
var bytes2 = supportsUint8Array ? new Uint8Array([4, 5, 6]) : '456';
clone.setBytesField(bytes2);
var newExtension = new proto.jspb.test.CloneExtension(); var newExtension = new proto.jspb.test.CloneExtension();
newExtension.setExt('e2'); newExtension.setExt('e2');
clone.setExtension(proto.jspb.test.CloneExtension.extField, newExtension); clone.setExtension(proto.jspb.test.CloneExtension.extField, newExtension);
assertArrayEquals(['v2',, ['a1', ['b1', 'c1']],, assertArrayEquals(['v2',, ['a1', ['b1', 'c1']],,
[['a2', ['b2', 'c2']], ['a3', ['b3', 'c3']]],,, { 100: [, 'e2'] }], [['a2', ['b2', 'c2']], ['a3', ['b3', 'c3']]], bytes2,, { 100: [, 'e2'] }],
clone.toArray()); clone.toArray());
assertArrayEquals(['v1',, ['x1', ['y1', 'z1']],, assertArrayEquals(['v1',, ['x1', ['y1', 'z1']],,
[['x2', ['y2', 'z2']], ['x3', ['y3', 'z3']]],,, { 100: [, 'e1'] }], [['x2', ['y2', 'z2']], ['x3', ['y3', 'z3']]], bytes1,, { 100: [, 'e1'] }],
original.toArray()); original.toArray());
}); });
it('testCopyInto', function() { it('testCopyInto', function() {
var supportsUint8Array =
!goog.userAgent.IE || goog.userAgent.isVersionOrHigher('10');
var original = new proto.jspb.test.TestClone(); var original = new proto.jspb.test.TestClone();
original.setStr('v1'); original.setStr('v1');
var dest = new proto.jspb.test.TestClone(); var dest = new proto.jspb.test.TestClone();
@ -484,6 +498,10 @@ describe('Message test suite', function() {
original.setSimple2List([simple2, simple3]); original.setSimple2List([simple2, simple3]);
dest.setSimple1(destSimple1); dest.setSimple1(destSimple1);
dest.setSimple2List([destSimple2, destSimple3]); dest.setSimple2List([destSimple2, destSimple3]);
var bytes1 = supportsUint8Array ? new Uint8Array([1, 2, 3]) : '123';
var bytes2 = supportsUint8Array ? new Uint8Array([4, 5, 6]) : '456';
original.setBytesField(bytes1);
dest.setBytesField(bytes2);
var extension = new proto.jspb.test.CloneExtension(); var extension = new proto.jspb.test.CloneExtension();
extension.setExt('e1'); extension.setExt('e1');
original.setExtension(proto.jspb.test.CloneExtension.extField, extension); original.setExtension(proto.jspb.test.CloneExtension.extField, extension);
@ -496,6 +514,15 @@ describe('Message test suite', function() {
dest.getSimple1().setAString('new value'); dest.getSimple1().setAString('new value');
assertNotEquals(dest.getSimple1().getAString(), assertNotEquals(dest.getSimple1().getAString(),
original.getSimple1().getAString()); original.getSimple1().getAString());
if (supportsUint8Array) {
dest.getBytesField()[0] = 7;
assertObjectEquals(bytes1, original.getBytesField());
assertObjectEquals(new Uint8Array([7, 2, 3]), dest.getBytesField());
} else {
dest.setBytesField('789');
assertObjectEquals(bytes1, original.getBytesField());
assertObjectEquals('789', dest.getBytesField());
}
dest.getExtension(proto.jspb.test.CloneExtension.extField). dest.getExtension(proto.jspb.test.CloneExtension.extField).
setExt('new value'); setExt('new value');
assertNotEquals( assertNotEquals(

@ -160,6 +160,7 @@ message TestClone {
optional string str = 1; optional string str = 1;
optional Simple1 simple1 = 3; optional Simple1 simple1 = 3;
repeated Simple1 simple2 = 5; repeated Simple1 simple2 = 5;
optional bytes bytes_field = 6;
optional string unused = 7; optional string unused = 7;
extensions 10 to max; extensions 10 to max;
} }

@ -183,3 +183,32 @@ extend TestExtendable {
[packed=true]; [packed=true];
} }
message TestMapFields {
option (jspb.generate_from_object) = true;
map<string, string> map_string_string = 1;
map<string, int32> map_string_int32 = 2;
map<string, int64> map_string_int64 = 3;
map<string, bool> map_string_bool = 4;
map<string, double> map_string_double = 5;
map<string, MapValueEnum> map_string_enum = 6;
map<string, MapValueMessage> map_string_msg = 7;
map<int32, string> map_int32_string = 8;
map<int64, string> map_int64_string = 9;
map<bool, string> map_bool_string = 10;
optional TestMapFields test_map_fields = 11;
map<string, TestMapFields> map_string_testmapfields = 12;
}
enum MapValueEnum {
MAP_VALUE_FOO = 0;
MAP_VALUE_BAR = 1;
MAP_VALUE_BAZ = 2;
}
message MapValueMessage {
optional int32 foo = 1;
}

@ -258,7 +258,7 @@ class Descriptor(_NestedDescriptorBase):
def __new__(cls, name, full_name, filename, containing_type, fields, def __new__(cls, name, full_name, filename, containing_type, fields,
nested_types, enum_types, extensions, options=None, nested_types, enum_types, extensions, options=None,
is_extendable=True, extension_ranges=None, oneofs=None, is_extendable=True, extension_ranges=None, oneofs=None,
file=None, serialized_start=None, serialized_end=None, file=None, serialized_start=None, serialized_end=None, # pylint: disable=redefined-builtin
syntax=None): syntax=None):
_message.Message._CheckCalledFromGeneratedFile() _message.Message._CheckCalledFromGeneratedFile()
return _message.default_pool.FindMessageTypeByName(full_name) return _message.default_pool.FindMessageTypeByName(full_name)
@ -269,8 +269,8 @@ class Descriptor(_NestedDescriptorBase):
def __init__(self, name, full_name, filename, containing_type, fields, def __init__(self, name, full_name, filename, containing_type, fields,
nested_types, enum_types, extensions, options=None, nested_types, enum_types, extensions, options=None,
is_extendable=True, extension_ranges=None, oneofs=None, is_extendable=True, extension_ranges=None, oneofs=None,
file=None, serialized_start=None, serialized_end=None, file=None, serialized_start=None, serialized_end=None, # pylint: disable=redefined-builtin
syntax=None): # pylint:disable=redefined-builtin syntax=None):
"""Arguments to __init__() are as described in the description """Arguments to __init__() are as described in the description
of Descriptor fields above. of Descriptor fields above.
@ -665,7 +665,7 @@ class EnumValueDescriptor(DescriptorBase):
self.type = type self.type = type
class OneofDescriptor(object): class OneofDescriptor(DescriptorBase):
"""Descriptor for a oneof field. """Descriptor for a oneof field.
name: (str) Name of the oneof field. name: (str) Name of the oneof field.
@ -682,12 +682,15 @@ class OneofDescriptor(object):
if _USE_C_DESCRIPTORS: if _USE_C_DESCRIPTORS:
_C_DESCRIPTOR_CLASS = _message.OneofDescriptor _C_DESCRIPTOR_CLASS = _message.OneofDescriptor
def __new__(cls, name, full_name, index, containing_type, fields): def __new__(
cls, name, full_name, index, containing_type, fields, options=None):
_message.Message._CheckCalledFromGeneratedFile() _message.Message._CheckCalledFromGeneratedFile()
return _message.default_pool.FindOneofByName(full_name) return _message.default_pool.FindOneofByName(full_name)
def __init__(self, name, full_name, index, containing_type, fields): def __init__(
self, name, full_name, index, containing_type, fields, options=None):
"""Arguments are as described in the attribute description above.""" """Arguments are as described in the attribute description above."""
super(OneofDescriptor, self).__init__(options, 'OneofOptions')
self.name = name self.name = name
self.full_name = full_name self.full_name = full_name
self.index = index self.index = index
@ -705,11 +708,22 @@ class ServiceDescriptor(_NestedDescriptorBase):
definition appears withing the .proto file. definition appears withing the .proto file.
methods: (list of MethodDescriptor) List of methods provided by this methods: (list of MethodDescriptor) List of methods provided by this
service. service.
methods_by_name: (dict str -> MethodDescriptor) Same MethodDescriptor
objects as in |methods_by_name|, but indexed by "name" attribute in each
MethodDescriptor.
options: (descriptor_pb2.ServiceOptions) Service options message or options: (descriptor_pb2.ServiceOptions) Service options message or
None to use default service options. None to use default service options.
file: (FileDescriptor) Reference to file info. file: (FileDescriptor) Reference to file info.
""" """
if _USE_C_DESCRIPTORS:
_C_DESCRIPTOR_CLASS = _message.ServiceDescriptor
def __new__(cls, name, full_name, index, methods, options=None, file=None, # pylint: disable=redefined-builtin
serialized_start=None, serialized_end=None):
_message.Message._CheckCalledFromGeneratedFile() # pylint: disable=protected-access
return _message.default_pool.FindServiceByName(full_name)
def __init__(self, name, full_name, index, methods, options=None, file=None, def __init__(self, name, full_name, index, methods, options=None, file=None,
serialized_start=None, serialized_end=None): serialized_start=None, serialized_end=None):
super(ServiceDescriptor, self).__init__( super(ServiceDescriptor, self).__init__(
@ -718,16 +732,14 @@ class ServiceDescriptor(_NestedDescriptorBase):
serialized_end=serialized_end) serialized_end=serialized_end)
self.index = index self.index = index
self.methods = methods self.methods = methods
self.methods_by_name = dict((m.name, m) for m in methods)
# Set the containing service for each method in this service. # Set the containing service for each method in this service.
for method in self.methods: for method in self.methods:
method.containing_service = self method.containing_service = self
def FindMethodByName(self, name): def FindMethodByName(self, name):
"""Searches for the specified method, and returns its descriptor.""" """Searches for the specified method, and returns its descriptor."""
for method in self.methods: return self.methods_by_name.get(name, None)
if name == method.name:
return method
return None
def CopyToProto(self, proto): def CopyToProto(self, proto):
"""Copies this to a descriptor_pb2.ServiceDescriptorProto. """Copies this to a descriptor_pb2.ServiceDescriptorProto.
@ -754,6 +766,14 @@ class MethodDescriptor(DescriptorBase):
None to use default method options. None to use default method options.
""" """
if _USE_C_DESCRIPTORS:
_C_DESCRIPTOR_CLASS = _message.MethodDescriptor
def __new__(cls, name, full_name, index, containing_service,
input_type, output_type, options=None):
_message.Message._CheckCalledFromGeneratedFile() # pylint: disable=protected-access
return _message.default_pool.FindMethodByName(full_name)
def __init__(self, name, full_name, index, containing_service, def __init__(self, name, full_name, index, containing_service,
input_type, output_type, options=None): input_type, output_type, options=None):
"""The arguments are as described in the description of MethodDescriptor """The arguments are as described in the description of MethodDescriptor
@ -788,6 +808,7 @@ class FileDescriptor(DescriptorBase):
message_types_by_name: Dict of message names of their descriptors. message_types_by_name: Dict of message names of their descriptors.
enum_types_by_name: Dict of enum names and their descriptors. enum_types_by_name: Dict of enum names and their descriptors.
extensions_by_name: Dict of extension names and their descriptors. extensions_by_name: Dict of extension names and their descriptors.
services_by_name: Dict of services names and their descriptors.
pool: the DescriptorPool this descriptor belongs to. When not passed to the pool: the DescriptorPool this descriptor belongs to. When not passed to the
constructor, the global default pool is used. constructor, the global default pool is used.
""" """
@ -825,6 +846,7 @@ class FileDescriptor(DescriptorBase):
self.enum_types_by_name = {} self.enum_types_by_name = {}
self.extensions_by_name = {} self.extensions_by_name = {}
self.services_by_name = {}
self.dependencies = (dependencies or []) self.dependencies = (dependencies or [])
self.public_dependencies = (public_dependencies or []) self.public_dependencies = (public_dependencies or [])

@ -394,6 +394,11 @@ class DescriptorPool(object):
desc_proto_prefix, desc_proto.name, scope) desc_proto_prefix, desc_proto.name, scope)
file_descriptor.message_types_by_name[desc_proto.name] = desc file_descriptor.message_types_by_name[desc_proto.name] = desc
for index, service_proto in enumerate(file_proto.service):
file_descriptor.services_by_name[service_proto.name] = (
self._MakeServiceDescriptor(service_proto, index, scope,
file_proto.package, file_descriptor))
self.Add(file_proto) self.Add(file_proto)
self._file_descriptors[file_proto.name] = file_descriptor self._file_descriptors[file_proto.name] = file_descriptor
@ -441,7 +446,7 @@ class DescriptorPool(object):
for index, extension in enumerate(desc_proto.extension)] for index, extension in enumerate(desc_proto.extension)]
oneofs = [ oneofs = [
descriptor.OneofDescriptor(desc.name, '.'.join((desc_name, desc.name)), descriptor.OneofDescriptor(desc.name, '.'.join((desc_name, desc.name)),
index, None, []) index, None, [], desc.options)
for index, desc in enumerate(desc_proto.oneof_decl)] for index, desc in enumerate(desc_proto.oneof_decl)]
extension_ranges = [(r.start, r.end) for r in desc_proto.extension_range] extension_ranges = [(r.start, r.end) for r in desc_proto.extension_range]
if extension_ranges: if extension_ranges:
@ -679,6 +684,64 @@ class DescriptorPool(object):
options=value_proto.options, options=value_proto.options,
type=None) type=None)
def _MakeServiceDescriptor(self, service_proto, service_index, scope,
package, file_desc):
"""Make a protobuf ServiceDescriptor given a ServiceDescriptorProto.
Args:
service_proto: The descriptor_pb2.ServiceDescriptorProto protobuf message.
service_index: The index of the service in the File.
scope: Dict mapping short and full symbols to message and enum types.
package: Optional package name for the new message EnumDescriptor.
file_desc: The file containing the service descriptor.
Returns:
The added descriptor.
"""
if package:
service_name = '.'.join((package, service_proto.name))
else:
service_name = service_proto.name
methods = [self._MakeMethodDescriptor(method_proto, service_name, package,
scope, index)
for index, method_proto in enumerate(service_proto.method)]
desc = descriptor.ServiceDescriptor(name=service_proto.name,
full_name=service_name,
index=service_index,
methods=methods,
options=service_proto.options,
file=file_desc)
return desc
def _MakeMethodDescriptor(self, method_proto, service_name, package, scope,
index):
"""Creates a method descriptor from a MethodDescriptorProto.
Args:
method_proto: The proto describing the method.
service_name: The name of the containing service.
package: Optional package name to look up for types.
scope: Scope containing available types.
index: Index of the method in the service.
Returns:
An initialized MethodDescriptor object.
"""
full_name = '.'.join((service_name, method_proto.name))
input_type = self._GetTypeFromScope(
package, method_proto.input_type, scope)
output_type = self._GetTypeFromScope(
package, method_proto.output_type, scope)
return descriptor.MethodDescriptor(name=method_proto.name,
full_name=full_name,
index=index,
containing_service=None,
input_type=input_type,
output_type=output_type,
options=method_proto.options)
def _ExtractSymbols(self, descriptors): def _ExtractSymbols(self, descriptors):
"""Pulls out all the symbols from descriptor protos. """Pulls out all the symbols from descriptor protos.

@ -594,7 +594,11 @@ class MessageMap(MutableMapping):
def MergeFrom(self, other): def MergeFrom(self, other):
for key in other: for key in other:
self[key].MergeFrom(other[key]) # According to documentation: "When parsing from the wire or when merging,
# if there are duplicate map keys the last key seen is used".
if key in self:
del self[key]
self[key].CopyFrom(other[key])
# self._message_listener.Modified() not required here, because # self._message_listener.Modified() not required here, because
# mutations to submessages already propagate. # mutations to submessages already propagate.

@ -51,6 +51,7 @@ from google.protobuf.internal import descriptor_pool_test1_pb2
from google.protobuf.internal import descriptor_pool_test2_pb2 from google.protobuf.internal import descriptor_pool_test2_pb2
from google.protobuf.internal import factory_test1_pb2 from google.protobuf.internal import factory_test1_pb2
from google.protobuf.internal import factory_test2_pb2 from google.protobuf.internal import factory_test2_pb2
from google.protobuf.internal import file_options_test_pb2
from google.protobuf.internal import more_messages_pb2 from google.protobuf.internal import more_messages_pb2
from google.protobuf import descriptor from google.protobuf import descriptor
from google.protobuf import descriptor_database from google.protobuf import descriptor_database
@ -630,6 +631,23 @@ class AddDescriptorTest(unittest.TestCase):
self.assertEqual(pool.FindMessageTypeByName('package.Message').name, self.assertEqual(pool.FindMessageTypeByName('package.Message').name,
'Message') 'Message')
def testFileDescriptorOptionsWithCustomDescriptorPool(self):
# Create a descriptor pool, and add a new FileDescriptorProto to it.
pool = descriptor_pool.DescriptorPool()
file_name = 'file_descriptor_options_with_custom_descriptor_pool.proto'
file_descriptor_proto = descriptor_pb2.FileDescriptorProto(name=file_name)
extension_id = file_options_test_pb2.foo_options
file_descriptor_proto.options.Extensions[extension_id].foo_name = 'foo'
pool.Add(file_descriptor_proto)
# The options set on the FileDescriptorProto should be available in the
# descriptor even if they contain extensions that cannot be deserialized
# using the pool.
file_descriptor = pool.FindFileByName(file_name)
options = file_descriptor.GetOptions()
self.assertEqual('foo', options.Extensions[extension_id].foo_name)
# The object returned by GetOptions() is cached.
self.assertIs(options, file_descriptor.GetOptions())
@unittest.skipIf( @unittest.skipIf(
api_implementation.Type() != 'cpp', api_implementation.Type() != 'cpp',

@ -77,27 +77,24 @@ class DescriptorTest(unittest.TestCase):
enum_proto.value.add(name='FOREIGN_BAR', number=5) enum_proto.value.add(name='FOREIGN_BAR', number=5)
enum_proto.value.add(name='FOREIGN_BAZ', number=6) enum_proto.value.add(name='FOREIGN_BAZ', number=6)
file_proto.message_type.add(name='ResponseMessage')
service_proto = file_proto.service.add(
name='Service')
method_proto = service_proto.method.add(
name='CallMethod',
input_type='.protobuf_unittest.NestedMessage',
output_type='.protobuf_unittest.ResponseMessage')
# Note: Calling DescriptorPool.Add() multiple times with the same file only
# works if the input is canonical; in particular, all type names must be
# fully qualified.
self.pool = self.GetDescriptorPool() self.pool = self.GetDescriptorPool()
self.pool.Add(file_proto) self.pool.Add(file_proto)
self.my_file = self.pool.FindFileByName(file_proto.name) self.my_file = self.pool.FindFileByName(file_proto.name)
self.my_message = self.my_file.message_types_by_name[message_proto.name] self.my_message = self.my_file.message_types_by_name[message_proto.name]
self.my_enum = self.my_message.enum_types_by_name[enum_proto.name] self.my_enum = self.my_message.enum_types_by_name[enum_proto.name]
self.my_service = self.my_file.services_by_name[service_proto.name]
self.my_method = descriptor.MethodDescriptor( self.my_method = self.my_service.methods_by_name[method_proto.name]
name='Bar',
full_name='protobuf_unittest.TestService.Bar',
index=0,
containing_service=None,
input_type=None,
output_type=None)
self.my_service = descriptor.ServiceDescriptor(
name='TestServiceWithOptions',
full_name='protobuf_unittest.TestServiceWithOptions',
file=self.my_file,
index=0,
methods=[
self.my_method
])
def GetDescriptorPool(self): def GetDescriptorPool(self):
return symbol_database.Default().pool return symbol_database.Default().pool
@ -139,13 +136,14 @@ class DescriptorTest(unittest.TestCase):
file_descriptor = unittest_custom_options_pb2.DESCRIPTOR file_descriptor = unittest_custom_options_pb2.DESCRIPTOR
message_descriptor =\ message_descriptor =\
unittest_custom_options_pb2.TestMessageWithCustomOptions.DESCRIPTOR unittest_custom_options_pb2.TestMessageWithCustomOptions.DESCRIPTOR
field_descriptor = message_descriptor.fields_by_name["field1"] field_descriptor = message_descriptor.fields_by_name['field1']
enum_descriptor = message_descriptor.enum_types_by_name["AnEnum"] oneof_descriptor = message_descriptor.oneofs_by_name['AnOneof']
enum_descriptor = message_descriptor.enum_types_by_name['AnEnum']
enum_value_descriptor =\ enum_value_descriptor =\
message_descriptor.enum_values_by_name["ANENUM_VAL2"] message_descriptor.enum_values_by_name['ANENUM_VAL2']
service_descriptor =\ service_descriptor =\
unittest_custom_options_pb2.TestServiceWithCustomOptions.DESCRIPTOR unittest_custom_options_pb2.TestServiceWithCustomOptions.DESCRIPTOR
method_descriptor = service_descriptor.FindMethodByName("Foo") method_descriptor = service_descriptor.FindMethodByName('Foo')
file_options = file_descriptor.GetOptions() file_options = file_descriptor.GetOptions()
file_opt1 = unittest_custom_options_pb2.file_opt1 file_opt1 = unittest_custom_options_pb2.file_opt1
@ -158,6 +156,9 @@ class DescriptorTest(unittest.TestCase):
self.assertEqual(8765432109, field_options.Extensions[field_opt1]) self.assertEqual(8765432109, field_options.Extensions[field_opt1])
field_opt2 = unittest_custom_options_pb2.field_opt2 field_opt2 = unittest_custom_options_pb2.field_opt2
self.assertEqual(42, field_options.Extensions[field_opt2]) self.assertEqual(42, field_options.Extensions[field_opt2])
oneof_options = oneof_descriptor.GetOptions()
oneof_opt1 = unittest_custom_options_pb2.oneof_opt1
self.assertEqual(-99, oneof_options.Extensions[oneof_opt1])
enum_options = enum_descriptor.GetOptions() enum_options = enum_descriptor.GetOptions()
enum_opt1 = unittest_custom_options_pb2.enum_opt1 enum_opt1 = unittest_custom_options_pb2.enum_opt1
self.assertEqual(-789, enum_options.Extensions[enum_opt1]) self.assertEqual(-789, enum_options.Extensions[enum_opt1])

@ -0,0 +1,43 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto2";
import "google/protobuf/descriptor.proto";
package google.protobuf.python.internal;
message FooOptions {
optional string foo_name = 1;
}
extend .google.protobuf.FileOptions {
optional FooOptions foo_options = 120436268;
}

@ -643,6 +643,19 @@ class JsonFormatTest(JsonFormatBase):
'Message type "proto3.TestMessage" has no field named ' 'Message type "proto3.TestMessage" has no field named '
'"unknownName".') '"unknownName".')
def testIgnoreUnknownField(self):
text = '{"unknownName": 1}'
parsed_message = json_format_proto3_pb2.TestMessage()
json_format.Parse(text, parsed_message, ignore_unknown_fields=True)
text = ('{\n'
' "repeatedValue": [ {\n'
' "@type": "type.googleapis.com/proto3.MessageType",\n'
' "unknownName": 1\n'
' }]\n'
'}\n')
parsed_message = json_format_proto3_pb2.TestAny()
json_format.Parse(text, parsed_message, ignore_unknown_fields=True)
def testDuplicateField(self): def testDuplicateField(self):
# Duplicate key check is not supported for python2.6 # Duplicate key check is not supported for python2.6
if sys.version_info < (2, 7): if sys.version_info < (2, 7):

@ -1435,6 +1435,8 @@ class Proto3Test(unittest.TestCase):
msg2.map_int32_int32[12] = 55 msg2.map_int32_int32[12] = 55
msg2.map_int64_int64[88] = 99 msg2.map_int64_int64[88] = 99
msg2.map_int32_foreign_message[222].c = 15 msg2.map_int32_foreign_message[222].c = 15
msg2.map_int32_foreign_message[222].d = 20
old_map_value = msg2.map_int32_foreign_message[222]
msg2.MergeFrom(msg) msg2.MergeFrom(msg)
@ -1444,6 +1446,8 @@ class Proto3Test(unittest.TestCase):
self.assertEqual(99, msg2.map_int64_int64[88]) self.assertEqual(99, msg2.map_int64_int64[88])
self.assertEqual(5, msg2.map_int32_foreign_message[111].c) self.assertEqual(5, msg2.map_int32_foreign_message[111].c)
self.assertEqual(10, msg2.map_int32_foreign_message[222].c) self.assertEqual(10, msg2.map_int32_foreign_message[222].c)
self.assertFalse(msg2.map_int32_foreign_message[222].HasField('d'))
self.assertEqual(15, old_map_value.c)
# Verify that there is only one entry per key, even though the MergeFrom # Verify that there is only one entry per key, even though the MergeFrom
# may have internally created multiple entries for a single key in the # may have internally created multiple entries for a single key in the

@ -40,12 +40,13 @@ import six
import string import string
try: try:
import unittest2 as unittest #PY26 import unittest2 as unittest # PY26, pylint: disable=g-import-not-at-top
except ImportError: except ImportError:
import unittest import unittest # pylint: disable=g-import-not-at-top
from google.protobuf.internal import _parameterized from google.protobuf.internal import _parameterized
from google.protobuf import any_test_pb2
from google.protobuf import map_unittest_pb2 from google.protobuf import map_unittest_pb2
from google.protobuf import unittest_mset_pb2 from google.protobuf import unittest_mset_pb2
from google.protobuf import unittest_pb2 from google.protobuf import unittest_pb2
@ -53,6 +54,7 @@ from google.protobuf import unittest_proto3_arena_pb2
from google.protobuf.internal import api_implementation from google.protobuf.internal import api_implementation
from google.protobuf.internal import test_util from google.protobuf.internal import test_util
from google.protobuf.internal import message_set_extensions_pb2 from google.protobuf.internal import message_set_extensions_pb2
from google.protobuf import descriptor_pool
from google.protobuf import text_format from google.protobuf import text_format
@ -90,13 +92,11 @@ class TextFormatBase(unittest.TestCase):
.replace('e-0','e-').replace('e-0','e-') .replace('e-0','e-').replace('e-0','e-')
# Floating point fields are printed with .0 suffix even if they are # Floating point fields are printed with .0 suffix even if they are
# actualy integer numbers. # actualy integer numbers.
text = re.compile('\.0$', re.MULTILINE).sub('', text) text = re.compile(r'\.0$', re.MULTILINE).sub('', text)
return text return text
@_parameterized.Parameters( @_parameterized.Parameters((unittest_pb2), (unittest_proto3_arena_pb2))
(unittest_pb2),
(unittest_proto3_arena_pb2))
class TextFormatTest(TextFormatBase): class TextFormatTest(TextFormatBase):
def testPrintExotic(self, message_module): def testPrintExotic(self, message_module):
@ -120,8 +120,10 @@ class TextFormatTest(TextFormatBase):
'repeated_string: "\\303\\274\\352\\234\\237"\n') 'repeated_string: "\\303\\274\\352\\234\\237"\n')
def testPrintExoticUnicodeSubclass(self, message_module): def testPrintExoticUnicodeSubclass(self, message_module):
class UnicodeSub(six.text_type): class UnicodeSub(six.text_type):
pass pass
message = message_module.TestAllTypes() message = message_module.TestAllTypes()
message.repeated_string.append(UnicodeSub(u'\u00fc\ua71f')) message.repeated_string.append(UnicodeSub(u'\u00fc\ua71f'))
self.CompareToGoldenText( self.CompareToGoldenText(
@ -165,8 +167,8 @@ class TextFormatTest(TextFormatBase):
message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"') message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
message.repeated_string.append(u'\u00fc\ua71f') message.repeated_string.append(u'\u00fc\ua71f')
self.CompareToGoldenText( self.CompareToGoldenText(
self.RemoveRedundantZeros( self.RemoveRedundantZeros(text_format.MessageToString(
text_format.MessageToString(message, as_one_line=True)), message, as_one_line=True)),
'repeated_int64: -9223372036854775808' 'repeated_int64: -9223372036854775808'
' repeated_uint64: 18446744073709551615' ' repeated_uint64: 18446744073709551615'
' repeated_double: 123.456' ' repeated_double: 123.456'
@ -187,16 +189,18 @@ class TextFormatTest(TextFormatBase):
message.repeated_string.append(u'\u00fc\ua71f') message.repeated_string.append(u'\u00fc\ua71f')
# Test as_utf8 = False. # Test as_utf8 = False.
wire_text = text_format.MessageToString( wire_text = text_format.MessageToString(message,
message, as_one_line=True, as_utf8=False) as_one_line=True,
as_utf8=False)
parsed_message = message_module.TestAllTypes() parsed_message = message_module.TestAllTypes()
r = text_format.Parse(wire_text, parsed_message) r = text_format.Parse(wire_text, parsed_message)
self.assertIs(r, parsed_message) self.assertIs(r, parsed_message)
self.assertEqual(message, parsed_message) self.assertEqual(message, parsed_message)
# Test as_utf8 = True. # Test as_utf8 = True.
wire_text = text_format.MessageToString( wire_text = text_format.MessageToString(message,
message, as_one_line=True, as_utf8=True) as_one_line=True,
as_utf8=True)
parsed_message = message_module.TestAllTypes() parsed_message = message_module.TestAllTypes()
r = text_format.Parse(wire_text, parsed_message) r = text_format.Parse(wire_text, parsed_message)
self.assertIs(r, parsed_message) self.assertIs(r, parsed_message)
@ -232,14 +236,15 @@ class TextFormatTest(TextFormatBase):
message.payload.repeated_double.append(.000078900) message.payload.repeated_double.append(.000078900)
formatted_fields = ['optional_float: 1.25', formatted_fields = ['optional_float: 1.25',
'optional_double: -3.45678901234568e-6', 'optional_double: -3.45678901234568e-6',
'repeated_float: -5642', 'repeated_float: -5642', 'repeated_double: 7.89e-5']
'repeated_double: 7.89e-5']
text_message = text_format.MessageToString(message, float_format='.15g') text_message = text_format.MessageToString(message, float_format='.15g')
self.CompareToGoldenText( self.CompareToGoldenText(
self.RemoveRedundantZeros(text_message), self.RemoveRedundantZeros(text_message),
'payload {{\n {0}\n {1}\n {2}\n {3}\n}}\n'.format(*formatted_fields)) 'payload {{\n {0}\n {1}\n {2}\n {3}\n}}\n'.format(
*formatted_fields))
# as_one_line=True is a separate code branch where float_format is passed. # as_one_line=True is a separate code branch where float_format is passed.
text_message = text_format.MessageToString(message, as_one_line=True, text_message = text_format.MessageToString(message,
as_one_line=True,
float_format='.15g') float_format='.15g')
self.CompareToGoldenText( self.CompareToGoldenText(
self.RemoveRedundantZeros(text_message), self.RemoveRedundantZeros(text_message),
@ -311,8 +316,7 @@ class TextFormatTest(TextFormatBase):
self.assertEqual(123.456, message.repeated_double[0]) self.assertEqual(123.456, message.repeated_double[0])
self.assertEqual(1.23e22, message.repeated_double[1]) self.assertEqual(1.23e22, message.repeated_double[1])
self.assertEqual(1.23e-18, message.repeated_double[2]) self.assertEqual(1.23e-18, message.repeated_double[2])
self.assertEqual( self.assertEqual('\000\001\a\b\f\n\r\t\v\\\'"', message.repeated_string[0])
'\000\001\a\b\f\n\r\t\v\\\'"', message.repeated_string[0])
self.assertEqual('foocorgegrault', message.repeated_string[1]) self.assertEqual('foocorgegrault', message.repeated_string[1])
self.assertEqual(u'\u00fc\ua71f', message.repeated_string[2]) self.assertEqual(u'\u00fc\ua71f', message.repeated_string[2])
self.assertEqual(u'\u00fc', message.repeated_string[3]) self.assertEqual(u'\u00fc', message.repeated_string[3])
@ -371,43 +375,36 @@ class TextFormatTest(TextFormatBase):
def testParseSingleWord(self, message_module): def testParseSingleWord(self, message_module):
message = message_module.TestAllTypes() message = message_module.TestAllTypes()
text = 'foo' text = 'foo'
six.assertRaisesRegex(self, six.assertRaisesRegex(self, text_format.ParseError, (
text_format.ParseError, r'1:1 : Message type "\w+.TestAllTypes" has no field named '
(r'1:1 : Message type "\w+.TestAllTypes" has no field named ' r'"foo".'), text_format.Parse, text, message)
r'"foo".'),
text_format.Parse, text, message)
def testParseUnknownField(self, message_module): def testParseUnknownField(self, message_module):
message = message_module.TestAllTypes() message = message_module.TestAllTypes()
text = 'unknown_field: 8\n' text = 'unknown_field: 8\n'
six.assertRaisesRegex(self, six.assertRaisesRegex(self, text_format.ParseError, (
text_format.ParseError, r'1:1 : Message type "\w+.TestAllTypes" has no field named '
(r'1:1 : Message type "\w+.TestAllTypes" has no field named ' r'"unknown_field".'), text_format.Parse, text, message)
r'"unknown_field".'),
text_format.Parse, text, message)
def testParseBadEnumValue(self, message_module): def testParseBadEnumValue(self, message_module):
message = message_module.TestAllTypes() message = message_module.TestAllTypes()
text = 'optional_nested_enum: BARR' text = 'optional_nested_enum: BARR'
six.assertRaisesRegex(self, six.assertRaisesRegex(self, text_format.ParseError,
text_format.ParseError,
(r'1:23 : Enum type "\w+.TestAllTypes.NestedEnum" ' (r'1:23 : Enum type "\w+.TestAllTypes.NestedEnum" '
r'has no value named BARR.'), r'has no value named BARR.'), text_format.Parse,
text_format.Parse, text, message) text, message)
message = message_module.TestAllTypes() message = message_module.TestAllTypes()
text = 'optional_nested_enum: 100' text = 'optional_nested_enum: 100'
six.assertRaisesRegex(self, six.assertRaisesRegex(self, text_format.ParseError,
text_format.ParseError,
(r'1:23 : Enum type "\w+.TestAllTypes.NestedEnum" ' (r'1:23 : Enum type "\w+.TestAllTypes.NestedEnum" '
r'has no value with number 100.'), r'has no value with number 100.'), text_format.Parse,
text_format.Parse, text, message) text, message)
def testParseBadIntValue(self, message_module): def testParseBadIntValue(self, message_module):
message = message_module.TestAllTypes() message = message_module.TestAllTypes()
text = 'optional_int32: bork' text = 'optional_int32: bork'
six.assertRaisesRegex(self, six.assertRaisesRegex(self, text_format.ParseError,
text_format.ParseError,
('1:17 : Couldn\'t parse integer: bork'), ('1:17 : Couldn\'t parse integer: bork'),
text_format.Parse, text, message) text_format.Parse, text, message)
@ -419,6 +416,7 @@ class TextFormatTest(TextFormatBase):
repeated_string: "\\\\xf\\\\x62" repeated_string: "\\\\xf\\\\x62"
repeated_string: "\\\\\xf\\\\\x62" repeated_string: "\\\\\xf\\\\\x62"
repeated_string: "\x5cx20"''' repeated_string: "\x5cx20"'''
text_format.Parse(text, message) text_format.Parse(text, message)
SLASH = '\\' SLASH = '\\'
@ -433,8 +431,7 @@ class TextFormatTest(TextFormatBase):
def testMergeDuplicateScalars(self, message_module): def testMergeDuplicateScalars(self, message_module):
message = message_module.TestAllTypes() message = message_module.TestAllTypes()
text = ('optional_int32: 42 ' text = ('optional_int32: 42 ' 'optional_int32: 67')
'optional_int32: 67')
r = text_format.Merge(text, message) r = text_format.Merge(text, message)
self.assertIs(r, message) self.assertIs(r, message)
self.assertEqual(67, message.optional_int32) self.assertEqual(67, message.optional_int32)
@ -455,13 +452,11 @@ class TextFormatTest(TextFormatBase):
self.assertEqual('oneof_uint32', m2.WhichOneof('oneof_field')) self.assertEqual('oneof_uint32', m2.WhichOneof('oneof_field'))
def testParseMultipleOneof(self, message_module): def testParseMultipleOneof(self, message_module):
m_string = '\n'.join([ m_string = '\n'.join(['oneof_uint32: 11', 'oneof_string: "foo"'])
'oneof_uint32: 11',
'oneof_string: "foo"'])
m2 = message_module.TestAllTypes() m2 = message_module.TestAllTypes()
if message_module is unittest_pb2: if message_module is unittest_pb2:
with self.assertRaisesRegexp( with self.assertRaisesRegexp(text_format.ParseError,
text_format.ParseError, ' is specified along with field '): ' is specified along with field '):
text_format.Parse(m_string, m2) text_format.Parse(m_string, m2)
else: else:
text_format.Parse(m_string, m2) text_format.Parse(m_string, m2)
@ -477,8 +472,8 @@ class OnlyWorksWithProto2RightNowTests(TextFormatBase):
message = unittest_pb2.TestAllTypes() message = unittest_pb2.TestAllTypes()
test_util.SetAllFields(message) test_util.SetAllFields(message)
self.CompareToGoldenFile( self.CompareToGoldenFile(
self.RemoveRedundantZeros( self.RemoveRedundantZeros(text_format.MessageToString(
text_format.MessageToString(message, pointy_brackets=True)), message, pointy_brackets=True)),
'text_format_unittest_data_pointy_oneof.txt') 'text_format_unittest_data_pointy_oneof.txt')
def testParseGolden(self): def testParseGolden(self):
@ -499,14 +494,6 @@ class OnlyWorksWithProto2RightNowTests(TextFormatBase):
self.RemoveRedundantZeros(text_format.MessageToString(message)), self.RemoveRedundantZeros(text_format.MessageToString(message)),
'text_format_unittest_data_oneof_implemented.txt') 'text_format_unittest_data_oneof_implemented.txt')
def testPrintAllFieldsPointy(self):
message = unittest_pb2.TestAllTypes()
test_util.SetAllFields(message)
self.CompareToGoldenFile(
self.RemoveRedundantZeros(
text_format.MessageToString(message, pointy_brackets=True)),
'text_format_unittest_data_pointy_oneof.txt')
def testPrintInIndexOrder(self): def testPrintInIndexOrder(self):
message = unittest_pb2.TestFieldOrderings() message = unittest_pb2.TestFieldOrderings()
message.my_string = '115' message.my_string = '115'
@ -520,8 +507,7 @@ class OnlyWorksWithProto2RightNowTests(TextFormatBase):
'my_string: \"115\"\nmy_int: 101\nmy_float: 111\n' 'my_string: \"115\"\nmy_int: 101\nmy_float: 111\n'
'optional_nested_message {\n oo: 0\n bb: 1\n}\n') 'optional_nested_message {\n oo: 0\n bb: 1\n}\n')
self.CompareToGoldenText( self.CompareToGoldenText(
self.RemoveRedundantZeros(text_format.MessageToString( self.RemoveRedundantZeros(text_format.MessageToString(message)),
message)),
'my_int: 101\nmy_string: \"115\"\nmy_float: 111\n' 'my_int: 101\nmy_string: \"115\"\nmy_float: 111\n'
'optional_nested_message {\n bb: 1\n oo: 0\n}\n') 'optional_nested_message {\n bb: 1\n oo: 0\n}\n')
@ -552,14 +538,13 @@ class OnlyWorksWithProto2RightNowTests(TextFormatBase):
message.map_int64_int64[-2**33] = -2**34 message.map_int64_int64[-2**33] = -2**34
message.map_uint32_uint32[123] = 456 message.map_uint32_uint32[123] = 456
message.map_uint64_uint64[2**33] = 2**34 message.map_uint64_uint64[2**33] = 2**34
message.map_string_string["abc"] = "123" message.map_string_string['abc'] = '123'
message.map_int32_foreign_message[111].c = 5 message.map_int32_foreign_message[111].c = 5
# Maps are serialized to text format using their underlying repeated # Maps are serialized to text format using their underlying repeated
# representation. # representation.
self.CompareToGoldenText( self.CompareToGoldenText(
text_format.MessageToString(message), text_format.MessageToString(message), 'map_int32_int32 {\n'
'map_int32_int32 {\n'
' key: -123\n' ' key: -123\n'
' value: -456\n' ' value: -456\n'
'}\n' '}\n'
@ -592,9 +577,8 @@ class OnlyWorksWithProto2RightNowTests(TextFormatBase):
message.map_string_string[letter] = 'dummy' message.map_string_string[letter] = 'dummy'
for letter in reversed(string.ascii_uppercase[0:13]): for letter in reversed(string.ascii_uppercase[0:13]):
message.map_string_string[letter] = 'dummy' message.map_string_string[letter] = 'dummy'
golden = ''.join(( golden = ''.join(('map_string_string {\n key: "%c"\n value: "dummy"\n}\n'
'map_string_string {\n key: "%c"\n value: "dummy"\n}\n' % (letter,) % (letter,) for letter in string.ascii_uppercase))
for letter in string.ascii_uppercase))
self.CompareToGoldenText(text_format.MessageToString(message), golden) self.CompareToGoldenText(text_format.MessageToString(message), golden)
def testMapOrderSemantics(self): def testMapOrderSemantics(self):
@ -602,9 +586,7 @@ class OnlyWorksWithProto2RightNowTests(TextFormatBase):
# The C++ implementation emits defaulted-value fields, while the Python # The C++ implementation emits defaulted-value fields, while the Python
# implementation does not. Adjusting for this is awkward, but it is # implementation does not. Adjusting for this is awkward, but it is
# valuable to test against a common golden file. # valuable to test against a common golden file.
line_blacklist = (' key: 0\n', line_blacklist = (' key: 0\n', ' value: 0\n', ' key: false\n',
' value: 0\n',
' key: false\n',
' value: false\n') ' value: false\n')
golden_lines = [line for line in golden_lines if line not in line_blacklist] golden_lines = [line for line in golden_lines if line not in line_blacklist]
@ -627,8 +609,7 @@ class Proto2Tests(TextFormatBase):
message.message_set.Extensions[ext1].i = 23 message.message_set.Extensions[ext1].i = 23
message.message_set.Extensions[ext2].str = 'foo' message.message_set.Extensions[ext2].str = 'foo'
self.CompareToGoldenText( self.CompareToGoldenText(
text_format.MessageToString(message), text_format.MessageToString(message), 'message_set {\n'
'message_set {\n'
' [protobuf_unittest.TestMessageSetExtension1] {\n' ' [protobuf_unittest.TestMessageSetExtension1] {\n'
' i: 23\n' ' i: 23\n'
' }\n' ' }\n'
@ -654,9 +635,7 @@ class Proto2Tests(TextFormatBase):
message.message_set.Extensions[ext1].i = 23 message.message_set.Extensions[ext1].i = 23
message.message_set.Extensions[ext2].str = 'foo' message.message_set.Extensions[ext2].str = 'foo'
text_format.PrintMessage(message, out, use_field_number=True) text_format.PrintMessage(message, out, use_field_number=True)
self.CompareToGoldenText( self.CompareToGoldenText(out.getvalue(), '1 {\n'
out.getvalue(),
'1 {\n'
' 1545008 {\n' ' 1545008 {\n'
' 15: 23\n' ' 15: 23\n'
' }\n' ' }\n'
@ -685,8 +664,7 @@ class Proto2Tests(TextFormatBase):
def testParseMessageSet(self): def testParseMessageSet(self):
message = unittest_pb2.TestAllTypes() message = unittest_pb2.TestAllTypes()
text = ('repeated_uint64: 1\n' text = ('repeated_uint64: 1\n' 'repeated_uint64: 2\n')
'repeated_uint64: 2\n')
text_format.Parse(text, message) text_format.Parse(text, message)
self.assertEqual(1, message.repeated_uint64[0]) self.assertEqual(1, message.repeated_uint64[0])
self.assertEqual(2, message.repeated_uint64[1]) self.assertEqual(2, message.repeated_uint64[1])
@ -708,8 +686,7 @@ class Proto2Tests(TextFormatBase):
def testParseMessageByFieldNumber(self): def testParseMessageByFieldNumber(self):
message = unittest_pb2.TestAllTypes() message = unittest_pb2.TestAllTypes()
text = ('34: 1\n' text = ('34: 1\n' 'repeated_uint64: 2\n')
'repeated_uint64: 2\n')
text_format.Parse(text, message, allow_field_number=True) text_format.Parse(text, message, allow_field_number=True)
self.assertEqual(1, message.repeated_uint64[0]) self.assertEqual(1, message.repeated_uint64[0])
self.assertEqual(2, message.repeated_uint64[1]) self.assertEqual(2, message.repeated_uint64[1])
@ -732,12 +709,9 @@ class Proto2Tests(TextFormatBase):
# Can't parse field number without set allow_field_number=True. # Can't parse field number without set allow_field_number=True.
message = unittest_pb2.TestAllTypes() message = unittest_pb2.TestAllTypes()
text = '34:1\n' text = '34:1\n'
six.assertRaisesRegex( six.assertRaisesRegex(self, text_format.ParseError, (
self, r'1:1 : Message type "\w+.TestAllTypes" has no field named '
text_format.ParseError, r'"34".'), text_format.Parse, text, message)
(r'1:1 : Message type "\w+.TestAllTypes" has no field named '
r'"34".'),
text_format.Parse, text, message)
# Can't parse if field number is not found. # Can't parse if field number is not found.
text = '1234:1\n' text = '1234:1\n'
@ -746,7 +720,10 @@ class Proto2Tests(TextFormatBase):
text_format.ParseError, text_format.ParseError,
(r'1:1 : Message type "\w+.TestAllTypes" has no field named ' (r'1:1 : Message type "\w+.TestAllTypes" has no field named '
r'"1234".'), r'"1234".'),
text_format.Parse, text, message, allow_field_number=True) text_format.Parse,
text,
message,
allow_field_number=True)
def testPrintAllExtensions(self): def testPrintAllExtensions(self):
message = unittest_pb2.TestAllExtensions() message = unittest_pb2.TestAllExtensions()
@ -824,7 +801,9 @@ class Proto2Tests(TextFormatBase):
six.assertRaisesRegex(self, six.assertRaisesRegex(self,
text_format.ParseError, text_format.ParseError,
'Invalid field value: }', 'Invalid field value: }',
text_format.Parse, malformed, message, text_format.Parse,
malformed,
message,
allow_unknown_extension=True) allow_unknown_extension=True)
message = unittest_mset_pb2.TestMessageSetContainer() message = unittest_mset_pb2.TestMessageSetContainer()
@ -836,7 +815,9 @@ class Proto2Tests(TextFormatBase):
six.assertRaisesRegex(self, six.assertRaisesRegex(self,
text_format.ParseError, text_format.ParseError,
'Invalid field value: "', 'Invalid field value: "',
text_format.Parse, malformed, message, text_format.Parse,
malformed,
message,
allow_unknown_extension=True) allow_unknown_extension=True)
message = unittest_mset_pb2.TestMessageSetContainer() message = unittest_mset_pb2.TestMessageSetContainer()
@ -848,7 +829,9 @@ class Proto2Tests(TextFormatBase):
six.assertRaisesRegex(self, six.assertRaisesRegex(self,
text_format.ParseError, text_format.ParseError,
'Invalid field value: "', 'Invalid field value: "',
text_format.Parse, malformed, message, text_format.Parse,
malformed,
message,
allow_unknown_extension=True) allow_unknown_extension=True)
message = unittest_mset_pb2.TestMessageSetContainer() message = unittest_mset_pb2.TestMessageSetContainer()
@ -860,7 +843,9 @@ class Proto2Tests(TextFormatBase):
six.assertRaisesRegex(self, six.assertRaisesRegex(self,
text_format.ParseError, text_format.ParseError,
'5:1 : Expected ">".', '5:1 : Expected ">".',
text_format.Parse, malformed, message, text_format.Parse,
malformed,
message,
allow_unknown_extension=True) allow_unknown_extension=True)
# Don't allow unknown fields with allow_unknown_extension=True. # Don't allow unknown fields with allow_unknown_extension=True.
@ -874,7 +859,9 @@ class Proto2Tests(TextFormatBase):
('2:3 : Message type ' ('2:3 : Message type '
'"proto2_wireformat_unittest.TestMessageSet" has no' '"proto2_wireformat_unittest.TestMessageSet" has no'
' field named "unknown_field".'), ' field named "unknown_field".'),
text_format.Parse, malformed, message, text_format.Parse,
malformed,
message,
allow_unknown_extension=True) allow_unknown_extension=True)
# Parse known extension correcty. # Parse known extension correcty.
@ -896,33 +883,28 @@ class Proto2Tests(TextFormatBase):
def testParseBadExtension(self): def testParseBadExtension(self):
message = unittest_pb2.TestAllExtensions() message = unittest_pb2.TestAllExtensions()
text = '[unknown_extension]: 8\n' text = '[unknown_extension]: 8\n'
six.assertRaisesRegex(self, six.assertRaisesRegex(self, text_format.ParseError,
text_format.ParseError,
'1:2 : Extension "unknown_extension" not registered.', '1:2 : Extension "unknown_extension" not registered.',
text_format.Parse, text, message) text_format.Parse, text, message)
message = unittest_pb2.TestAllTypes() message = unittest_pb2.TestAllTypes()
six.assertRaisesRegex(self, six.assertRaisesRegex(self, text_format.ParseError, (
text_format.ParseError, '1:2 : Message type "protobuf_unittest.TestAllTypes" does not have '
('1:2 : Message type "protobuf_unittest.TestAllTypes" does not have ' 'extensions.'), text_format.Parse, text, message)
'extensions.'),
text_format.Parse, text, message)
def testMergeDuplicateExtensionScalars(self): def testMergeDuplicateExtensionScalars(self):
message = unittest_pb2.TestAllExtensions() message = unittest_pb2.TestAllExtensions()
text = ('[protobuf_unittest.optional_int32_extension]: 42 ' text = ('[protobuf_unittest.optional_int32_extension]: 42 '
'[protobuf_unittest.optional_int32_extension]: 67') '[protobuf_unittest.optional_int32_extension]: 67')
text_format.Merge(text, message) text_format.Merge(text, message)
self.assertEqual( self.assertEqual(67,
67,
message.Extensions[unittest_pb2.optional_int32_extension]) message.Extensions[unittest_pb2.optional_int32_extension])
def testParseDuplicateExtensionScalars(self): def testParseDuplicateExtensionScalars(self):
message = unittest_pb2.TestAllExtensions() message = unittest_pb2.TestAllExtensions()
text = ('[protobuf_unittest.optional_int32_extension]: 42 ' text = ('[protobuf_unittest.optional_int32_extension]: 42 '
'[protobuf_unittest.optional_int32_extension]: 67') '[protobuf_unittest.optional_int32_extension]: 67')
six.assertRaisesRegex(self, six.assertRaisesRegex(self, text_format.ParseError, (
text_format.ParseError, '1:96 : Message type "protobuf_unittest.TestAllExtensions" '
('1:96 : Message type "protobuf_unittest.TestAllExtensions" '
'should not have multiple ' 'should not have multiple '
'"protobuf_unittest.optional_int32_extension" extensions.'), '"protobuf_unittest.optional_int32_extension" extensions.'),
text_format.Parse, text, message) text_format.Parse, text, message)
@ -931,31 +913,26 @@ class Proto2Tests(TextFormatBase):
message = unittest_pb2.TestAllTypes() message = unittest_pb2.TestAllTypes()
text = ('optional_nested_message { bb: 1 } ' text = ('optional_nested_message { bb: 1 } '
'optional_nested_message { bb: 2 }') 'optional_nested_message { bb: 2 }')
six.assertRaisesRegex(self, six.assertRaisesRegex(self, text_format.ParseError, (
text_format.ParseError, '1:65 : Message type "protobuf_unittest.TestAllTypes.NestedMessage" '
('1:65 : Message type "protobuf_unittest.TestAllTypes.NestedMessage" ' 'should not have multiple "bb" fields.'), text_format.Parse, text,
'should not have multiple "bb" fields.'), message)
text_format.Parse, text, message)
def testParseDuplicateScalars(self): def testParseDuplicateScalars(self):
message = unittest_pb2.TestAllTypes() message = unittest_pb2.TestAllTypes()
text = ('optional_int32: 42 ' text = ('optional_int32: 42 ' 'optional_int32: 67')
'optional_int32: 67') six.assertRaisesRegex(self, text_format.ParseError, (
six.assertRaisesRegex(self, '1:36 : Message type "protobuf_unittest.TestAllTypes" should not '
text_format.ParseError, 'have multiple "optional_int32" fields.'), text_format.Parse, text,
('1:36 : Message type "protobuf_unittest.TestAllTypes" should not ' message)
'have multiple "optional_int32" fields.'),
text_format.Parse, text, message)
def testParseGroupNotClosed(self): def testParseGroupNotClosed(self):
message = unittest_pb2.TestAllTypes() message = unittest_pb2.TestAllTypes()
text = 'RepeatedGroup: <' text = 'RepeatedGroup: <'
six.assertRaisesRegex(self, six.assertRaisesRegex(self, text_format.ParseError, '1:16 : Expected ">".',
text_format.ParseError, '1:16 : Expected ">".',
text_format.Parse, text, message) text_format.Parse, text, message)
text = 'RepeatedGroup: {' text = 'RepeatedGroup: {'
six.assertRaisesRegex(self, six.assertRaisesRegex(self, text_format.ParseError, '1:16 : Expected "}".',
text_format.ParseError, '1:16 : Expected "}".',
text_format.Parse, text, message) text_format.Parse, text, message)
def testParseEmptyGroup(self): def testParseEmptyGroup(self):
@ -1007,10 +984,197 @@ class Proto2Tests(TextFormatBase):
self.assertEqual(-2**34, message.map_int64_int64[-2**33]) self.assertEqual(-2**34, message.map_int64_int64[-2**33])
self.assertEqual(456, message.map_uint32_uint32[123]) self.assertEqual(456, message.map_uint32_uint32[123])
self.assertEqual(2**34, message.map_uint64_uint64[2**33]) self.assertEqual(2**34, message.map_uint64_uint64[2**33])
self.assertEqual("123", message.map_string_string["abc"]) self.assertEqual('123', message.map_string_string['abc'])
self.assertEqual(5, message.map_int32_foreign_message[111].c) self.assertEqual(5, message.map_int32_foreign_message[111].c)
class Proto3Tests(unittest.TestCase):
def testPrintMessageExpandAny(self):
packed_message = unittest_pb2.OneString()
packed_message.data = 'string'
message = any_test_pb2.TestAny()
message.any_value.Pack(packed_message)
self.assertEqual(
text_format.MessageToString(message,
descriptor_pool=descriptor_pool.Default()),
'any_value {\n'
' [type.googleapis.com/protobuf_unittest.OneString] {\n'
' data: "string"\n'
' }\n'
'}\n')
def testPrintMessageExpandAnyRepeated(self):
packed_message = unittest_pb2.OneString()
message = any_test_pb2.TestAny()
packed_message.data = 'string0'
message.repeated_any_value.add().Pack(packed_message)
packed_message.data = 'string1'
message.repeated_any_value.add().Pack(packed_message)
self.assertEqual(
text_format.MessageToString(message,
descriptor_pool=descriptor_pool.Default()),
'repeated_any_value {\n'
' [type.googleapis.com/protobuf_unittest.OneString] {\n'
' data: "string0"\n'
' }\n'
'}\n'
'repeated_any_value {\n'
' [type.googleapis.com/protobuf_unittest.OneString] {\n'
' data: "string1"\n'
' }\n'
'}\n')
def testPrintMessageExpandAnyNoDescriptorPool(self):
packed_message = unittest_pb2.OneString()
packed_message.data = 'string'
message = any_test_pb2.TestAny()
message.any_value.Pack(packed_message)
self.assertEqual(
text_format.MessageToString(message, descriptor_pool=None),
'any_value {\n'
' type_url: "type.googleapis.com/protobuf_unittest.OneString"\n'
' value: "\\n\\006string"\n'
'}\n')
def testPrintMessageExpandAnyDescriptorPoolMissingType(self):
packed_message = unittest_pb2.OneString()
packed_message.data = 'string'
message = any_test_pb2.TestAny()
message.any_value.Pack(packed_message)
empty_pool = descriptor_pool.DescriptorPool()
self.assertEqual(
text_format.MessageToString(message, descriptor_pool=empty_pool),
'any_value {\n'
' type_url: "type.googleapis.com/protobuf_unittest.OneString"\n'
' value: "\\n\\006string"\n'
'}\n')
def testPrintMessageExpandAnyPointyBrackets(self):
packed_message = unittest_pb2.OneString()
packed_message.data = 'string'
message = any_test_pb2.TestAny()
message.any_value.Pack(packed_message)
self.assertEqual(
text_format.MessageToString(message,
pointy_brackets=True,
descriptor_pool=descriptor_pool.Default()),
'any_value <\n'
' [type.googleapis.com/protobuf_unittest.OneString] <\n'
' data: "string"\n'
' >\n'
'>\n')
def testPrintMessageExpandAnyAsOneLine(self):
packed_message = unittest_pb2.OneString()
packed_message.data = 'string'
message = any_test_pb2.TestAny()
message.any_value.Pack(packed_message)
self.assertEqual(
text_format.MessageToString(message,
as_one_line=True,
descriptor_pool=descriptor_pool.Default()),
'any_value {'
' [type.googleapis.com/protobuf_unittest.OneString]'
' { data: "string" } '
'}')
def testPrintMessageExpandAnyAsOneLinePointyBrackets(self):
packed_message = unittest_pb2.OneString()
packed_message.data = 'string'
message = any_test_pb2.TestAny()
message.any_value.Pack(packed_message)
self.assertEqual(
text_format.MessageToString(message,
as_one_line=True,
pointy_brackets=True,
descriptor_pool=descriptor_pool.Default()),
'any_value <'
' [type.googleapis.com/protobuf_unittest.OneString]'
' < data: "string" > '
'>')
def testMergeExpandedAny(self):
message = any_test_pb2.TestAny()
text = ('any_value {\n'
' [type.googleapis.com/protobuf_unittest.OneString] {\n'
' data: "string"\n'
' }\n'
'}\n')
text_format.Merge(text, message, descriptor_pool=descriptor_pool.Default())
packed_message = unittest_pb2.OneString()
message.any_value.Unpack(packed_message)
self.assertEqual('string', packed_message.data)
def testMergeExpandedAnyRepeated(self):
message = any_test_pb2.TestAny()
text = ('repeated_any_value {\n'
' [type.googleapis.com/protobuf_unittest.OneString] {\n'
' data: "string0"\n'
' }\n'
'}\n'
'repeated_any_value {\n'
' [type.googleapis.com/protobuf_unittest.OneString] {\n'
' data: "string1"\n'
' }\n'
'}\n')
text_format.Merge(text, message, descriptor_pool=descriptor_pool.Default())
packed_message = unittest_pb2.OneString()
message.repeated_any_value[0].Unpack(packed_message)
self.assertEqual('string0', packed_message.data)
message.repeated_any_value[1].Unpack(packed_message)
self.assertEqual('string1', packed_message.data)
def testMergeExpandedAnyPointyBrackets(self):
message = any_test_pb2.TestAny()
text = ('any_value {\n'
' [type.googleapis.com/protobuf_unittest.OneString] <\n'
' data: "string"\n'
' >\n'
'}\n')
text_format.Merge(text, message, descriptor_pool=descriptor_pool.Default())
packed_message = unittest_pb2.OneString()
message.any_value.Unpack(packed_message)
self.assertEqual('string', packed_message.data)
def testMergeExpandedAnyNoDescriptorPool(self):
message = any_test_pb2.TestAny()
text = ('any_value {\n'
' [type.googleapis.com/protobuf_unittest.OneString] {\n'
' data: "string"\n'
' }\n'
'}\n')
with self.assertRaises(text_format.ParseError) as e:
text_format.Merge(text, message, descriptor_pool=None)
self.assertEqual(str(e.exception),
'Descriptor pool required to parse expanded Any field')
def testMergeExpandedAnyDescriptorPoolMissingType(self):
message = any_test_pb2.TestAny()
text = ('any_value {\n'
' [type.googleapis.com/protobuf_unittest.OneString] {\n'
' data: "string"\n'
' }\n'
'}\n')
with self.assertRaises(text_format.ParseError) as e:
empty_pool = descriptor_pool.DescriptorPool()
text_format.Merge(text, message, descriptor_pool=empty_pool)
self.assertEqual(
str(e.exception),
'Type protobuf_unittest.OneString not found in descriptor pool')
def testMergeUnexpandedAny(self):
text = ('any_value {\n'
' type_url: "type.googleapis.com/protobuf_unittest.OneString"\n'
' value: "\\n\\006string"\n'
'}\n')
message = any_test_pb2.TestAny()
text_format.Merge(text, message)
packed_message = unittest_pb2.OneString()
message.any_value.Unpack(packed_message)
self.assertEqual('string', packed_message.data)
class TokenizerTest(unittest.TestCase): class TokenizerTest(unittest.TestCase):
def testSimpleTokenCases(self): def testSimpleTokenCases(self):
@ -1021,79 +1185,55 @@ class TokenizerTest(unittest.TestCase):
'ID9: 22 ID10: -111111111111111111 ID11: -22\n' 'ID9: 22 ID10: -111111111111111111 ID11: -22\n'
'ID12: 2222222222222222222 ID13: 1.23456f ID14: 1.2e+2f ' 'ID12: 2222222222222222222 ID13: 1.23456f ID14: 1.2e+2f '
'false_bool: 0 true_BOOL:t \n true_bool1: 1 false_BOOL1:f ') 'false_bool: 0 true_BOOL:t \n true_bool1: 1 false_BOOL1:f ')
tokenizer = text_format._Tokenizer(text.splitlines()) tokenizer = text_format.Tokenizer(text.splitlines())
methods = [(tokenizer.ConsumeIdentifier, 'identifier1'), methods = [(tokenizer.ConsumeIdentifier, 'identifier1'), ':',
':',
(tokenizer.ConsumeString, 'string1'), (tokenizer.ConsumeString, 'string1'),
(tokenizer.ConsumeIdentifier, 'identifier2'), (tokenizer.ConsumeIdentifier, 'identifier2'), ':',
':', (tokenizer.ConsumeInteger, 123),
(tokenizer.ConsumeInt32, 123), (tokenizer.ConsumeIdentifier, 'identifier3'), ':',
(tokenizer.ConsumeIdentifier, 'identifier3'),
':',
(tokenizer.ConsumeString, 'string'), (tokenizer.ConsumeString, 'string'),
(tokenizer.ConsumeIdentifier, 'identifiER_4'), (tokenizer.ConsumeIdentifier, 'identifiER_4'), ':',
':',
(tokenizer.ConsumeFloat, 1.1e+2), (tokenizer.ConsumeFloat, 1.1e+2),
(tokenizer.ConsumeIdentifier, 'ID5'), (tokenizer.ConsumeIdentifier, 'ID5'), ':',
':',
(tokenizer.ConsumeFloat, -0.23), (tokenizer.ConsumeFloat, -0.23),
(tokenizer.ConsumeIdentifier, 'ID6'), (tokenizer.ConsumeIdentifier, 'ID6'), ':',
':',
(tokenizer.ConsumeString, 'aaaa\'bbbb'), (tokenizer.ConsumeString, 'aaaa\'bbbb'),
(tokenizer.ConsumeIdentifier, 'ID7'), (tokenizer.ConsumeIdentifier, 'ID7'), ':',
':',
(tokenizer.ConsumeString, 'aa\"bb'), (tokenizer.ConsumeString, 'aa\"bb'),
(tokenizer.ConsumeIdentifier, 'ID8'), (tokenizer.ConsumeIdentifier, 'ID8'), ':', '{',
':', (tokenizer.ConsumeIdentifier, 'A'), ':',
'{',
(tokenizer.ConsumeIdentifier, 'A'),
':',
(tokenizer.ConsumeFloat, float('inf')), (tokenizer.ConsumeFloat, float('inf')),
(tokenizer.ConsumeIdentifier, 'B'), (tokenizer.ConsumeIdentifier, 'B'), ':',
':',
(tokenizer.ConsumeFloat, -float('inf')), (tokenizer.ConsumeFloat, -float('inf')),
(tokenizer.ConsumeIdentifier, 'C'), (tokenizer.ConsumeIdentifier, 'C'), ':',
':',
(tokenizer.ConsumeBool, True), (tokenizer.ConsumeBool, True),
(tokenizer.ConsumeIdentifier, 'D'), (tokenizer.ConsumeIdentifier, 'D'), ':',
':', (tokenizer.ConsumeBool, False), '}',
(tokenizer.ConsumeBool, False), (tokenizer.ConsumeIdentifier, 'ID9'), ':',
'}', (tokenizer.ConsumeInteger, 22),
(tokenizer.ConsumeIdentifier, 'ID9'), (tokenizer.ConsumeIdentifier, 'ID10'), ':',
':', (tokenizer.ConsumeInteger, -111111111111111111),
(tokenizer.ConsumeUint32, 22), (tokenizer.ConsumeIdentifier, 'ID11'), ':',
(tokenizer.ConsumeIdentifier, 'ID10'), (tokenizer.ConsumeInteger, -22),
':', (tokenizer.ConsumeIdentifier, 'ID12'), ':',
(tokenizer.ConsumeInt64, -111111111111111111), (tokenizer.ConsumeInteger, 2222222222222222222),
(tokenizer.ConsumeIdentifier, 'ID11'), (tokenizer.ConsumeIdentifier, 'ID13'), ':',
':',
(tokenizer.ConsumeInt32, -22),
(tokenizer.ConsumeIdentifier, 'ID12'),
':',
(tokenizer.ConsumeUint64, 2222222222222222222),
(tokenizer.ConsumeIdentifier, 'ID13'),
':',
(tokenizer.ConsumeFloat, 1.23456), (tokenizer.ConsumeFloat, 1.23456),
(tokenizer.ConsumeIdentifier, 'ID14'), (tokenizer.ConsumeIdentifier, 'ID14'), ':',
':',
(tokenizer.ConsumeFloat, 1.2e+2), (tokenizer.ConsumeFloat, 1.2e+2),
(tokenizer.ConsumeIdentifier, 'false_bool'), (tokenizer.ConsumeIdentifier, 'false_bool'), ':',
':',
(tokenizer.ConsumeBool, False), (tokenizer.ConsumeBool, False),
(tokenizer.ConsumeIdentifier, 'true_BOOL'), (tokenizer.ConsumeIdentifier, 'true_BOOL'), ':',
':',
(tokenizer.ConsumeBool, True), (tokenizer.ConsumeBool, True),
(tokenizer.ConsumeIdentifier, 'true_bool1'), (tokenizer.ConsumeIdentifier, 'true_bool1'), ':',
':',
(tokenizer.ConsumeBool, True), (tokenizer.ConsumeBool, True),
(tokenizer.ConsumeIdentifier, 'false_BOOL1'), (tokenizer.ConsumeIdentifier, 'false_BOOL1'), ':',
':',
(tokenizer.ConsumeBool, False)] (tokenizer.ConsumeBool, False)]
i = 0 i = 0
while not tokenizer.AtEnd(): while not tokenizer.AtEnd():
m = methods[i] m = methods[i]
if type(m) == str: if isinstance(m, str):
token = tokenizer.token token = tokenizer.token
self.assertEqual(token, m) self.assertEqual(token, m)
tokenizer.NextToken() tokenizer.NextToken()
@ -1101,59 +1241,119 @@ class TokenizerTest(unittest.TestCase):
self.assertEqual(m[1], m[0]()) self.assertEqual(m[1], m[0]())
i += 1 i += 1
def testConsumeIntegers(self): def testConsumeAbstractIntegers(self):
# This test only tests the failures in the integer parsing methods as well # This test only tests the failures in the integer parsing methods as well
# as the '0' special cases. # as the '0' special cases.
int64_max = (1 << 63) - 1 int64_max = (1 << 63) - 1
uint32_max = (1 << 32) - 1 uint32_max = (1 << 32) - 1
text = '-1 %d %d' % (uint32_max + 1, int64_max + 1) text = '-1 %d %d' % (uint32_max + 1, int64_max + 1)
tokenizer = text_format._Tokenizer(text.splitlines()) tokenizer = text_format.Tokenizer(text.splitlines())
self.assertRaises(text_format.ParseError, tokenizer.ConsumeUint32) self.assertEqual(-1, tokenizer.ConsumeInteger())
self.assertRaises(text_format.ParseError, tokenizer.ConsumeUint64)
self.assertEqual(-1, tokenizer.ConsumeInt32()) self.assertEqual(uint32_max + 1, tokenizer.ConsumeInteger())
self.assertRaises(text_format.ParseError, tokenizer.ConsumeUint32) self.assertEqual(int64_max + 1, tokenizer.ConsumeInteger())
self.assertRaises(text_format.ParseError, tokenizer.ConsumeInt32) self.assertTrue(tokenizer.AtEnd())
self.assertEqual(uint32_max + 1, tokenizer.ConsumeInt64())
self.assertRaises(text_format.ParseError, tokenizer.ConsumeInt64) text = '-0 0'
self.assertEqual(int64_max + 1, tokenizer.ConsumeUint64()) tokenizer = text_format.Tokenizer(text.splitlines())
self.assertEqual(0, tokenizer.ConsumeInteger())
self.assertEqual(0, tokenizer.ConsumeInteger())
self.assertTrue(tokenizer.AtEnd())
def testConsumeIntegers(self):
# This test only tests the failures in the integer parsing methods as well
# as the '0' special cases.
int64_max = (1 << 63) - 1
uint32_max = (1 << 32) - 1
text = '-1 %d %d' % (uint32_max + 1, int64_max + 1)
tokenizer = text_format.Tokenizer(text.splitlines())
self.assertRaises(text_format.ParseError,
text_format._ConsumeUint32, tokenizer)
self.assertRaises(text_format.ParseError,
text_format._ConsumeUint64, tokenizer)
self.assertEqual(-1, text_format._ConsumeInt32(tokenizer))
self.assertRaises(text_format.ParseError,
text_format._ConsumeUint32, tokenizer)
self.assertRaises(text_format.ParseError,
text_format._ConsumeInt32, tokenizer)
self.assertEqual(uint32_max + 1, text_format._ConsumeInt64(tokenizer))
self.assertRaises(text_format.ParseError,
text_format._ConsumeInt64, tokenizer)
self.assertEqual(int64_max + 1, text_format._ConsumeUint64(tokenizer))
self.assertTrue(tokenizer.AtEnd()) self.assertTrue(tokenizer.AtEnd())
text = '-0 -0 0 0' text = '-0 -0 0 0'
tokenizer = text_format._Tokenizer(text.splitlines()) tokenizer = text_format.Tokenizer(text.splitlines())
self.assertEqual(0, tokenizer.ConsumeUint32()) self.assertEqual(0, text_format._ConsumeUint32(tokenizer))
self.assertEqual(0, tokenizer.ConsumeUint64()) self.assertEqual(0, text_format._ConsumeUint64(tokenizer))
self.assertEqual(0, tokenizer.ConsumeUint32()) self.assertEqual(0, text_format._ConsumeUint32(tokenizer))
self.assertEqual(0, tokenizer.ConsumeUint64()) self.assertEqual(0, text_format._ConsumeUint64(tokenizer))
self.assertTrue(tokenizer.AtEnd()) self.assertTrue(tokenizer.AtEnd())
def testConsumeByteString(self): def testConsumeByteString(self):
text = '"string1\'' text = '"string1\''
tokenizer = text_format._Tokenizer(text.splitlines()) tokenizer = text_format.Tokenizer(text.splitlines())
self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString) self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
text = 'string1"' text = 'string1"'
tokenizer = text_format._Tokenizer(text.splitlines()) tokenizer = text_format.Tokenizer(text.splitlines())
self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString) self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
text = '\n"\\xt"' text = '\n"\\xt"'
tokenizer = text_format._Tokenizer(text.splitlines()) tokenizer = text_format.Tokenizer(text.splitlines())
self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString) self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
text = '\n"\\"' text = '\n"\\"'
tokenizer = text_format._Tokenizer(text.splitlines()) tokenizer = text_format.Tokenizer(text.splitlines())
self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString) self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
text = '\n"\\x"' text = '\n"\\x"'
tokenizer = text_format._Tokenizer(text.splitlines()) tokenizer = text_format.Tokenizer(text.splitlines())
self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString) self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
def testConsumeBool(self): def testConsumeBool(self):
text = 'not-a-bool' text = 'not-a-bool'
tokenizer = text_format._Tokenizer(text.splitlines()) tokenizer = text_format.Tokenizer(text.splitlines())
self.assertRaises(text_format.ParseError, tokenizer.ConsumeBool) self.assertRaises(text_format.ParseError, tokenizer.ConsumeBool)
def testSkipComment(self):
tokenizer = text_format.Tokenizer('# some comment'.splitlines())
self.assertTrue(tokenizer.AtEnd())
self.assertRaises(text_format.ParseError, tokenizer.ConsumeComment)
def testConsumeComment(self):
tokenizer = text_format.Tokenizer('# some comment'.splitlines(),
skip_comments=False)
self.assertFalse(tokenizer.AtEnd())
self.assertEqual('# some comment', tokenizer.ConsumeComment())
self.assertTrue(tokenizer.AtEnd())
def testConsumeTwoComments(self):
text = '# some comment\n# another comment'
tokenizer = text_format.Tokenizer(text.splitlines(), skip_comments=False)
self.assertEqual('# some comment', tokenizer.ConsumeComment())
self.assertFalse(tokenizer.AtEnd())
self.assertEqual('# another comment', tokenizer.ConsumeComment())
self.assertTrue(tokenizer.AtEnd())
def testConsumeTrailingComment(self):
text = 'some_number: 4\n# some comment'
tokenizer = text_format.Tokenizer(text.splitlines(), skip_comments=False)
self.assertRaises(text_format.ParseError, tokenizer.ConsumeComment)
self.assertEqual('some_number', tokenizer.ConsumeIdentifier())
self.assertEqual(tokenizer.token, ':')
tokenizer.NextToken()
self.assertRaises(text_format.ParseError, tokenizer.ConsumeComment)
self.assertEqual(4, tokenizer.ConsumeInteger())
self.assertFalse(tokenizer.AtEnd())
self.assertEqual('# some comment', tokenizer.ConsumeComment())
self.assertTrue(tokenizer.AtEnd())
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

@ -53,6 +53,7 @@ import re
import six import six
import sys import sys
from operator import methodcaller
from google.protobuf import descriptor from google.protobuf import descriptor
from google.protobuf import symbol_database from google.protobuf import symbol_database
@ -98,34 +99,41 @@ def MessageToJson(message, including_default_value_fields=False):
Returns: Returns:
A string containing the JSON formatted protocol buffer message. A string containing the JSON formatted protocol buffer message.
""" """
js = _MessageToJsonObject(message, including_default_value_fields) printer = _Printer(including_default_value_fields)
return json.dumps(js, indent=2) return printer.ToJsonString(message)
def _IsMapEntry(field):
return (field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and
field.message_type.has_options and
field.message_type.GetOptions().map_entry)
def _MessageToJsonObject(message, including_default_value_fields): class _Printer(object):
"""JSON format printer for protocol message."""
def __init__(self,
including_default_value_fields=False):
self.including_default_value_fields = including_default_value_fields
def ToJsonString(self, message):
js = self._MessageToJsonObject(message)
return json.dumps(js, indent=2)
def _MessageToJsonObject(self, message):
"""Converts message to an object according to Proto3 JSON Specification.""" """Converts message to an object according to Proto3 JSON Specification."""
message_descriptor = message.DESCRIPTOR message_descriptor = message.DESCRIPTOR
full_name = message_descriptor.full_name full_name = message_descriptor.full_name
if _IsWrapperMessage(message_descriptor): if _IsWrapperMessage(message_descriptor):
return _WrapperMessageToJsonObject(message) return self._WrapperMessageToJsonObject(message)
if full_name in _WKTJSONMETHODS: if full_name in _WKTJSONMETHODS:
return _WKTJSONMETHODS[full_name][0]( return methodcaller(_WKTJSONMETHODS[full_name][0], message)(self)
message, including_default_value_fields)
js = {} js = {}
return _RegularMessageToJsonObject( return self._RegularMessageToJsonObject(message, js)
message, js, including_default_value_fields)
def _RegularMessageToJsonObject(self, message, js):
def _IsMapEntry(field):
return (field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and
field.message_type.has_options and
field.message_type.GetOptions().map_entry)
def _RegularMessageToJsonObject(message, js, including_default_value_fields):
"""Converts normal message according to Proto3 JSON Specification.""" """Converts normal message according to Proto3 JSON Specification."""
fields = message.ListFields() fields = message.ListFields()
include_default = including_default_value_fields
try: try:
for field, value in fields: for field, value in fields:
@ -142,18 +150,18 @@ def _RegularMessageToJsonObject(message, js, including_default_value_fields):
recorded_key = 'false' recorded_key = 'false'
else: else:
recorded_key = key recorded_key = key
js_map[recorded_key] = _FieldToJsonObject( js_map[recorded_key] = self._FieldToJsonObject(
v_field, value[key], including_default_value_fields) v_field, value[key])
js[name] = js_map js[name] = js_map
elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
# Convert a repeated field. # Convert a repeated field.
js[name] = [_FieldToJsonObject(field, k, include_default) js[name] = [self._FieldToJsonObject(field, k)
for k in value] for k in value]
else: else:
js[name] = _FieldToJsonObject(field, value, include_default) js[name] = self._FieldToJsonObject(field, value)
# Serialize default value if including_default_value_fields is True. # Serialize default value if including_default_value_fields is True.
if including_default_value_fields: if self.including_default_value_fields:
message_descriptor = message.DESCRIPTOR message_descriptor = message.DESCRIPTOR
for field in message_descriptor.fields: for field in message_descriptor.fields:
# Singular message fields and oneof fields will not be affected. # Singular message fields and oneof fields will not be affected.
@ -170,7 +178,7 @@ def _RegularMessageToJsonObject(message, js, including_default_value_fields):
elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
js[name] = [] js[name] = []
else: else:
js[name] = _FieldToJsonObject(field, field.default_value) js[name] = self._FieldToJsonObject(field, field.default_value)
except ValueError as e: except ValueError as e:
raise SerializeToJsonError( raise SerializeToJsonError(
@ -178,12 +186,10 @@ def _RegularMessageToJsonObject(message, js, including_default_value_fields):
return js return js
def _FieldToJsonObject(self, field, value):
def _FieldToJsonObject(
field, value, including_default_value_fields=False):
"""Converts field value according to Proto3 JSON Specification.""" """Converts field value according to Proto3 JSON Specification."""
if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
return _MessageToJsonObject(value, including_default_value_fields) return self._MessageToJsonObject(value)
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM:
enum_value = field.enum_type.values_by_number.get(value, None) enum_value = field.enum_type.values_by_number.get(value, None)
if enum_value is not None: if enum_value is not None:
@ -211,8 +217,7 @@ def _FieldToJsonObject(
return _NAN return _NAN
return value return value
def _AnyMessageToJsonObject(self, message):
def _AnyMessageToJsonObject(message, including_default):
"""Converts Any message according to Proto3 JSON Specification.""" """Converts Any message according to Proto3 JSON Specification."""
if not message.ListFields(): if not message.ListFields():
return {} return {}
@ -225,36 +230,21 @@ def _AnyMessageToJsonObject(message, including_default):
message_descriptor = sub_message.DESCRIPTOR message_descriptor = sub_message.DESCRIPTOR
full_name = message_descriptor.full_name full_name = message_descriptor.full_name
if _IsWrapperMessage(message_descriptor): if _IsWrapperMessage(message_descriptor):
js['value'] = _WrapperMessageToJsonObject(sub_message) js['value'] = self._WrapperMessageToJsonObject(sub_message)
return js return js
if full_name in _WKTJSONMETHODS: if full_name in _WKTJSONMETHODS:
js['value'] = _WKTJSONMETHODS[full_name][0](sub_message, including_default) js['value'] = methodcaller(_WKTJSONMETHODS[full_name][0],
sub_message)(self)
return js return js
return _RegularMessageToJsonObject(sub_message, js, including_default) return self._RegularMessageToJsonObject(sub_message, js)
def _CreateMessageFromTypeUrl(type_url): def _GenericMessageToJsonObject(self, message):
# TODO(jieluo): Should add a way that users can register the type resolver """Converts message according to Proto3 JSON Specification."""
# instead of the default one.
db = symbol_database.Default()
type_name = type_url.split('/')[-1]
try:
message_descriptor = db.pool.FindMessageTypeByName(type_name)
except KeyError:
raise TypeError(
'Can not find message descriptor by type_url: {0}.'.format(type_url))
message_class = db.GetPrototype(message_descriptor)
return message_class()
def _GenericMessageToJsonObject(message, unused_including_default):
"""Converts message by ToJsonString according to Proto3 JSON Specification."""
# Duration, Timestamp and FieldMask have ToJsonString method to do the # Duration, Timestamp and FieldMask have ToJsonString method to do the
# convert. Users can also call the method directly. # convert. Users can also call the method directly.
return message.ToJsonString() return message.ToJsonString()
def _ValueMessageToJsonObject(self, message):
def _ValueMessageToJsonObject(message, unused_including_default=False):
"""Converts Value message according to Proto3 JSON Specification.""" """Converts Value message according to Proto3 JSON Specification."""
which = message.WhichOneof('kind') which = message.WhichOneof('kind')
# If the Value message is not set treat as null_value when serialize # If the Value message is not set treat as null_value when serialize
@ -262,39 +252,36 @@ def _ValueMessageToJsonObject(message, unused_including_default=False):
if which is None or which == 'null_value': if which is None or which == 'null_value':
return None return None
if which == 'list_value': if which == 'list_value':
return _ListValueMessageToJsonObject(message.list_value) return self._ListValueMessageToJsonObject(message.list_value)
if which == 'struct_value': if which == 'struct_value':
value = message.struct_value value = message.struct_value
else: else:
value = getattr(message, which) value = getattr(message, which)
oneof_descriptor = message.DESCRIPTOR.fields_by_name[which] oneof_descriptor = message.DESCRIPTOR.fields_by_name[which]
return _FieldToJsonObject(oneof_descriptor, value) return self._FieldToJsonObject(oneof_descriptor, value)
def _ListValueMessageToJsonObject(message, unused_including_default=False): def _ListValueMessageToJsonObject(self, message):
"""Converts ListValue message according to Proto3 JSON Specification.""" """Converts ListValue message according to Proto3 JSON Specification."""
return [_ValueMessageToJsonObject(value) return [self._ValueMessageToJsonObject(value)
for value in message.values] for value in message.values]
def _StructMessageToJsonObject(self, message):
def _StructMessageToJsonObject(message, unused_including_default=False):
"""Converts Struct message according to Proto3 JSON Specification.""" """Converts Struct message according to Proto3 JSON Specification."""
fields = message.fields fields = message.fields
ret = {} ret = {}
for key in fields: for key in fields:
ret[key] = _ValueMessageToJsonObject(fields[key]) ret[key] = self._ValueMessageToJsonObject(fields[key])
return ret return ret
def _WrapperMessageToJsonObject(self, message):
return self._FieldToJsonObject(
message.DESCRIPTOR.fields_by_name['value'], message.value)
def _IsWrapperMessage(message_descriptor): def _IsWrapperMessage(message_descriptor):
return message_descriptor.file.name == 'google/protobuf/wrappers.proto' return message_descriptor.file.name == 'google/protobuf/wrappers.proto'
def _WrapperMessageToJsonObject(message):
return _FieldToJsonObject(
message.DESCRIPTOR.fields_by_name['value'], message.value)
def _DuplicateChecker(js): def _DuplicateChecker(js):
result = {} result = {}
for name, value in js: for name, value in js:
@ -304,12 +291,27 @@ def _DuplicateChecker(js):
return result return result
def Parse(text, message): def _CreateMessageFromTypeUrl(type_url):
# TODO(jieluo): Should add a way that users can register the type resolver
# instead of the default one.
db = symbol_database.Default()
type_name = type_url.split('/')[-1]
try:
message_descriptor = db.pool.FindMessageTypeByName(type_name)
except KeyError:
raise TypeError(
'Can not find message descriptor by type_url: {0}.'.format(type_url))
message_class = db.GetPrototype(message_descriptor)
return message_class()
def Parse(text, message, ignore_unknown_fields=False):
"""Parses a JSON representation of a protocol message into a message. """Parses a JSON representation of a protocol message into a message.
Args: Args:
text: Message JSON representation. text: Message JSON representation.
message: A protocol beffer message to merge into. message: A protocol beffer message to merge into.
ignore_unknown_fields: If True, do not raise errors for unknown fields.
Returns: Returns:
The same message passed as argument. The same message passed as argument.
@ -326,11 +328,41 @@ def Parse(text, message):
js = json.loads(text, object_pairs_hook=_DuplicateChecker) js = json.loads(text, object_pairs_hook=_DuplicateChecker)
except ValueError as e: except ValueError as e:
raise ParseError('Failed to load JSON: {0}.'.format(str(e))) raise ParseError('Failed to load JSON: {0}.'.format(str(e)))
_ConvertMessage(js, message) parser = _Parser(ignore_unknown_fields)
parser.ConvertMessage(js, message)
return message return message
def _ConvertFieldValuePair(js, message): _INT_OR_FLOAT = six.integer_types + (float,)
class _Parser(object):
"""JSON format parser for protocol message."""
def __init__(self,
ignore_unknown_fields):
self.ignore_unknown_fields = ignore_unknown_fields
def ConvertMessage(self, value, message):
"""Convert a JSON object into a message.
Args:
value: A JSON object.
message: A WKT or regular protocol message to record the data.
Raises:
ParseError: In case of convert problems.
"""
message_descriptor = message.DESCRIPTOR
full_name = message_descriptor.full_name
if _IsWrapperMessage(message_descriptor):
self._ConvertWrapperMessage(value, message)
elif full_name in _WKTJSONMETHODS:
methodcaller(_WKTJSONMETHODS[full_name][1], value, message)(self)
else:
self._ConvertFieldValuePair(value, message)
def _ConvertFieldValuePair(self, js, message):
"""Convert field value pairs into regular message. """Convert field value pairs into regular message.
Args: Args:
@ -346,20 +378,22 @@ def _ConvertFieldValuePair(js, message):
try: try:
field = message_descriptor.fields_by_camelcase_name.get(name, None) field = message_descriptor.fields_by_camelcase_name.get(name, None)
if not field: if not field:
if self.ignore_unknown_fields:
continue
raise ParseError( raise ParseError(
'Message type "{0}" has no field named "{1}".'.format( 'Message type "{0}" has no field named "{1}".'.format(
message_descriptor.full_name, name)) message_descriptor.full_name, name))
if name in names: if name in names:
raise ParseError( raise ParseError('Message type "{0}" should not have multiple '
'Message type "{0}" should not have multiple "{1}" fields.'.format( '"{1}" fields.'.format(
message.DESCRIPTOR.full_name, name)) message.DESCRIPTOR.full_name, name))
names.append(name) names.append(name)
# Check no other oneof field is parsed. # Check no other oneof field is parsed.
if field.containing_oneof is not None: if field.containing_oneof is not None:
oneof_name = field.containing_oneof.name oneof_name = field.containing_oneof.name
if oneof_name in names: if oneof_name in names:
raise ParseError('Message type "{0}" should not have multiple "{1}" ' raise ParseError('Message type "{0}" should not have multiple '
'oneof fields.'.format( '"{1}" oneof fields.'.format(
message.DESCRIPTOR.full_name, oneof_name)) message.DESCRIPTOR.full_name, oneof_name))
names.append(oneof_name) names.append(oneof_name)
@ -371,7 +405,7 @@ def _ConvertFieldValuePair(js, message):
# Parse field value. # Parse field value.
if _IsMapEntry(field): if _IsMapEntry(field):
message.ClearField(field.name) message.ClearField(field.name)
_ConvertMapFieldValue(value, message, field) self._ConvertMapFieldValue(value, message, field)
elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
message.ClearField(field.name) message.ClearField(field.name)
if not isinstance(value, list): if not isinstance(value, list):
@ -386,7 +420,7 @@ def _ConvertFieldValuePair(js, message):
sub_message.DESCRIPTOR.full_name != 'google.protobuf.Value'): sub_message.DESCRIPTOR.full_name != 'google.protobuf.Value'):
raise ParseError('null is not allowed to be used as an element' raise ParseError('null is not allowed to be used as an element'
' in a repeated field.') ' in a repeated field.')
_ConvertMessage(item, sub_message) self.ConvertMessage(item, sub_message)
else: else:
# Repeated scalar field. # Repeated scalar field.
for item in value: for item in value:
@ -397,7 +431,7 @@ def _ConvertFieldValuePair(js, message):
_ConvertScalarFieldValue(item, field)) _ConvertScalarFieldValue(item, field))
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
sub_message = getattr(message, field.name) sub_message = getattr(message, field.name)
_ConvertMessage(value, sub_message) self.ConvertMessage(value, sub_message)
else: else:
setattr(message, field.name, _ConvertScalarFieldValue(value, field)) setattr(message, field.name, _ConvertScalarFieldValue(value, field))
except ParseError as e: except ParseError as e:
@ -410,28 +444,7 @@ def _ConvertFieldValuePair(js, message):
except TypeError as e: except TypeError as e:
raise ParseError('Failed to parse {0} field: {1}.'.format(name, e)) raise ParseError('Failed to parse {0} field: {1}.'.format(name, e))
def _ConvertAnyMessage(self, value, message):
def _ConvertMessage(value, message):
"""Convert a JSON object into a message.
Args:
value: A JSON object.
message: A WKT or regular protocol message to record the data.
Raises:
ParseError: In case of convert problems.
"""
message_descriptor = message.DESCRIPTOR
full_name = message_descriptor.full_name
if _IsWrapperMessage(message_descriptor):
_ConvertWrapperMessage(value, message)
elif full_name in _WKTJSONMETHODS:
_WKTJSONMETHODS[full_name][1](value, message)
else:
_ConvertFieldValuePair(value, message)
def _ConvertAnyMessage(value, message):
"""Convert a JSON representation into Any message.""" """Convert a JSON representation into Any message."""
if isinstance(value, dict) and not value: if isinstance(value, dict) and not value:
return return
@ -444,33 +457,29 @@ def _ConvertAnyMessage(value, message):
message_descriptor = sub_message.DESCRIPTOR message_descriptor = sub_message.DESCRIPTOR
full_name = message_descriptor.full_name full_name = message_descriptor.full_name
if _IsWrapperMessage(message_descriptor): if _IsWrapperMessage(message_descriptor):
_ConvertWrapperMessage(value['value'], sub_message) self._ConvertWrapperMessage(value['value'], sub_message)
elif full_name in _WKTJSONMETHODS: elif full_name in _WKTJSONMETHODS:
_WKTJSONMETHODS[full_name][1](value['value'], sub_message) methodcaller(
_WKTJSONMETHODS[full_name][1], value['value'], sub_message)(self)
else: else:
del value['@type'] del value['@type']
_ConvertFieldValuePair(value, sub_message) self._ConvertFieldValuePair(value, sub_message)
# Sets Any message # Sets Any message
message.value = sub_message.SerializeToString() message.value = sub_message.SerializeToString()
message.type_url = type_url message.type_url = type_url
def _ConvertGenericMessage(self, value, message):
def _ConvertGenericMessage(value, message):
"""Convert a JSON representation into message with FromJsonString.""" """Convert a JSON representation into message with FromJsonString."""
# Durantion, Timestamp, FieldMask have FromJsonString method to do the # Durantion, Timestamp, FieldMask have FromJsonString method to do the
# convert. Users can also call the method directly. # convert. Users can also call the method directly.
message.FromJsonString(value) message.FromJsonString(value)
def _ConvertValueMessage(self, value, message):
_INT_OR_FLOAT = six.integer_types + (float,)
def _ConvertValueMessage(value, message):
"""Convert a JSON representation into Value message.""" """Convert a JSON representation into Value message."""
if isinstance(value, dict): if isinstance(value, dict):
_ConvertStructMessage(value, message.struct_value) self._ConvertStructMessage(value, message.struct_value)
elif isinstance(value, list): elif isinstance(value, list):
_ConvertListValueMessage(value, message.list_value) self. _ConvertListValueMessage(value, message.list_value)
elif value is None: elif value is None:
message.null_value = 0 message.null_value = 0
elif isinstance(value, bool): elif isinstance(value, bool):
@ -482,34 +491,30 @@ def _ConvertValueMessage(value, message):
else: else:
raise ParseError('Unexpected type for Value message.') raise ParseError('Unexpected type for Value message.')
def _ConvertListValueMessage(self, value, message):
def _ConvertListValueMessage(value, message):
"""Convert a JSON representation into ListValue message.""" """Convert a JSON representation into ListValue message."""
if not isinstance(value, list): if not isinstance(value, list):
raise ParseError( raise ParseError(
'ListValue must be in [] which is {0}.'.format(value)) 'ListValue must be in [] which is {0}.'.format(value))
message.ClearField('values') message.ClearField('values')
for item in value: for item in value:
_ConvertValueMessage(item, message.values.add()) self._ConvertValueMessage(item, message.values.add())
def _ConvertStructMessage(self, value, message):
def _ConvertStructMessage(value, message):
"""Convert a JSON representation into Struct message.""" """Convert a JSON representation into Struct message."""
if not isinstance(value, dict): if not isinstance(value, dict):
raise ParseError( raise ParseError(
'Struct must be in a dict which is {0}.'.format(value)) 'Struct must be in a dict which is {0}.'.format(value))
for key in value: for key in value:
_ConvertValueMessage(value[key], message.fields[key]) self._ConvertValueMessage(value[key], message.fields[key])
return return
def _ConvertWrapperMessage(self, value, message):
def _ConvertWrapperMessage(value, message):
"""Convert a JSON representation into Wrapper message.""" """Convert a JSON representation into Wrapper message."""
field = message.DESCRIPTOR.fields_by_name['value'] field = message.DESCRIPTOR.fields_by_name['value']
setattr(message, 'value', _ConvertScalarFieldValue(value, field)) setattr(message, 'value', _ConvertScalarFieldValue(value, field))
def _ConvertMapFieldValue(self, value, message, field):
def _ConvertMapFieldValue(value, message, field):
"""Convert map field value for a message map field. """Convert map field value for a message map field.
Args: Args:
@ -529,7 +534,8 @@ def _ConvertMapFieldValue(value, message, field):
for key in value: for key in value:
key_value = _ConvertScalarFieldValue(key, key_field, True) key_value = _ConvertScalarFieldValue(key, key_field, True)
if value_field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: if value_field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
_ConvertMessage(value[key], getattr(message, field.name)[key_value]) self.ConvertMessage(value[key], getattr(
message, field.name)[key_value])
else: else:
getattr(message, field.name)[key_value] = _ConvertScalarFieldValue( getattr(message, field.name)[key_value] = _ConvertScalarFieldValue(
value[key], value_field) value[key], value_field)
@ -641,18 +647,18 @@ def _ConvertBool(value, require_str):
return value return value
_WKTJSONMETHODS = { _WKTJSONMETHODS = {
'google.protobuf.Any': [_AnyMessageToJsonObject, 'google.protobuf.Any': ['_AnyMessageToJsonObject',
_ConvertAnyMessage], '_ConvertAnyMessage'],
'google.protobuf.Duration': [_GenericMessageToJsonObject, 'google.protobuf.Duration': ['_GenericMessageToJsonObject',
_ConvertGenericMessage], '_ConvertGenericMessage'],
'google.protobuf.FieldMask': [_GenericMessageToJsonObject, 'google.protobuf.FieldMask': ['_GenericMessageToJsonObject',
_ConvertGenericMessage], '_ConvertGenericMessage'],
'google.protobuf.ListValue': [_ListValueMessageToJsonObject, 'google.protobuf.ListValue': ['_ListValueMessageToJsonObject',
_ConvertListValueMessage], '_ConvertListValueMessage'],
'google.protobuf.Struct': [_StructMessageToJsonObject, 'google.protobuf.Struct': ['_StructMessageToJsonObject',
_ConvertStructMessage], '_ConvertStructMessage'],
'google.protobuf.Timestamp': [_GenericMessageToJsonObject, 'google.protobuf.Timestamp': ['_GenericMessageToJsonObject',
_ConvertGenericMessage], '_ConvertGenericMessage'],
'google.protobuf.Value': [_ValueMessageToJsonObject, 'google.protobuf.Value': ['_ValueMessageToJsonObject',
_ConvertValueMessage] '_ConvertValueMessage']
} }

@ -172,12 +172,16 @@ template<>
const FileDescriptor* GetFileDescriptor(const OneofDescriptor* descriptor) { const FileDescriptor* GetFileDescriptor(const OneofDescriptor* descriptor) {
return descriptor->containing_type()->file(); return descriptor->containing_type()->file();
} }
template<>
const FileDescriptor* GetFileDescriptor(const MethodDescriptor* descriptor) {
return descriptor->service()->file();
}
// Converts options into a Python protobuf, and cache the result. // Converts options into a Python protobuf, and cache the result.
// //
// This is a bit tricky because options can contain extension fields defined in // This is a bit tricky because options can contain extension fields defined in
// the same proto file. In this case the options parsed from the serialized_pb // the same proto file. In this case the options parsed from the serialized_pb
// have unkown fields, and we need to parse them again. // have unknown fields, and we need to parse them again.
// //
// Always returns a new reference. // Always returns a new reference.
template<class DescriptorClass> template<class DescriptorClass>
@ -204,11 +208,12 @@ static PyObject* GetOrBuildOptions(const DescriptorClass *descriptor) {
cdescriptor_pool::GetMessageClass(pool, message_type)); cdescriptor_pool::GetMessageClass(pool, message_type));
if (message_class == NULL) { if (message_class == NULL) {
// The Options message was not found in the current DescriptorPool. // The Options message was not found in the current DescriptorPool.
// In this case, there cannot be extensions to these options, and we can // This means that the pool cannot contain any extensions to the Options
// try to use the basic pool instead. // message either, so falling back to the basic pool we can only increase
// the chances of successfully parsing the options.
PyErr_Clear(); PyErr_Clear();
message_class = cdescriptor_pool::GetMessageClass( pool = GetDefaultDescriptorPool();
GetDefaultDescriptorPool(), message_type); message_class = cdescriptor_pool::GetMessageClass(pool, message_type);
} }
if (message_class == NULL) { if (message_class == NULL) {
PyErr_Format(PyExc_TypeError, "Could not retrieve class for Options: %s", PyErr_Format(PyExc_TypeError, "Could not retrieve class for Options: %s",
@ -248,7 +253,7 @@ static PyObject* GetOrBuildOptions(const DescriptorClass *descriptor) {
// Cache the result. // Cache the result.
Py_INCREF(value.get()); Py_INCREF(value.get());
(*pool->descriptor_options)[descriptor] = value.get(); (*descriptor_options)[descriptor] = value.get();
return value.release(); return value.release();
} }
@ -1091,7 +1096,7 @@ PyTypeObject PyEnumDescriptor_Type = {
0, // tp_weaklistoffset 0, // tp_weaklistoffset
0, // tp_iter 0, // tp_iter
0, // tp_iternext 0, // tp_iternext
enum_descriptor::Methods, // tp_getset enum_descriptor::Methods, // tp_methods
0, // tp_members 0, // tp_members
enum_descriptor::Getters, // tp_getset enum_descriptor::Getters, // tp_getset
&descriptor::PyBaseDescriptor_Type, // tp_base &descriptor::PyBaseDescriptor_Type, // tp_base
@ -1275,6 +1280,10 @@ static PyObject* GetExtensionsByName(PyFileDescriptor* self, void *closure) {
return NewFileExtensionsByName(_GetDescriptor(self)); return NewFileExtensionsByName(_GetDescriptor(self));
} }
static PyObject* GetServicesByName(PyFileDescriptor* self, void *closure) {
return NewFileServicesByName(_GetDescriptor(self));
}
static PyObject* GetDependencies(PyFileDescriptor* self, void *closure) { static PyObject* GetDependencies(PyFileDescriptor* self, void *closure) {
return NewFileDependencies(_GetDescriptor(self)); return NewFileDependencies(_GetDescriptor(self));
} }
@ -1324,6 +1333,7 @@ static PyGetSetDef Getters[] = {
{ "enum_types_by_name", (getter)GetEnumTypesByName, NULL, "Enums by name"}, { "enum_types_by_name", (getter)GetEnumTypesByName, NULL, "Enums by name"},
{ "extensions_by_name", (getter)GetExtensionsByName, NULL, { "extensions_by_name", (getter)GetExtensionsByName, NULL,
"Extensions by name"}, "Extensions by name"},
{ "services_by_name", (getter)GetServicesByName, NULL, "Services by name"},
{ "dependencies", (getter)GetDependencies, NULL, "Dependencies"}, { "dependencies", (getter)GetDependencies, NULL, "Dependencies"},
{ "public_dependencies", (getter)GetPublicDependencies, NULL, "Dependencies"}, { "public_dependencies", (getter)GetPublicDependencies, NULL, "Dependencies"},
@ -1452,16 +1462,45 @@ static PyObject* GetContainingType(PyBaseDescriptor *self, void *closure) {
} }
} }
static PyObject* GetHasOptions(PyBaseDescriptor *self, void *closure) {
const OneofOptions& options(_GetDescriptor(self)->options());
if (&options != &OneofOptions::default_instance()) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
}
static int SetHasOptions(PyBaseDescriptor *self, PyObject *value,
void *closure) {
return CheckCalledFromGeneratedFile("has_options");
}
static PyObject* GetOptions(PyBaseDescriptor *self) {
return GetOrBuildOptions(_GetDescriptor(self));
}
static int SetOptions(PyBaseDescriptor *self, PyObject *value,
void *closure) {
return CheckCalledFromGeneratedFile("_options");
}
static PyGetSetDef Getters[] = { static PyGetSetDef Getters[] = {
{ "name", (getter)GetName, NULL, "Name"}, { "name", (getter)GetName, NULL, "Name"},
{ "full_name", (getter)GetFullName, NULL, "Full name"}, { "full_name", (getter)GetFullName, NULL, "Full name"},
{ "index", (getter)GetIndex, NULL, "Index"}, { "index", (getter)GetIndex, NULL, "Index"},
{ "containing_type", (getter)GetContainingType, NULL, "Containing type"}, { "containing_type", (getter)GetContainingType, NULL, "Containing type"},
{ "has_options", (getter)GetHasOptions, (setter)SetHasOptions, "Has Options"},
{ "_options", (getter)NULL, (setter)SetOptions, "Options"},
{ "fields", (getter)GetFields, NULL, "Fields"}, { "fields", (getter)GetFields, NULL, "Fields"},
{NULL} {NULL}
}; };
static PyMethodDef Methods[] = {
{ "GetOptions", (PyCFunction)GetOptions, METH_NOARGS },
{NULL}
};
} // namespace oneof_descriptor } // namespace oneof_descriptor
PyTypeObject PyOneofDescriptor_Type = { PyTypeObject PyOneofDescriptor_Type = {
@ -1492,7 +1531,7 @@ PyTypeObject PyOneofDescriptor_Type = {
0, // tp_weaklistoffset 0, // tp_weaklistoffset
0, // tp_iter 0, // tp_iter
0, // tp_iternext 0, // tp_iternext
0, // tp_methods oneof_descriptor::Methods, // tp_methods
0, // tp_members 0, // tp_members
oneof_descriptor::Getters, // tp_getset oneof_descriptor::Getters, // tp_getset
&descriptor::PyBaseDescriptor_Type, // tp_base &descriptor::PyBaseDescriptor_Type, // tp_base
@ -1504,6 +1543,222 @@ PyObject* PyOneofDescriptor_FromDescriptor(
&PyOneofDescriptor_Type, oneof_descriptor, NULL); &PyOneofDescriptor_Type, oneof_descriptor, NULL);
} }
namespace service_descriptor {
// Unchecked accessor to the C++ pointer.
static const ServiceDescriptor* _GetDescriptor(
PyBaseDescriptor *self) {
return reinterpret_cast<const ServiceDescriptor*>(self->descriptor);
}
static PyObject* GetName(PyBaseDescriptor* self, void *closure) {
return PyString_FromCppString(_GetDescriptor(self)->name());
}
static PyObject* GetFullName(PyBaseDescriptor* self, void *closure) {
return PyString_FromCppString(_GetDescriptor(self)->full_name());
}
static PyObject* GetIndex(PyBaseDescriptor *self, void *closure) {
return PyInt_FromLong(_GetDescriptor(self)->index());
}
static PyObject* GetMethods(PyBaseDescriptor* self, void *closure) {
return NewServiceMethodsSeq(_GetDescriptor(self));
}
static PyObject* GetMethodsByName(PyBaseDescriptor* self, void *closure) {
return NewServiceMethodsByName(_GetDescriptor(self));
}
static PyObject* FindMethodByName(PyBaseDescriptor *self, PyObject* arg) {
Py_ssize_t name_size;
char* name;
if (PyString_AsStringAndSize(arg, &name, &name_size) < 0) {
return NULL;
}
const MethodDescriptor* method_descriptor =
_GetDescriptor(self)->FindMethodByName(string(name, name_size));
if (method_descriptor == NULL) {
PyErr_Format(PyExc_KeyError, "Couldn't find method %.200s", name);
return NULL;
}
return PyMethodDescriptor_FromDescriptor(method_descriptor);
}
static PyObject* GetOptions(PyBaseDescriptor *self) {
return GetOrBuildOptions(_GetDescriptor(self));
}
static PyObject* CopyToProto(PyBaseDescriptor *self, PyObject *target) {
return CopyToPythonProto<ServiceDescriptorProto>(_GetDescriptor(self),
target);
}
static PyGetSetDef Getters[] = {
{ "name", (getter)GetName, NULL, "Name", NULL},
{ "full_name", (getter)GetFullName, NULL, "Full name", NULL},
{ "index", (getter)GetIndex, NULL, "Index", NULL},
{ "methods", (getter)GetMethods, NULL, "Methods", NULL},
{ "methods_by_name", (getter)GetMethodsByName, NULL, "Methods by name", NULL},
{NULL}
};
static PyMethodDef Methods[] = {
{ "GetOptions", (PyCFunction)GetOptions, METH_NOARGS },
{ "CopyToProto", (PyCFunction)CopyToProto, METH_O, },
{ "FindMethodByName", (PyCFunction)FindMethodByName, METH_O },
{NULL}
};
} // namespace service_descriptor
PyTypeObject PyServiceDescriptor_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
FULL_MODULE_NAME ".ServiceDescriptor", // tp_name
sizeof(PyBaseDescriptor), // tp_basicsize
0, // tp_itemsize
0, // 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
"A Service Descriptor", // tp_doc
0, // tp_traverse
0, // tp_clear
0, // tp_richcompare
0, // tp_weaklistoffset
0, // tp_iter
0, // tp_iternext
service_descriptor::Methods, // tp_methods
0, // tp_members
service_descriptor::Getters, // tp_getset
&descriptor::PyBaseDescriptor_Type, // tp_base
};
PyObject* PyServiceDescriptor_FromDescriptor(
const ServiceDescriptor* service_descriptor) {
return descriptor::NewInternedDescriptor(
&PyServiceDescriptor_Type, service_descriptor, NULL);
}
namespace method_descriptor {
// Unchecked accessor to the C++ pointer.
static const MethodDescriptor* _GetDescriptor(
PyBaseDescriptor *self) {
return reinterpret_cast<const MethodDescriptor*>(self->descriptor);
}
static PyObject* GetName(PyBaseDescriptor* self, void *closure) {
return PyString_FromCppString(_GetDescriptor(self)->name());
}
static PyObject* GetFullName(PyBaseDescriptor* self, void *closure) {
return PyString_FromCppString(_GetDescriptor(self)->full_name());
}
static PyObject* GetIndex(PyBaseDescriptor *self, void *closure) {
return PyInt_FromLong(_GetDescriptor(self)->index());
}
static PyObject* GetContainingService(PyBaseDescriptor *self, void *closure) {
const ServiceDescriptor* containing_service =
_GetDescriptor(self)->service();
return PyServiceDescriptor_FromDescriptor(containing_service);
}
static PyObject* GetInputType(PyBaseDescriptor *self, void *closure) {
const Descriptor* input_type = _GetDescriptor(self)->input_type();
return PyMessageDescriptor_FromDescriptor(input_type);
}
static PyObject* GetOutputType(PyBaseDescriptor *self, void *closure) {
const Descriptor* output_type = _GetDescriptor(self)->output_type();
return PyMessageDescriptor_FromDescriptor(output_type);
}
static PyObject* GetOptions(PyBaseDescriptor *self) {
return GetOrBuildOptions(_GetDescriptor(self));
}
static PyObject* CopyToProto(PyBaseDescriptor *self, PyObject *target) {
return CopyToPythonProto<MethodDescriptorProto>(_GetDescriptor(self), target);
}
static PyGetSetDef Getters[] = {
{ "name", (getter)GetName, NULL, "Name", NULL},
{ "full_name", (getter)GetFullName, NULL, "Full name", NULL},
{ "index", (getter)GetIndex, NULL, "Index", NULL},
{ "containing_service", (getter)GetContainingService, NULL,
"Containing service", NULL},
{ "input_type", (getter)GetInputType, NULL, "Input type", NULL},
{ "output_type", (getter)GetOutputType, NULL, "Output type", NULL},
{NULL}
};
static PyMethodDef Methods[] = {
{ "GetOptions", (PyCFunction)GetOptions, METH_NOARGS, },
{ "CopyToProto", (PyCFunction)CopyToProto, METH_O, },
{NULL}
};
} // namespace method_descriptor
PyTypeObject PyMethodDescriptor_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
FULL_MODULE_NAME ".MethodDescriptor", // tp_name
sizeof(PyBaseDescriptor), // tp_basicsize
0, // tp_itemsize
0, // 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
"A Method Descriptor", // tp_doc
0, // tp_traverse
0, // tp_clear
0, // tp_richcompare
0, // tp_weaklistoffset
0, // tp_iter
0, // tp_iternext
method_descriptor::Methods, // tp_methods
0, // tp_members
method_descriptor::Getters, // tp_getset
&descriptor::PyBaseDescriptor_Type, // tp_base
};
PyObject* PyMethodDescriptor_FromDescriptor(
const MethodDescriptor* method_descriptor) {
return descriptor::NewInternedDescriptor(
&PyMethodDescriptor_Type, method_descriptor, NULL);
}
// Add a enum values to a type dictionary. // Add a enum values to a type dictionary.
static bool AddEnumValues(PyTypeObject *type, static bool AddEnumValues(PyTypeObject *type,
const EnumDescriptor* enum_descriptor) { const EnumDescriptor* enum_descriptor) {
@ -1573,6 +1828,12 @@ bool InitDescriptor() {
if (PyType_Ready(&PyOneofDescriptor_Type) < 0) if (PyType_Ready(&PyOneofDescriptor_Type) < 0)
return false; return false;
if (PyType_Ready(&PyServiceDescriptor_Type) < 0)
return false;
if (PyType_Ready(&PyMethodDescriptor_Type) < 0)
return false;
if (!InitDescriptorMappingTypes()) if (!InitDescriptorMappingTypes())
return false; return false;

@ -47,6 +47,8 @@ extern PyTypeObject PyEnumDescriptor_Type;
extern PyTypeObject PyEnumValueDescriptor_Type; extern PyTypeObject PyEnumValueDescriptor_Type;
extern PyTypeObject PyFileDescriptor_Type; extern PyTypeObject PyFileDescriptor_Type;
extern PyTypeObject PyOneofDescriptor_Type; extern PyTypeObject PyOneofDescriptor_Type;
extern PyTypeObject PyServiceDescriptor_Type;
extern PyTypeObject PyMethodDescriptor_Type;
// Wraps a Descriptor in a Python object. // Wraps a Descriptor in a Python object.
// The C++ pointer is usually borrowed from the global DescriptorPool. // The C++ pointer is usually borrowed from the global DescriptorPool.
@ -60,6 +62,10 @@ PyObject* PyEnumValueDescriptor_FromDescriptor(
PyObject* PyOneofDescriptor_FromDescriptor(const OneofDescriptor* descriptor); PyObject* PyOneofDescriptor_FromDescriptor(const OneofDescriptor* descriptor);
PyObject* PyFileDescriptor_FromDescriptor( PyObject* PyFileDescriptor_FromDescriptor(
const FileDescriptor* file_descriptor); const FileDescriptor* file_descriptor);
PyObject* PyServiceDescriptor_FromDescriptor(
const ServiceDescriptor* descriptor);
PyObject* PyMethodDescriptor_FromDescriptor(
const MethodDescriptor* descriptor);
// Alternate constructor of PyFileDescriptor, used when we already have a // Alternate constructor of PyFileDescriptor, used when we already have a
// serialized FileDescriptorProto that can be cached. // serialized FileDescriptorProto that can be cached.

@ -608,6 +608,24 @@ static PyObject* GetItem(PyContainer* self, Py_ssize_t index) {
return _NewObj_ByIndex(self, index); return _NewObj_ByIndex(self, index);
} }
static PyObject *
SeqSubscript(PyContainer* self, PyObject* item) {
if (PyIndex_Check(item)) {
Py_ssize_t index;
index = PyNumber_AsSsize_t(item, PyExc_IndexError);
if (index == -1 && PyErr_Occurred())
return NULL;
return GetItem(self, index);
}
// Materialize the list and delegate the operation to it.
ScopedPyObjectPtr list(PyObject_CallFunctionObjArgs(
reinterpret_cast<PyObject*>(&PyList_Type), self, NULL));
if (list == NULL) {
return NULL;
}
return Py_TYPE(list.get())->tp_as_mapping->mp_subscript(list.get(), item);
}
// Returns the position of the item in the sequence, of -1 if not found. // Returns the position of the item in the sequence, of -1 if not found.
// This function never fails. // This function never fails.
int Find(PyContainer* self, PyObject* item) { int Find(PyContainer* self, PyObject* item) {
@ -713,6 +731,12 @@ static PySequenceMethods SeqSequenceMethods = {
(objobjproc)SeqContains, // sq_contains (objobjproc)SeqContains, // sq_contains
}; };
static PyMappingMethods SeqMappingMethods = {
(lenfunc)Length, // mp_length
(binaryfunc)SeqSubscript, // mp_subscript
0, // mp_ass_subscript
};
PyTypeObject DescriptorSequence_Type = { PyTypeObject DescriptorSequence_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0) PyVarObject_HEAD_INIT(&PyType_Type, 0)
"DescriptorSequence", // tp_name "DescriptorSequence", // tp_name
@ -726,7 +750,7 @@ PyTypeObject DescriptorSequence_Type = {
(reprfunc)ContainerRepr, // tp_repr (reprfunc)ContainerRepr, // tp_repr
0, // tp_as_number 0, // tp_as_number
&SeqSequenceMethods, // tp_as_sequence &SeqSequenceMethods, // tp_as_sequence
0, // tp_as_mapping &SeqMappingMethods, // tp_as_mapping
0, // tp_hash 0, // tp_hash
0, // tp_call 0, // tp_call
0, // tp_str 0, // tp_str
@ -1407,6 +1431,68 @@ PyObject* NewOneofFieldsSeq(ParentDescriptor descriptor) {
} // namespace oneof_descriptor } // namespace oneof_descriptor
namespace service_descriptor {
typedef const ServiceDescriptor* ParentDescriptor;
static ParentDescriptor GetDescriptor(PyContainer* self) {
return reinterpret_cast<ParentDescriptor>(self->descriptor);
}
namespace methods {
typedef const MethodDescriptor* ItemDescriptor;
static int Count(PyContainer* self) {
return GetDescriptor(self)->method_count();
}
static ItemDescriptor GetByName(PyContainer* self, const string& name) {
return GetDescriptor(self)->FindMethodByName(name);
}
static ItemDescriptor GetByIndex(PyContainer* self, int index) {
return GetDescriptor(self)->method(index);
}
static PyObject* NewObjectFromItem(ItemDescriptor item) {
return PyMethodDescriptor_FromDescriptor(item);
}
static const string& GetItemName(ItemDescriptor item) {
return item->name();
}
static int GetItemIndex(ItemDescriptor item) {
return item->index();
}
static DescriptorContainerDef ContainerDef = {
"ServiceMethods",
(CountMethod)Count,
(GetByIndexMethod)GetByIndex,
(GetByNameMethod)GetByName,
(GetByCamelcaseNameMethod)NULL,
(GetByNumberMethod)NULL,
(NewObjectFromItemMethod)NewObjectFromItem,
(GetItemNameMethod)GetItemName,
(GetItemCamelcaseNameMethod)NULL,
(GetItemNumberMethod)NULL,
(GetItemIndexMethod)GetItemIndex,
};
} // namespace methods
PyObject* NewServiceMethodsSeq(ParentDescriptor descriptor) {
return descriptor::NewSequence(&methods::ContainerDef, descriptor);
}
PyObject* NewServiceMethodsByName(ParentDescriptor descriptor) {
return descriptor::NewMappingByName(&methods::ContainerDef, descriptor);
}
} // namespace service_descriptor
namespace file_descriptor { namespace file_descriptor {
typedef const FileDescriptor* ParentDescriptor; typedef const FileDescriptor* ParentDescriptor;
@ -1459,7 +1545,7 @@ static DescriptorContainerDef ContainerDef = {
} // namespace messages } // namespace messages
PyObject* NewFileMessageTypesByName(const FileDescriptor* descriptor) { PyObject* NewFileMessageTypesByName(ParentDescriptor descriptor) {
return descriptor::NewMappingByName(&messages::ContainerDef, descriptor); return descriptor::NewMappingByName(&messages::ContainerDef, descriptor);
} }
@ -1507,7 +1593,7 @@ static DescriptorContainerDef ContainerDef = {
} // namespace enums } // namespace enums
PyObject* NewFileEnumTypesByName(const FileDescriptor* descriptor) { PyObject* NewFileEnumTypesByName(ParentDescriptor descriptor) {
return descriptor::NewMappingByName(&enums::ContainerDef, descriptor); return descriptor::NewMappingByName(&enums::ContainerDef, descriptor);
} }
@ -1555,10 +1641,58 @@ static DescriptorContainerDef ContainerDef = {
} // namespace extensions } // namespace extensions
PyObject* NewFileExtensionsByName(const FileDescriptor* descriptor) { PyObject* NewFileExtensionsByName(ParentDescriptor descriptor) {
return descriptor::NewMappingByName(&extensions::ContainerDef, descriptor); return descriptor::NewMappingByName(&extensions::ContainerDef, descriptor);
} }
namespace services {
typedef const ServiceDescriptor* ItemDescriptor;
static int Count(PyContainer* self) {
return GetDescriptor(self)->service_count();
}
static ItemDescriptor GetByName(PyContainer* self, const string& name) {
return GetDescriptor(self)->FindServiceByName(name);
}
static ItemDescriptor GetByIndex(PyContainer* self, int index) {
return GetDescriptor(self)->service(index);
}
static PyObject* NewObjectFromItem(ItemDescriptor item) {
return PyServiceDescriptor_FromDescriptor(item);
}
static const string& GetItemName(ItemDescriptor item) {
return item->name();
}
static int GetItemIndex(ItemDescriptor item) {
return item->index();
}
static DescriptorContainerDef ContainerDef = {
"FileServices",
(CountMethod)Count,
(GetByIndexMethod)GetByIndex,
(GetByNameMethod)GetByName,
(GetByCamelcaseNameMethod)NULL,
(GetByNumberMethod)NULL,
(NewObjectFromItemMethod)NewObjectFromItem,
(GetItemNameMethod)GetItemName,
(GetItemCamelcaseNameMethod)NULL,
(GetItemNumberMethod)NULL,
(GetItemIndexMethod)GetItemIndex,
};
} // namespace services
PyObject* NewFileServicesByName(const FileDescriptor* descriptor) {
return descriptor::NewMappingByName(&services::ContainerDef, descriptor);
}
namespace dependencies { namespace dependencies {
typedef const FileDescriptor* ItemDescriptor; typedef const FileDescriptor* ItemDescriptor;

@ -43,6 +43,7 @@ class Descriptor;
class FileDescriptor; class FileDescriptor;
class EnumDescriptor; class EnumDescriptor;
class OneofDescriptor; class OneofDescriptor;
class ServiceDescriptor;
namespace python { namespace python {
@ -89,10 +90,17 @@ PyObject* NewFileEnumTypesByName(const FileDescriptor* descriptor);
PyObject* NewFileExtensionsByName(const FileDescriptor* descriptor); PyObject* NewFileExtensionsByName(const FileDescriptor* descriptor);
PyObject* NewFileServicesByName(const FileDescriptor* descriptor);
PyObject* NewFileDependencies(const FileDescriptor* descriptor); PyObject* NewFileDependencies(const FileDescriptor* descriptor);
PyObject* NewFilePublicDependencies(const FileDescriptor* descriptor); PyObject* NewFilePublicDependencies(const FileDescriptor* descriptor);
} // namespace file_descriptor } // namespace file_descriptor
namespace service_descriptor {
PyObject* NewServiceMethodsSeq(const ServiceDescriptor* descriptor);
PyObject* NewServiceMethodsByName(const ServiceDescriptor* descriptor);
} // namespace service_descriptor
} // namespace python } // namespace python
} // namespace protobuf } // namespace protobuf

@ -305,6 +305,40 @@ PyObject* FindOneofByName(PyDescriptorPool* self, PyObject* arg) {
return PyOneofDescriptor_FromDescriptor(oneof_descriptor); return PyOneofDescriptor_FromDescriptor(oneof_descriptor);
} }
PyObject* FindServiceByName(PyDescriptorPool* self, PyObject* arg) {
Py_ssize_t name_size;
char* name;
if (PyString_AsStringAndSize(arg, &name, &name_size) < 0) {
return NULL;
}
const ServiceDescriptor* service_descriptor =
self->pool->FindServiceByName(string(name, name_size));
if (service_descriptor == NULL) {
PyErr_Format(PyExc_KeyError, "Couldn't find service %.200s", name);
return NULL;
}
return PyServiceDescriptor_FromDescriptor(service_descriptor);
}
PyObject* FindMethodByName(PyDescriptorPool* self, PyObject* arg) {
Py_ssize_t name_size;
char* name;
if (PyString_AsStringAndSize(arg, &name, &name_size) < 0) {
return NULL;
}
const MethodDescriptor* method_descriptor =
self->pool->FindMethodByName(string(name, name_size));
if (method_descriptor == NULL) {
PyErr_Format(PyExc_KeyError, "Couldn't find method %.200s", name);
return NULL;
}
return PyMethodDescriptor_FromDescriptor(method_descriptor);
}
PyObject* FindFileContainingSymbol(PyDescriptorPool* self, PyObject* arg) { PyObject* FindFileContainingSymbol(PyDescriptorPool* self, PyObject* arg) {
Py_ssize_t name_size; Py_ssize_t name_size;
char* name; char* name;
@ -491,6 +525,10 @@ static PyMethodDef Methods[] = {
"Searches for enum type descriptor by full name." }, "Searches for enum type descriptor by full name." },
{ "FindOneofByName", (PyCFunction)FindOneofByName, METH_O, { "FindOneofByName", (PyCFunction)FindOneofByName, METH_O,
"Searches for oneof descriptor by full name." }, "Searches for oneof descriptor by full name." },
{ "FindServiceByName", (PyCFunction)FindServiceByName, METH_O,
"Searches for service descriptor by full name." },
{ "FindMethodByName", (PyCFunction)FindMethodByName, METH_O,
"Searches for method descriptor by full name." },
{ "FindFileContainingSymbol", (PyCFunction)FindFileContainingSymbol, METH_O, { "FindFileContainingSymbol", (PyCFunction)FindFileContainingSymbol, METH_O,
"Gets the FileDescriptor containing the specified symbol." }, "Gets the FileDescriptor containing the specified symbol." },

@ -39,7 +39,6 @@
#include <google/protobuf/stubs/logging.h> #include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h> #include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/scoped_ptr.h>
#include <google/protobuf/map_field.h> #include <google/protobuf/map_field.h>
#include <google/protobuf/map.h> #include <google/protobuf/map.h>
#include <google/protobuf/message.h> #include <google/protobuf/message.h>

@ -1593,23 +1593,20 @@ struct ReleaseChild : public ChildVisitor {
parent_(parent) {} parent_(parent) {}
int VisitRepeatedCompositeContainer(RepeatedCompositeContainer* container) { int VisitRepeatedCompositeContainer(RepeatedCompositeContainer* container) {
return repeated_composite_container::Release( return repeated_composite_container::Release(container);
reinterpret_cast<RepeatedCompositeContainer*>(container));
} }
int VisitRepeatedScalarContainer(RepeatedScalarContainer* container) { int VisitRepeatedScalarContainer(RepeatedScalarContainer* container) {
return repeated_scalar_container::Release( return repeated_scalar_container::Release(container);
reinterpret_cast<RepeatedScalarContainer*>(container));
} }
int VisitMapContainer(MapContainer* container) { int VisitMapContainer(MapContainer* container) {
return reinterpret_cast<MapContainer*>(container)->Release(); return container->Release();
} }
int VisitCMessage(CMessage* cmessage, int VisitCMessage(CMessage* cmessage,
const FieldDescriptor* field_descriptor) { const FieldDescriptor* field_descriptor) {
return ReleaseSubMessage(parent_, field_descriptor, return ReleaseSubMessage(parent_, field_descriptor, cmessage);
reinterpret_cast<CMessage*>(cmessage));
} }
CMessage* parent_; CMessage* parent_;
@ -1903,7 +1900,7 @@ static bool allow_oversize_protos = false;
// Provide a method in the module to set allow_oversize_protos to a boolean // Provide a method in the module to set allow_oversize_protos to a boolean
// value. This method returns the newly value of allow_oversize_protos. // value. This method returns the newly value of allow_oversize_protos.
static PyObject* SetAllowOversizeProtos(PyObject* m, PyObject* arg) { PyObject* SetAllowOversizeProtos(PyObject* m, PyObject* arg) {
if (!arg || !PyBool_Check(arg)) { if (!arg || !PyBool_Check(arg)) {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"Argument to SetAllowOversizeProtos must be boolean"); "Argument to SetAllowOversizeProtos must be boolean");
@ -3044,6 +3041,10 @@ bool InitProto2MessageModule(PyObject *m) {
&PyFileDescriptor_Type)); &PyFileDescriptor_Type));
PyModule_AddObject(m, "OneofDescriptor", reinterpret_cast<PyObject*>( PyModule_AddObject(m, "OneofDescriptor", reinterpret_cast<PyObject*>(
&PyOneofDescriptor_Type)); &PyOneofDescriptor_Type));
PyModule_AddObject(m, "ServiceDescriptor", reinterpret_cast<PyObject*>(
&PyServiceDescriptor_Type));
PyModule_AddObject(m, "MethodDescriptor", reinterpret_cast<PyObject*>(
&PyMethodDescriptor_Type));
PyObject* enum_type_wrapper = PyImport_ImportModule( PyObject* enum_type_wrapper = PyImport_ImportModule(
"google.protobuf.internal.enum_type_wrapper"); "google.protobuf.internal.enum_type_wrapper");
@ -3081,53 +3082,4 @@ bool InitProto2MessageModule(PyObject *m) {
} // namespace python } // namespace python
} // namespace protobuf } // namespace protobuf
static PyMethodDef ModuleMethods[] = {
{"SetAllowOversizeProtos",
(PyCFunction)google::protobuf::python::cmessage::SetAllowOversizeProtos,
METH_O, "Enable/disable oversize proto parsing."},
{ NULL, NULL}
};
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef _module = {
PyModuleDef_HEAD_INIT,
"_message",
google::protobuf::python::module_docstring,
-1,
ModuleMethods, /* m_methods */
NULL,
NULL,
NULL,
NULL
};
#define INITFUNC PyInit__message
#define INITFUNC_ERRORVAL NULL
#else // Python 2
#define INITFUNC init_message
#define INITFUNC_ERRORVAL
#endif
extern "C" {
PyMODINIT_FUNC INITFUNC(void) {
PyObject* m;
#if PY_MAJOR_VERSION >= 3
m = PyModule_Create(&_module);
#else
m = Py_InitModule3("_message", ModuleMethods,
google::protobuf::python::module_docstring);
#endif
if (m == NULL) {
return INITFUNC_ERRORVAL;
}
if (!google::protobuf::python::InitProto2MessageModule(m)) {
Py_DECREF(m);
return INITFUNC_ERRORVAL;
}
#if PY_MAJOR_VERSION >= 3
return m;
#endif
}
}
} // namespace google } // namespace google

@ -54,7 +54,7 @@ class MessageFactory;
#ifdef _SHARED_PTR_H #ifdef _SHARED_PTR_H
using std::shared_ptr; using std::shared_ptr;
using ::std::string; using std::string;
#else #else
using internal::shared_ptr; using internal::shared_ptr;
#endif #endif
@ -269,6 +269,8 @@ int AssureWritable(CMessage* self);
// even in the case of extensions. // even in the case of extensions.
PyDescriptorPool* GetDescriptorPoolForMessage(CMessage* message); PyDescriptorPool* GetDescriptorPoolForMessage(CMessage* message);
PyObject* SetAllowOversizeProtos(PyObject* m, PyObject* arg);
} // namespace cmessage } // namespace cmessage
@ -354,6 +356,8 @@ bool CheckFieldBelongsToMessage(const FieldDescriptor* field_descriptor,
extern PyObject* PickleError_class; extern PyObject* PickleError_class;
bool InitProto2MessageModule(PyObject *m);
} // namespace python } // namespace python
} // namespace protobuf } // namespace protobuf

@ -0,0 +1,88 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <google/protobuf/pyext/message.h>
static const char module_docstring[] =
"python-proto2 is a module that can be used to enhance proto2 Python API\n"
"performance.\n"
"\n"
"It provides access to the protocol buffers C++ reflection API that\n"
"implements the basic protocol buffer functions.";
static PyMethodDef ModuleMethods[] = {
{"SetAllowOversizeProtos",
(PyCFunction)google::protobuf::python::cmessage::SetAllowOversizeProtos,
METH_O, "Enable/disable oversize proto parsing."},
{ NULL, NULL}
};
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef _module = {
PyModuleDef_HEAD_INIT,
"_message",
module_docstring,
-1,
ModuleMethods, /* m_methods */
NULL,
NULL,
NULL,
NULL
};
#define INITFUNC PyInit__message
#define INITFUNC_ERRORVAL NULL
#else // Python 2
#define INITFUNC init_message
#define INITFUNC_ERRORVAL
#endif
extern "C" {
PyMODINIT_FUNC INITFUNC(void) {
PyObject* m;
#if PY_MAJOR_VERSION >= 3
m = PyModule_Create(&_module);
#else
m = Py_InitModule3("_message", ModuleMethods,
module_docstring);
#endif
if (m == NULL) {
return INITFUNC_ERRORVAL;
}
if (!google::protobuf::python::InitProto2MessageModule(m)) {
Py_DECREF(m);
return INITFUNC_ERRORVAL;
}
#if PY_MAJOR_VERSION >= 3
return m;
#endif
}
}

@ -48,15 +48,15 @@ import re
import six import six
if six.PY3: if six.PY3:
long = int long = int # pylint: disable=redefined-builtin,invalid-name
# pylint: disable=g-import-not-at-top
from google.protobuf.internal import type_checkers from google.protobuf.internal import type_checkers
from google.protobuf import descriptor from google.protobuf import descriptor
from google.protobuf import text_encoding from google.protobuf import text_encoding
__all__ = ['MessageToString', 'PrintMessage', 'PrintField', __all__ = ['MessageToString', 'PrintMessage', 'PrintField', 'PrintFieldValue',
'PrintFieldValue', 'Merge'] 'Merge']
_INTEGER_CHECKERS = (type_checkers.Uint32ValueChecker(), _INTEGER_CHECKERS = (type_checkers.Uint32ValueChecker(),
type_checkers.Int32ValueChecker(), type_checkers.Int32ValueChecker(),
@ -67,6 +67,7 @@ _FLOAT_NAN = re.compile('nanf?', re.IGNORECASE)
_FLOAT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_FLOAT, _FLOAT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_FLOAT,
descriptor.FieldDescriptor.CPPTYPE_DOUBLE]) descriptor.FieldDescriptor.CPPTYPE_DOUBLE])
_QUOTES = frozenset(("'", '"')) _QUOTES = frozenset(("'", '"'))
_ANY_FULL_TYPE_NAME = 'google.protobuf.Any'
class Error(Exception): class Error(Exception):
@ -74,10 +75,30 @@ class Error(Exception):
class ParseError(Error): class ParseError(Error):
"""Thrown in case of text parsing error.""" """Thrown in case of text parsing or tokenizing error."""
def __init__(self, message=None, line=None, column=None):
if message is not None and line is not None:
loc = str(line)
if column is not None:
loc += ':{}'.format(column)
message = '{} : {}'.format(loc, message)
if message is not None:
super(ParseError, self).__init__(message)
else:
super(ParseError, self).__init__()
self._line = line
self._column = column
def GetLine(self):
return self._line
def GetColumn(self):
return self._column
class TextWriter(object): class TextWriter(object):
def __init__(self, as_utf8): def __init__(self, as_utf8):
if six.PY2: if six.PY2:
self._writer = io.BytesIO() self._writer = io.BytesIO()
@ -97,9 +118,15 @@ class TextWriter(object):
return self._writer.getvalue() return self._writer.getvalue()
def MessageToString(message, as_utf8=False, as_one_line=False, def MessageToString(message,
pointy_brackets=False, use_index_order=False, as_utf8=False,
float_format=None, use_field_number=False): as_one_line=False,
pointy_brackets=False,
use_index_order=False,
float_format=None,
use_field_number=False,
descriptor_pool=None,
indent=0):
"""Convert protobuf message to text format. """Convert protobuf message to text format.
Floating point values can be formatted compactly with 15 digits of Floating point values can be formatted compactly with 15 digits of
@ -119,14 +146,16 @@ def MessageToString(message, as_utf8=False, as_one_line=False,
float_format: If set, use this to specify floating point number formatting float_format: If set, use this to specify floating point number formatting
(per the "Format Specification Mini-Language"); otherwise, str() is used. (per the "Format Specification Mini-Language"); otherwise, str() is used.
use_field_number: If True, print field numbers instead of names. use_field_number: If True, print field numbers instead of names.
descriptor_pool: A DescriptorPool used to resolve Any types.
indent: The indent level, in terms of spaces, for pretty print.
Returns: Returns:
A string of the text formatted protocol buffer message. A string of the text formatted protocol buffer message.
""" """
out = TextWriter(as_utf8) out = TextWriter(as_utf8)
printer = _Printer(out, 0, as_utf8, as_one_line, printer = _Printer(out, indent, as_utf8, as_one_line, pointy_brackets,
pointy_brackets, use_index_order, float_format, use_index_order, float_format, use_field_number,
use_field_number) descriptor_pool)
printer.PrintMessage(message) printer.PrintMessage(message)
result = out.getvalue() result = out.getvalue()
out.close() out.close()
@ -141,39 +170,87 @@ def _IsMapEntry(field):
field.message_type.GetOptions().map_entry) field.message_type.GetOptions().map_entry)
def PrintMessage(message, out, indent=0, as_utf8=False, as_one_line=False, def PrintMessage(message,
pointy_brackets=False, use_index_order=False, out,
float_format=None, use_field_number=False): indent=0,
printer = _Printer(out, indent, as_utf8, as_one_line, as_utf8=False,
pointy_brackets, use_index_order, float_format, as_one_line=False,
use_field_number) pointy_brackets=False,
use_index_order=False,
float_format=None,
use_field_number=False,
descriptor_pool=None):
printer = _Printer(out, indent, as_utf8, as_one_line, pointy_brackets,
use_index_order, float_format, use_field_number,
descriptor_pool)
printer.PrintMessage(message) printer.PrintMessage(message)
def PrintField(field, value, out, indent=0, as_utf8=False, as_one_line=False, def PrintField(field,
pointy_brackets=False, use_index_order=False, float_format=None): value,
out,
indent=0,
as_utf8=False,
as_one_line=False,
pointy_brackets=False,
use_index_order=False,
float_format=None):
"""Print a single field name/value pair.""" """Print a single field name/value pair."""
printer = _Printer(out, indent, as_utf8, as_one_line, printer = _Printer(out, indent, as_utf8, as_one_line, pointy_brackets,
pointy_brackets, use_index_order, float_format) use_index_order, float_format)
printer.PrintField(field, value) printer.PrintField(field, value)
def PrintFieldValue(field, value, out, indent=0, as_utf8=False, def PrintFieldValue(field,
as_one_line=False, pointy_brackets=False, value,
out,
indent=0,
as_utf8=False,
as_one_line=False,
pointy_brackets=False,
use_index_order=False, use_index_order=False,
float_format=None): float_format=None):
"""Print a single field value (not including name).""" """Print a single field value (not including name)."""
printer = _Printer(out, indent, as_utf8, as_one_line, printer = _Printer(out, indent, as_utf8, as_one_line, pointy_brackets,
pointy_brackets, use_index_order, float_format) use_index_order, float_format)
printer.PrintFieldValue(field, value) printer.PrintFieldValue(field, value)
def _BuildMessageFromTypeName(type_name, descriptor_pool):
"""Returns a protobuf message instance.
Args:
type_name: Fully-qualified protobuf message type name string.
descriptor_pool: DescriptorPool instance.
Returns:
A Message instance of type matching type_name, or None if the a Descriptor
wasn't found matching type_name.
"""
# pylint: disable=g-import-not-at-top
from google.protobuf import message_factory
factory = message_factory.MessageFactory(descriptor_pool)
try:
message_descriptor = descriptor_pool.FindMessageTypeByName(type_name)
except KeyError:
return None
message_type = factory.GetPrototype(message_descriptor)
return message_type()
class _Printer(object): class _Printer(object):
"""Text format printer for protocol message.""" """Text format printer for protocol message."""
def __init__(self, out, indent=0, as_utf8=False, as_one_line=False, def __init__(self,
pointy_brackets=False, use_index_order=False, float_format=None, out,
use_field_number=False): indent=0,
as_utf8=False,
as_one_line=False,
pointy_brackets=False,
use_index_order=False,
float_format=None,
use_field_number=False,
descriptor_pool=None):
"""Initialize the Printer. """Initialize the Printer.
Floating point values can be formatted compactly with 15 digits of Floating point values can be formatted compactly with 15 digits of
@ -195,6 +272,7 @@ class _Printer(object):
(per the "Format Specification Mini-Language"); otherwise, str() is (per the "Format Specification Mini-Language"); otherwise, str() is
used. used.
use_field_number: If True, print field numbers instead of names. use_field_number: If True, print field numbers instead of names.
descriptor_pool: A DescriptorPool used to resolve Any types.
""" """
self.out = out self.out = out
self.indent = indent self.indent = indent
@ -204,6 +282,20 @@ class _Printer(object):
self.use_index_order = use_index_order self.use_index_order = use_index_order
self.float_format = float_format self.float_format = float_format
self.use_field_number = use_field_number self.use_field_number = use_field_number
self.descriptor_pool = descriptor_pool
def _TryPrintAsAnyMessage(self, message):
"""Serializes if message is a google.protobuf.Any field."""
packed_message = _BuildMessageFromTypeName(message.TypeName(),
self.descriptor_pool)
if packed_message:
packed_message.MergeFromString(message.value)
self.out.write('%s[%s]' % (self.indent * ' ', message.type_url))
self._PrintMessageFieldValue(packed_message)
self.out.write(' ' if self.as_one_line else '\n')
return True
else:
return False
def PrintMessage(self, message): def PrintMessage(self, message):
"""Convert protobuf message to text format. """Convert protobuf message to text format.
@ -211,6 +303,9 @@ class _Printer(object):
Args: Args:
message: The protocol buffers message. message: The protocol buffers message.
""" """
if (message.DESCRIPTOR.full_name == _ANY_FULL_TYPE_NAME and
self.descriptor_pool and self._TryPrintAsAnyMessage(message)):
return
fields = message.ListFields() fields = message.ListFields()
if self.use_index_order: if self.use_index_order:
fields.sort(key=lambda x: x[0].index) fields.sort(key=lambda x: x[0].index)
@ -222,8 +317,8 @@ class _Printer(object):
# of this file to work around. # of this file to work around.
# #
# TODO(haberman): refactor and optimize if this becomes an issue. # TODO(haberman): refactor and optimize if this becomes an issue.
entry_submsg = field.message_type._concrete_class( entry_submsg = field.message_type._concrete_class(key=key,
key=key, value=value[key]) value=value[key])
self.PrintField(field, entry_submsg) self.PrintField(field, entry_submsg)
elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
for element in value: for element in value:
@ -264,16 +359,7 @@ class _Printer(object):
else: else:
out.write('\n') out.write('\n')
def PrintFieldValue(self, field, value): def _PrintMessageFieldValue(self, value):
"""Print a single field value (not including name).
For repeated fields, the value should be a single element.
Args:
field: The descriptor of the field to be printed.
value: The value of the field.
"""
out = self.out
if self.pointy_brackets: if self.pointy_brackets:
openb = '<' openb = '<'
closeb = '>' closeb = '>'
@ -281,17 +367,29 @@ class _Printer(object):
openb = '{' openb = '{'
closeb = '}' closeb = '}'
if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
if self.as_one_line: if self.as_one_line:
out.write(' %s ' % openb) self.out.write(' %s ' % openb)
self.PrintMessage(value) self.PrintMessage(value)
out.write(closeb) self.out.write(closeb)
else: else:
out.write(' %s\n' % openb) self.out.write(' %s\n' % openb)
self.indent += 2 self.indent += 2
self.PrintMessage(value) self.PrintMessage(value)
self.indent -= 2 self.indent -= 2
out.write(' ' * self.indent + closeb) self.out.write(' ' * self.indent + closeb)
def PrintFieldValue(self, field, value):
"""Print a single field value (not including name).
For repeated fields, the value should be a single element.
Args:
field: The descriptor of the field to be printed.
value: The value of the field.
"""
out = self.out
if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
self._PrintMessageFieldValue(value)
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM:
enum_value = field.enum_type.values_by_number.get(value, None) enum_value = field.enum_type.values_by_number.get(value, None)
if enum_value is not None: if enum_value is not None:
@ -322,9 +420,11 @@ class _Printer(object):
out.write(str(value)) out.write(str(value))
def Parse(text, message, def Parse(text,
allow_unknown_extension=False, allow_field_number=False): message,
"""Parses an text representation of a protocol message into a message. allow_unknown_extension=False,
allow_field_number=False):
"""Parses a text representation of a protocol message into a message.
Args: Args:
text: Message text representation. text: Message text representation.
@ -341,13 +441,16 @@ def Parse(text, message,
""" """
if not isinstance(text, str): if not isinstance(text, str):
text = text.decode('utf-8') text = text.decode('utf-8')
return ParseLines(text.split('\n'), message, allow_unknown_extension, return ParseLines(
allow_field_number) text.split('\n'), message, allow_unknown_extension, allow_field_number)
def Merge(text, message, allow_unknown_extension=False, def Merge(text,
allow_field_number=False): message,
"""Parses an text representation of a protocol message into a message. allow_unknown_extension=False,
allow_field_number=False,
descriptor_pool=None):
"""Parses a text representation of a protocol message into a message.
Like Parse(), but allows repeated values for a non-repeated field, and uses Like Parse(), but allows repeated values for a non-repeated field, and uses
the last one. the last one.
@ -358,6 +461,7 @@ def Merge(text, message, allow_unknown_extension=False,
allow_unknown_extension: if True, skip over missing extensions and keep allow_unknown_extension: if True, skip over missing extensions and keep
parsing parsing
allow_field_number: if True, both field number and field name are allowed. allow_field_number: if True, both field number and field name are allowed.
descriptor_pool: A DescriptorPool used to resolve Any types.
Returns: Returns:
The same message passed as argument. The same message passed as argument.
@ -365,13 +469,19 @@ def Merge(text, message, allow_unknown_extension=False,
Raises: Raises:
ParseError: On text parsing problems. ParseError: On text parsing problems.
""" """
return MergeLines(text.split('\n'), message, allow_unknown_extension, return MergeLines(
allow_field_number) text.split('\n'),
message,
allow_unknown_extension,
allow_field_number,
descriptor_pool=descriptor_pool)
def ParseLines(lines, message, allow_unknown_extension=False, def ParseLines(lines,
message,
allow_unknown_extension=False,
allow_field_number=False): allow_field_number=False):
"""Parses an text representation of a protocol message into a message. """Parses a text representation of a protocol message into a message.
Args: Args:
lines: An iterable of lines of a message's text representation. lines: An iterable of lines of a message's text representation.
@ -379,6 +489,7 @@ def ParseLines(lines, message, allow_unknown_extension=False,
allow_unknown_extension: if True, skip over missing extensions and keep allow_unknown_extension: if True, skip over missing extensions and keep
parsing parsing
allow_field_number: if True, both field number and field name are allowed. allow_field_number: if True, both field number and field name are allowed.
descriptor_pool: A DescriptorPool used to resolve Any types.
Returns: Returns:
The same message passed as argument. The same message passed as argument.
@ -390,9 +501,12 @@ def ParseLines(lines, message, allow_unknown_extension=False,
return parser.ParseLines(lines, message) return parser.ParseLines(lines, message)
def MergeLines(lines, message, allow_unknown_extension=False, def MergeLines(lines,
allow_field_number=False): message,
"""Parses an text representation of a protocol message into a message. allow_unknown_extension=False,
allow_field_number=False,
descriptor_pool=None):
"""Parses a text representation of a protocol message into a message.
Args: Args:
lines: An iterable of lines of a message's text representation. lines: An iterable of lines of a message's text representation.
@ -407,41 +521,47 @@ def MergeLines(lines, message, allow_unknown_extension=False,
Raises: Raises:
ParseError: On text parsing problems. ParseError: On text parsing problems.
""" """
parser = _Parser(allow_unknown_extension, allow_field_number) parser = _Parser(allow_unknown_extension,
allow_field_number,
descriptor_pool=descriptor_pool)
return parser.MergeLines(lines, message) return parser.MergeLines(lines, message)
class _Parser(object): class _Parser(object):
"""Text format parser for protocol message.""" """Text format parser for protocol message."""
def __init__(self, allow_unknown_extension=False, allow_field_number=False): def __init__(self,
allow_unknown_extension=False,
allow_field_number=False,
descriptor_pool=None):
self.allow_unknown_extension = allow_unknown_extension self.allow_unknown_extension = allow_unknown_extension
self.allow_field_number = allow_field_number self.allow_field_number = allow_field_number
self.descriptor_pool = descriptor_pool
def ParseFromString(self, text, message): def ParseFromString(self, text, message):
"""Parses an text representation of a protocol message into a message.""" """Parses a text representation of a protocol message into a message."""
if not isinstance(text, str): if not isinstance(text, str):
text = text.decode('utf-8') text = text.decode('utf-8')
return self.ParseLines(text.split('\n'), message) return self.ParseLines(text.split('\n'), message)
def ParseLines(self, lines, message): def ParseLines(self, lines, message):
"""Parses an text representation of a protocol message into a message.""" """Parses a text representation of a protocol message into a message."""
self._allow_multiple_scalars = False self._allow_multiple_scalars = False
self._ParseOrMerge(lines, message) self._ParseOrMerge(lines, message)
return message return message
def MergeFromString(self, text, message): def MergeFromString(self, text, message):
"""Merges an text representation of a protocol message into a message.""" """Merges a text representation of a protocol message into a message."""
return self._MergeLines(text.split('\n'), message) return self._MergeLines(text.split('\n'), message)
def MergeLines(self, lines, message): def MergeLines(self, lines, message):
"""Merges an text representation of a protocol message into a message.""" """Merges a text representation of a protocol message into a message."""
self._allow_multiple_scalars = True self._allow_multiple_scalars = True
self._ParseOrMerge(lines, message) self._ParseOrMerge(lines, message)
return message return message
def _ParseOrMerge(self, lines, message): def _ParseOrMerge(self, lines, message):
"""Converts an text representation of a protocol message into a message. """Converts a text representation of a protocol message into a message.
Args: Args:
lines: Lines of a message's text representation. lines: Lines of a message's text representation.
@ -450,7 +570,7 @@ class _Parser(object):
Raises: Raises:
ParseError: On text parsing problems. ParseError: On text parsing problems.
""" """
tokenizer = _Tokenizer(lines) tokenizer = Tokenizer(lines)
while not tokenizer.AtEnd(): while not tokenizer.AtEnd():
self._MergeField(tokenizer, message) self._MergeField(tokenizer, message)
@ -491,13 +611,13 @@ class _Parser(object):
'Extension "%s" not registered.' % name) 'Extension "%s" not registered.' % name)
elif message_descriptor != field.containing_type: elif message_descriptor != field.containing_type:
raise tokenizer.ParseErrorPreviousToken( raise tokenizer.ParseErrorPreviousToken(
'Extension "%s" does not extend message type "%s".' % ( 'Extension "%s" does not extend message type "%s".' %
name, message_descriptor.full_name)) (name, message_descriptor.full_name))
tokenizer.Consume(']') tokenizer.Consume(']')
else: else:
name = tokenizer.ConsumeIdentifier() name = tokenizer.ConsumeIdentifierOrNumber()
if self.allow_field_number and name.isdigit(): if self.allow_field_number and name.isdigit():
number = ParseInteger(name, True, True) number = ParseInteger(name, True, True)
field = message_descriptor.fields_by_number.get(number, None) field = message_descriptor.fields_by_number.get(number, None)
@ -520,8 +640,8 @@ class _Parser(object):
if not field: if not field:
raise tokenizer.ParseErrorPreviousToken( raise tokenizer.ParseErrorPreviousToken(
'Message type "%s" has no field named "%s".' % ( 'Message type "%s" has no field named "%s".' %
message_descriptor.full_name, name)) (message_descriptor.full_name, name))
if field: if field:
if not self._allow_multiple_scalars and field.containing_oneof: if not self._allow_multiple_scalars and field.containing_oneof:
@ -532,8 +652,8 @@ class _Parser(object):
if which_oneof is not None and which_oneof != field.name: if which_oneof is not None and which_oneof != field.name:
raise tokenizer.ParseErrorPreviousToken( raise tokenizer.ParseErrorPreviousToken(
'Field "%s" is specified along with field "%s", another member ' 'Field "%s" is specified along with field "%s", another member '
'of oneof "%s" for message type "%s".' % ( 'of oneof "%s" for message type "%s".' %
field.name, which_oneof, field.containing_oneof.name, (field.name, which_oneof, field.containing_oneof.name,
message_descriptor.full_name)) message_descriptor.full_name))
if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
@ -543,12 +663,13 @@ class _Parser(object):
tokenizer.Consume(':') tokenizer.Consume(':')
merger = self._MergeScalarField merger = self._MergeScalarField
if (field.label == descriptor.FieldDescriptor.LABEL_REPEATED if (field.label == descriptor.FieldDescriptor.LABEL_REPEATED and
and tokenizer.TryConsume('[')): tokenizer.TryConsume('[')):
# Short repeated format, e.g. "foo: [1, 2, 3]" # Short repeated format, e.g. "foo: [1, 2, 3]"
while True: while True:
merger(tokenizer, message, field) merger(tokenizer, message, field)
if tokenizer.TryConsume(']'): break if tokenizer.TryConsume(']'):
break
tokenizer.Consume(',') tokenizer.Consume(',')
else: else:
@ -563,6 +684,21 @@ class _Parser(object):
if not tokenizer.TryConsume(','): if not tokenizer.TryConsume(','):
tokenizer.TryConsume(';') tokenizer.TryConsume(';')
def _ConsumeAnyTypeUrl(self, tokenizer):
"""Consumes a google.protobuf.Any type URL and returns the type name."""
# Consume "type.googleapis.com/".
tokenizer.ConsumeIdentifier()
tokenizer.Consume('.')
tokenizer.ConsumeIdentifier()
tokenizer.Consume('.')
tokenizer.ConsumeIdentifier()
tokenizer.Consume('/')
# Consume the fully-qualified type name.
name = [tokenizer.ConsumeIdentifier()]
while tokenizer.TryConsume('.'):
name.append(tokenizer.ConsumeIdentifier())
return '.'.join(name)
def _MergeMessageField(self, tokenizer, message, field): def _MergeMessageField(self, tokenizer, message, field):
"""Merges a single scalar field into a message. """Merges a single scalar field into a message.
@ -582,7 +718,34 @@ class _Parser(object):
tokenizer.Consume('{') tokenizer.Consume('{')
end_token = '}' end_token = '}'
if (field.message_type.full_name == _ANY_FULL_TYPE_NAME and
tokenizer.TryConsume('[')):
packed_type_name = self._ConsumeAnyTypeUrl(tokenizer)
tokenizer.Consume(']')
tokenizer.TryConsume(':')
if tokenizer.TryConsume('<'):
expanded_any_end_token = '>'
else:
tokenizer.Consume('{')
expanded_any_end_token = '}'
if not self.descriptor_pool:
raise ParseError('Descriptor pool required to parse expanded Any field')
expanded_any_sub_message = _BuildMessageFromTypeName(packed_type_name,
self.descriptor_pool)
if not expanded_any_sub_message:
raise ParseError('Type %s not found in descriptor pool' %
packed_type_name)
while not tokenizer.TryConsume(expanded_any_end_token):
if tokenizer.AtEnd():
raise tokenizer.ParseErrorPreviousToken('Expected "%s".' %
(expanded_any_end_token,))
self._MergeField(tokenizer, expanded_any_sub_message)
if field.label == descriptor.FieldDescriptor.LABEL_REPEATED: if field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
any_message = getattr(message, field.name).add()
else:
any_message = getattr(message, field.name)
any_message.Pack(expanded_any_sub_message)
elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
if field.is_extension: if field.is_extension:
sub_message = message.Extensions[field].add() sub_message = message.Extensions[field].add()
elif is_map_entry: elif is_map_entry:
@ -628,17 +791,17 @@ class _Parser(object):
if field.type in (descriptor.FieldDescriptor.TYPE_INT32, if field.type in (descriptor.FieldDescriptor.TYPE_INT32,
descriptor.FieldDescriptor.TYPE_SINT32, descriptor.FieldDescriptor.TYPE_SINT32,
descriptor.FieldDescriptor.TYPE_SFIXED32): descriptor.FieldDescriptor.TYPE_SFIXED32):
value = tokenizer.ConsumeInt32() value = _ConsumeInt32(tokenizer)
elif field.type in (descriptor.FieldDescriptor.TYPE_INT64, elif field.type in (descriptor.FieldDescriptor.TYPE_INT64,
descriptor.FieldDescriptor.TYPE_SINT64, descriptor.FieldDescriptor.TYPE_SINT64,
descriptor.FieldDescriptor.TYPE_SFIXED64): descriptor.FieldDescriptor.TYPE_SFIXED64):
value = tokenizer.ConsumeInt64() value = _ConsumeInt64(tokenizer)
elif field.type in (descriptor.FieldDescriptor.TYPE_UINT32, elif field.type in (descriptor.FieldDescriptor.TYPE_UINT32,
descriptor.FieldDescriptor.TYPE_FIXED32): descriptor.FieldDescriptor.TYPE_FIXED32):
value = tokenizer.ConsumeUint32() value = _ConsumeUint32(tokenizer)
elif field.type in (descriptor.FieldDescriptor.TYPE_UINT64, elif field.type in (descriptor.FieldDescriptor.TYPE_UINT64,
descriptor.FieldDescriptor.TYPE_FIXED64): descriptor.FieldDescriptor.TYPE_FIXED64):
value = tokenizer.ConsumeUint64() value = _ConsumeUint64(tokenizer)
elif field.type in (descriptor.FieldDescriptor.TYPE_FLOAT, elif field.type in (descriptor.FieldDescriptor.TYPE_FLOAT,
descriptor.FieldDescriptor.TYPE_DOUBLE): descriptor.FieldDescriptor.TYPE_DOUBLE):
value = tokenizer.ConsumeFloat() value = tokenizer.ConsumeFloat()
@ -753,13 +916,12 @@ def _SkipFieldValue(tokenizer):
return return
if (not tokenizer.TryConsumeIdentifier() and if (not tokenizer.TryConsumeIdentifier() and
not tokenizer.TryConsumeInt64() and not _TryConsumeInt64(tokenizer) and not _TryConsumeUint64(tokenizer) and
not tokenizer.TryConsumeUint64() and
not tokenizer.TryConsumeFloat()): not tokenizer.TryConsumeFloat()):
raise ParseError('Invalid field value: ' + tokenizer.token) raise ParseError('Invalid field value: ' + tokenizer.token)
class _Tokenizer(object): class Tokenizer(object):
"""Protocol buffer text representation tokenizer. """Protocol buffer text representation tokenizer.
This class handles the lower level string parsing by splitting it into This class handles the lower level string parsing by splitting it into
@ -768,7 +930,9 @@ class _Tokenizer(object):
It was directly ported from the Java protocol buffer API. It was directly ported from the Java protocol buffer API.
""" """
_WHITESPACE = re.compile('(\\s|(#.*$))+', re.MULTILINE) _WHITESPACE = re.compile(r'\s+')
_COMMENT = re.compile(r'(\s*#.*$)', re.MULTILINE)
_WHITESPACE_OR_COMMENT = re.compile(r'(\s|(#.*$))+', re.MULTILINE)
_TOKEN = re.compile('|'.join([ _TOKEN = re.compile('|'.join([
r'[a-zA-Z_][0-9a-zA-Z_+-]*', # an identifier r'[a-zA-Z_][0-9a-zA-Z_+-]*', # an identifier
r'([0-9+-]|(\.[0-9]))[0-9a-zA-Z_.+-]*', # a number r'([0-9+-]|(\.[0-9]))[0-9a-zA-Z_.+-]*', # a number
@ -776,9 +940,10 @@ class _Tokenizer(object):
r'{qt}([^{qt}\n\\]|\\.)*({qt}|\\?$)'.format(qt=mark) for mark in _QUOTES r'{qt}([^{qt}\n\\]|\\.)*({qt}|\\?$)'.format(qt=mark) for mark in _QUOTES
])) ]))
_IDENTIFIER = re.compile(r'\w+') _IDENTIFIER = re.compile(r'[^\d\W]\w*')
_IDENTIFIER_OR_NUMBER = re.compile(r'\w+')
def __init__(self, lines): def __init__(self, lines, skip_comments=True):
self._position = 0 self._position = 0
self._line = -1 self._line = -1
self._column = 0 self._column = 0
@ -789,6 +954,9 @@ class _Tokenizer(object):
self._previous_line = 0 self._previous_line = 0
self._previous_column = 0 self._previous_column = 0
self._more_lines = True self._more_lines = True
self._skip_comments = skip_comments
self._whitespace_pattern = (skip_comments and self._WHITESPACE_OR_COMMENT
or self._WHITESPACE)
self._SkipWhitespace() self._SkipWhitespace()
self.NextToken() self.NextToken()
@ -818,7 +986,7 @@ class _Tokenizer(object):
def _SkipWhitespace(self): def _SkipWhitespace(self):
while True: while True:
self._PopLine() self._PopLine()
match = self._WHITESPACE.match(self._current_line, self._column) match = self._whitespace_pattern.match(self._current_line, self._column)
if not match: if not match:
break break
length = len(match.group(0)) length = len(match.group(0))
@ -848,7 +1016,14 @@ class _Tokenizer(object):
ParseError: If the text couldn't be consumed. ParseError: If the text couldn't be consumed.
""" """
if not self.TryConsume(token): if not self.TryConsume(token):
raise self._ParseError('Expected "%s".' % token) raise self.ParseError('Expected "%s".' % token)
def ConsumeComment(self):
result = self.token
if not self._COMMENT.match(result):
raise self.ParseError('Expected comment.')
self.NextToken()
return result
def TryConsumeIdentifier(self): def TryConsumeIdentifier(self):
try: try:
@ -868,85 +1043,55 @@ class _Tokenizer(object):
""" """
result = self.token result = self.token
if not self._IDENTIFIER.match(result): if not self._IDENTIFIER.match(result):
raise self._ParseError('Expected identifier.') raise self.ParseError('Expected identifier.')
self.NextToken() self.NextToken()
return result return result
def ConsumeInt32(self): def TryConsumeIdentifierOrNumber(self):
"""Consumes a signed 32bit integer number.
Returns:
The integer parsed.
Raises:
ParseError: If a signed 32bit integer couldn't be consumed.
"""
try: try:
result = ParseInteger(self.token, is_signed=True, is_long=False) self.ConsumeIdentifierOrNumber()
except ValueError as e:
raise self._ParseError(str(e))
self.NextToken()
return result
def ConsumeUint32(self):
"""Consumes an unsigned 32bit integer number.
Returns:
The integer parsed.
Raises:
ParseError: If an unsigned 32bit integer couldn't be consumed.
"""
try:
result = ParseInteger(self.token, is_signed=False, is_long=False)
except ValueError as e:
raise self._ParseError(str(e))
self.NextToken()
return result
def TryConsumeInt64(self):
try:
self.ConsumeInt64()
return True return True
except ParseError: except ParseError:
return False return False
def ConsumeInt64(self): def ConsumeIdentifierOrNumber(self):
"""Consumes a signed 64bit integer number. """Consumes protocol message field identifier.
Returns: Returns:
The integer parsed. Identifier string.
Raises: Raises:
ParseError: If a signed 64bit integer couldn't be consumed. ParseError: If an identifier couldn't be consumed.
""" """
try: result = self.token
result = ParseInteger(self.token, is_signed=True, is_long=True) if not self._IDENTIFIER_OR_NUMBER.match(result):
except ValueError as e: raise self.ParseError('Expected identifier or number.')
raise self._ParseError(str(e))
self.NextToken() self.NextToken()
return result return result
def TryConsumeUint64(self): def TryConsumeInteger(self):
try: try:
self.ConsumeUint64() # Note: is_long only affects value type, not whether an error is raised.
self.ConsumeInteger()
return True return True
except ParseError: except ParseError:
return False return False
def ConsumeUint64(self): def ConsumeInteger(self, is_long=False):
"""Consumes an unsigned 64bit integer number. """Consumes an integer number.
Args:
is_long: True if the value should be returned as a long integer.
Returns: Returns:
The integer parsed. The integer parsed.
Raises: Raises:
ParseError: If an unsigned 64bit integer couldn't be consumed. ParseError: If an integer couldn't be consumed.
""" """
try: try:
result = ParseInteger(self.token, is_signed=False, is_long=True) result = _ParseAbstractInteger(self.token, is_long=is_long)
except ValueError as e: except ValueError as e:
raise self._ParseError(str(e)) raise self.ParseError(str(e))
self.NextToken() self.NextToken()
return result return result
@ -969,7 +1114,7 @@ class _Tokenizer(object):
try: try:
result = ParseFloat(self.token) result = ParseFloat(self.token)
except ValueError as e: except ValueError as e:
raise self._ParseError(str(e)) raise self.ParseError(str(e))
self.NextToken() self.NextToken()
return result return result
@ -985,7 +1130,7 @@ class _Tokenizer(object):
try: try:
result = ParseBool(self.token) result = ParseBool(self.token)
except ValueError as e: except ValueError as e:
raise self._ParseError(str(e)) raise self.ParseError(str(e))
self.NextToken() self.NextToken()
return result return result
@ -1039,15 +1184,15 @@ class _Tokenizer(object):
""" """
text = self.token text = self.token
if len(text) < 1 or text[0] not in _QUOTES: if len(text) < 1 or text[0] not in _QUOTES:
raise self._ParseError('Expected string but found: %r' % (text,)) raise self.ParseError('Expected string but found: %r' % (text,))
if len(text) < 2 or text[-1] != text[0]: if len(text) < 2 or text[-1] != text[0]:
raise self._ParseError('String missing ending quote: %r' % (text,)) raise self.ParseError('String missing ending quote: %r' % (text,))
try: try:
result = text_encoding.CUnescape(text[1:-1]) result = text_encoding.CUnescape(text[1:-1])
except ValueError as e: except ValueError as e:
raise self._ParseError(str(e)) raise self.ParseError(str(e))
self.NextToken() self.NextToken()
return result return result
@ -1055,7 +1200,7 @@ class _Tokenizer(object):
try: try:
result = ParseEnum(field, self.token) result = ParseEnum(field, self.token)
except ValueError as e: except ValueError as e:
raise self._ParseError(str(e)) raise self.ParseError(str(e))
self.NextToken() self.NextToken()
return result return result
@ -1068,16 +1213,15 @@ class _Tokenizer(object):
Returns: Returns:
A ParseError instance. A ParseError instance.
""" """
return ParseError('%d:%d : %s' % ( return ParseError(message, self._previous_line + 1,
self._previous_line + 1, self._previous_column + 1, message)) self._previous_column + 1)
def _ParseError(self, message): def ParseError(self, message):
"""Creates and *returns* a ParseError for the current token.""" """Creates and *returns* a ParseError for the current token."""
return ParseError('%d:%d : %s' % ( return ParseError(message, self._line + 1, self._column + 1)
self._line + 1, self._column + 1, message))
def _StringParseError(self, e): def _StringParseError(self, e):
return self._ParseError('Couldn\'t parse string: ' + str(e)) return self.ParseError('Couldn\'t parse string: ' + str(e))
def NextToken(self): def NextToken(self):
"""Reads the next meaningful token.""" """Reads the next meaningful token."""
@ -1092,12 +1236,124 @@ class _Tokenizer(object):
return return
match = self._TOKEN.match(self._current_line, self._column) match = self._TOKEN.match(self._current_line, self._column)
if not match and not self._skip_comments:
match = self._COMMENT.match(self._current_line, self._column)
if match: if match:
token = match.group(0) token = match.group(0)
self.token = token self.token = token
else: else:
self.token = self._current_line[self._column] self.token = self._current_line[self._column]
# Aliased so it can still be accessed by current visibility violators.
# TODO(dbarnett): Migrate violators to textformat_tokenizer.
_Tokenizer = Tokenizer # pylint: disable=invalid-name
def _ConsumeInt32(tokenizer):
"""Consumes a signed 32bit integer number from tokenizer.
Args:
tokenizer: A tokenizer used to parse the number.
Returns:
The integer parsed.
Raises:
ParseError: If a signed 32bit integer couldn't be consumed.
"""
return _ConsumeInteger(tokenizer, is_signed=True, is_long=False)
def _ConsumeUint32(tokenizer):
"""Consumes an unsigned 32bit integer number from tokenizer.
Args:
tokenizer: A tokenizer used to parse the number.
Returns:
The integer parsed.
Raises:
ParseError: If an unsigned 32bit integer couldn't be consumed.
"""
return _ConsumeInteger(tokenizer, is_signed=False, is_long=False)
def _TryConsumeInt64(tokenizer):
try:
_ConsumeInt64(tokenizer)
return True
except ParseError:
return False
def _ConsumeInt64(tokenizer):
"""Consumes a signed 32bit integer number from tokenizer.
Args:
tokenizer: A tokenizer used to parse the number.
Returns:
The integer parsed.
Raises:
ParseError: If a signed 32bit integer couldn't be consumed.
"""
return _ConsumeInteger(tokenizer, is_signed=True, is_long=True)
def _TryConsumeUint64(tokenizer):
try:
_ConsumeUint64(tokenizer)
return True
except ParseError:
return False
def _ConsumeUint64(tokenizer):
"""Consumes an unsigned 64bit integer number from tokenizer.
Args:
tokenizer: A tokenizer used to parse the number.
Returns:
The integer parsed.
Raises:
ParseError: If an unsigned 64bit integer couldn't be consumed.
"""
return _ConsumeInteger(tokenizer, is_signed=False, is_long=True)
def _TryConsumeInteger(tokenizer, is_signed=False, is_long=False):
try:
_ConsumeInteger(tokenizer, is_signed=is_signed, is_long=is_long)
return True
except ParseError:
return False
def _ConsumeInteger(tokenizer, is_signed=False, is_long=False):
"""Consumes an integer number from tokenizer.
Args:
tokenizer: A tokenizer used to parse the number.
is_signed: True if a signed integer must be parsed.
is_long: True if a long integer must be parsed.
Returns:
The integer parsed.
Raises:
ParseError: If an integer with given characteristics couldn't be consumed.
"""
try:
result = ParseInteger(tokenizer.token, is_signed=is_signed, is_long=is_long)
except ValueError as e:
raise tokenizer.ParseError(str(e))
tokenizer.NextToken()
return result
def ParseInteger(text, is_signed=False, is_long=False): def ParseInteger(text, is_signed=False, is_long=False):
"""Parses an integer. """Parses an integer.
@ -1110,6 +1366,28 @@ def ParseInteger(text, is_signed=False, is_long=False):
Returns: Returns:
The integer value. The integer value.
Raises:
ValueError: Thrown Iff the text is not a valid integer.
"""
# Do the actual parsing. Exception handling is propagated to caller.
result = _ParseAbstractInteger(text, is_long=is_long)
# Check if the integer is sane. Exceptions handled by callers.
checker = _INTEGER_CHECKERS[2 * int(is_long) + int(is_signed)]
checker.CheckValue(result)
return result
def _ParseAbstractInteger(text, is_long=False):
"""Parses an integer without checking size/signedness.
Args:
text: The text to parse.
is_long: True if the value should be returned as a long integer.
Returns:
The integer value.
Raises: Raises:
ValueError: Thrown Iff the text is not a valid integer. ValueError: Thrown Iff the text is not a valid integer.
""" """
@ -1119,17 +1397,12 @@ def ParseInteger(text, is_signed=False, is_long=False):
# alternate implementations where the distinction is more significant # alternate implementations where the distinction is more significant
# (e.g. the C++ implementation) simpler. # (e.g. the C++ implementation) simpler.
if is_long: if is_long:
result = long(text, 0) return long(text, 0)
else: else:
result = int(text, 0) return int(text, 0)
except ValueError: except ValueError:
raise ValueError('Couldn\'t parse integer: %s' % text) raise ValueError('Couldn\'t parse integer: %s' % text)
# Check if the integer is sane. Exceptions handled by callers.
checker = _INTEGER_CHECKERS[2 * int(is_long) + int(is_signed)]
checker.CheckValue(result)
return result
def ParseFloat(text): def ParseFloat(text):
"""Parse a floating point number. """Parse a floating point number.
@ -1206,14 +1479,12 @@ def ParseEnum(field, value):
# Identifier. # Identifier.
enum_value = enum_descriptor.values_by_name.get(value, None) enum_value = enum_descriptor.values_by_name.get(value, None)
if enum_value is None: if enum_value is None:
raise ValueError( raise ValueError('Enum type "%s" has no value named %s.' %
'Enum type "%s" has no value named %s.' % ( (enum_descriptor.full_name, value))
enum_descriptor.full_name, value))
else: else:
# Numeric value. # Numeric value.
enum_value = enum_descriptor.values_by_number.get(number, None) enum_value = enum_descriptor.values_by_number.get(number, None)
if enum_value is None: if enum_value is None:
raise ValueError( raise ValueError('Enum type "%s" has no value with number %d.' %
'Enum type "%s" has no value with number %d.' % ( (enum_descriptor.full_name, number))
enum_descriptor.full_name, number))
return enum_value.number return enum_value.number

@ -76,6 +76,7 @@ def generate_proto(source, require = True):
sys.exit(-1) sys.exit(-1)
def GenerateUnittestProtos(): def GenerateUnittestProtos():
generate_proto("../src/google/protobuf/any_test.proto", False)
generate_proto("../src/google/protobuf/map_unittest.proto", False) generate_proto("../src/google/protobuf/map_unittest.proto", False)
generate_proto("../src/google/protobuf/unittest_arena.proto", False) generate_proto("../src/google/protobuf/unittest_arena.proto", False)
generate_proto("../src/google/protobuf/unittest_no_arena.proto", False) generate_proto("../src/google/protobuf/unittest_no_arena.proto", False)
@ -94,6 +95,7 @@ def GenerateUnittestProtos():
generate_proto("google/protobuf/internal/descriptor_pool_test2.proto", False) generate_proto("google/protobuf/internal/descriptor_pool_test2.proto", False)
generate_proto("google/protobuf/internal/factory_test1.proto", False) generate_proto("google/protobuf/internal/factory_test1.proto", False)
generate_proto("google/protobuf/internal/factory_test2.proto", False) generate_proto("google/protobuf/internal/factory_test2.proto", False)
generate_proto("google/protobuf/internal/file_options_test.proto", False)
generate_proto("google/protobuf/internal/import_test_package/inner.proto", False) generate_proto("google/protobuf/internal/import_test_package/inner.proto", False)
generate_proto("google/protobuf/internal/import_test_package/outer.proto", False) generate_proto("google/protobuf/internal/import_test_package/outer.proto", False)
generate_proto("google/protobuf/internal/missing_enum_values.proto", False) generate_proto("google/protobuf/internal/missing_enum_values.proto", False)

@ -283,8 +283,8 @@ void Any::SerializeWithCachedSizes(
// @@protoc_insertion_point(serialize_end:google.protobuf.Any) // @@protoc_insertion_point(serialize_end:google.protobuf.Any)
} }
::google::protobuf::uint8* Any::SerializeWithCachedSizesToArray( ::google::protobuf::uint8* Any::InternalSerializeWithCachedSizesToArray(
::google::protobuf::uint8* target) const { bool deterministic, ::google::protobuf::uint8* target) const {
// @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Any) // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Any)
// optional string type_url = 1; // optional string type_url = 1;
if (this->type_url().size() > 0) { if (this->type_url().size() > 0) {

@ -42,7 +42,7 @@ class Any;
// =================================================================== // ===================================================================
class LIBPROTOBUF_EXPORT Any : public ::google::protobuf::Message { class LIBPROTOBUF_EXPORT Any : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:google.protobuf.Any) */ {
public: public:
Any(); Any();
virtual ~Any(); virtual ~Any();
@ -86,7 +86,11 @@ class LIBPROTOBUF_EXPORT Any : public ::google::protobuf::Message {
::google::protobuf::io::CodedInputStream* input); ::google::protobuf::io::CodedInputStream* input);
void SerializeWithCachedSizes( void SerializeWithCachedSizes(
::google::protobuf::io::CodedOutputStream* output) const; ::google::protobuf::io::CodedOutputStream* output) const;
::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray(
bool deterministic, ::google::protobuf::uint8* output) const;
::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const {
return InternalSerializeWithCachedSizesToArray(false, output);
}
int GetCachedSize() const { return _cached_size_; } int GetCachedSize() const { return _cached_size_; }
private: private:
void SharedCtor(); void SharedCtor();

@ -65,6 +65,16 @@ option objc_class_prefix = "GPB";
// foo = any.unpack(Foo.class); // foo = any.unpack(Foo.class);
// } // }
// //
// Example 3: Pack and unpack a message in Python.
//
// foo = Foo(...)
// any = Any()
// any.Pack(foo)
// ...
// if any.Is(Foo.DESCRIPTOR):
// any.Unpack(foo)
// ...
//
// The pack methods provided by protobuf library will by default use // The pack methods provided by protobuf library will by default use
// 'type.googleapis.com/full.type.name' as the type URL and the unpack // 'type.googleapis.com/full.type.name' as the type URL and the unpack
// methods only use the fully qualified type name after the last '/' // methods only use the fully qualified type name after the last '/'
@ -104,10 +114,10 @@ message Any {
// A URL/resource name whose content describes the type of the // A URL/resource name whose content describes the type of the
// serialized protocol buffer message. // serialized protocol buffer message.
// //
// For URLs which use the schema `http`, `https`, or no schema, the // For URLs which use the scheme `http`, `https`, or no scheme, the
// following restrictions and interpretations apply: // following restrictions and interpretations apply:
// //
// * If no schema is provided, `https` is assumed. // * If no scheme is provided, `https` is assumed.
// * The last segment of the URL's path must represent the fully // * The last segment of the URL's path must represent the fully
// qualified name of the type (as in `path/google.protobuf.Duration`). // qualified name of the type (as in `path/google.protobuf.Duration`).
// The name should be in a canonical form (e.g., leading "." is // The name should be in a canonical form (e.g., leading "." is
@ -120,7 +130,7 @@ message Any {
// on changes to types. (Use versioned type names to manage // on changes to types. (Use versioned type names to manage
// breaking changes.) // breaking changes.)
// //
// Schemas other than `http`, `https` (or the empty schema) might be // Schemes other than `http`, `https` (or the empty scheme) might be
// used with implementation specific semantics. // used with implementation specific semantics.
// //
string type_url = 1; string type_url = 1;

@ -475,8 +475,8 @@ void Api::SerializeWithCachedSizes(
// @@protoc_insertion_point(serialize_end:google.protobuf.Api) // @@protoc_insertion_point(serialize_end:google.protobuf.Api)
} }
::google::protobuf::uint8* Api::SerializeWithCachedSizesToArray( ::google::protobuf::uint8* Api::InternalSerializeWithCachedSizesToArray(
::google::protobuf::uint8* target) const { bool deterministic, ::google::protobuf::uint8* target) const {
// @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Api) // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Api)
// optional string name = 1; // optional string name = 1;
if (this->name().size() > 0) { if (this->name().size() > 0) {
@ -492,15 +492,15 @@ void Api::SerializeWithCachedSizes(
// repeated .google.protobuf.Method methods = 2; // repeated .google.protobuf.Method methods = 2;
for (unsigned int i = 0, n = this->methods_size(); i < n; i++) { for (unsigned int i = 0, n = this->methods_size(); i < n; i++) {
target = ::google::protobuf::internal::WireFormatLite:: target = ::google::protobuf::internal::WireFormatLite::
WriteMessageNoVirtualToArray( InternalWriteMessageNoVirtualToArray(
2, this->methods(i), target); 2, this->methods(i), false, target);
} }
// repeated .google.protobuf.Option options = 3; // repeated .google.protobuf.Option options = 3;
for (unsigned int i = 0, n = this->options_size(); i < n; i++) { for (unsigned int i = 0, n = this->options_size(); i < n; i++) {
target = ::google::protobuf::internal::WireFormatLite:: target = ::google::protobuf::internal::WireFormatLite::
WriteMessageNoVirtualToArray( InternalWriteMessageNoVirtualToArray(
3, this->options(i), target); 3, this->options(i), false, target);
} }
// optional string version = 4; // optional string version = 4;
@ -517,15 +517,15 @@ void Api::SerializeWithCachedSizes(
// optional .google.protobuf.SourceContext source_context = 5; // optional .google.protobuf.SourceContext source_context = 5;
if (this->has_source_context()) { if (this->has_source_context()) {
target = ::google::protobuf::internal::WireFormatLite:: target = ::google::protobuf::internal::WireFormatLite::
WriteMessageNoVirtualToArray( InternalWriteMessageNoVirtualToArray(
5, *this->source_context_, target); 5, *this->source_context_, false, target);
} }
// repeated .google.protobuf.Mixin mixins = 6; // repeated .google.protobuf.Mixin mixins = 6;
for (unsigned int i = 0, n = this->mixins_size(); i < n; i++) { for (unsigned int i = 0, n = this->mixins_size(); i < n; i++) {
target = ::google::protobuf::internal::WireFormatLite:: target = ::google::protobuf::internal::WireFormatLite::
WriteMessageNoVirtualToArray( InternalWriteMessageNoVirtualToArray(
6, this->mixins(i), target); 6, this->mixins(i), false, target);
} }
// optional .google.protobuf.Syntax syntax = 7; // optional .google.protobuf.Syntax syntax = 7;
@ -1225,8 +1225,8 @@ void Method::SerializeWithCachedSizes(
// @@protoc_insertion_point(serialize_end:google.protobuf.Method) // @@protoc_insertion_point(serialize_end:google.protobuf.Method)
} }
::google::protobuf::uint8* Method::SerializeWithCachedSizesToArray( ::google::protobuf::uint8* Method::InternalSerializeWithCachedSizesToArray(
::google::protobuf::uint8* target) const { bool deterministic, ::google::protobuf::uint8* target) const {
// @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Method) // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Method)
// optional string name = 1; // optional string name = 1;
if (this->name().size() > 0) { if (this->name().size() > 0) {
@ -1274,8 +1274,8 @@ void Method::SerializeWithCachedSizes(
// repeated .google.protobuf.Option options = 6; // repeated .google.protobuf.Option options = 6;
for (unsigned int i = 0, n = this->options_size(); i < n; i++) { for (unsigned int i = 0, n = this->options_size(); i < n; i++) {
target = ::google::protobuf::internal::WireFormatLite:: target = ::google::protobuf::internal::WireFormatLite::
WriteMessageNoVirtualToArray( InternalWriteMessageNoVirtualToArray(
6, this->options(i), target); 6, this->options(i), false, target);
} }
// optional .google.protobuf.Syntax syntax = 7; // optional .google.protobuf.Syntax syntax = 7;
@ -1803,8 +1803,8 @@ void Mixin::SerializeWithCachedSizes(
// @@protoc_insertion_point(serialize_end:google.protobuf.Mixin) // @@protoc_insertion_point(serialize_end:google.protobuf.Mixin)
} }
::google::protobuf::uint8* Mixin::SerializeWithCachedSizesToArray( ::google::protobuf::uint8* Mixin::InternalSerializeWithCachedSizesToArray(
::google::protobuf::uint8* target) const { bool deterministic, ::google::protobuf::uint8* target) const {
// @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Mixin) // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Mixin)
// optional string name = 1; // optional string name = 1;
if (this->name().size() > 0) { if (this->name().size() > 0) {

@ -45,7 +45,7 @@ class Mixin;
// =================================================================== // ===================================================================
class LIBPROTOBUF_EXPORT Api : public ::google::protobuf::Message { class LIBPROTOBUF_EXPORT Api : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:google.protobuf.Api) */ {
public: public:
Api(); Api();
virtual ~Api(); virtual ~Api();
@ -79,7 +79,11 @@ class LIBPROTOBUF_EXPORT Api : public ::google::protobuf::Message {
::google::protobuf::io::CodedInputStream* input); ::google::protobuf::io::CodedInputStream* input);
void SerializeWithCachedSizes( void SerializeWithCachedSizes(
::google::protobuf::io::CodedOutputStream* output) const; ::google::protobuf::io::CodedOutputStream* output) const;
::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray(
bool deterministic, ::google::protobuf::uint8* output) const;
::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const {
return InternalSerializeWithCachedSizesToArray(false, output);
}
int GetCachedSize() const { return _cached_size_; } int GetCachedSize() const { return _cached_size_; }
private: private:
void SharedCtor(); void SharedCtor();
@ -196,7 +200,7 @@ class LIBPROTOBUF_EXPORT Api : public ::google::protobuf::Message {
}; };
// ------------------------------------------------------------------- // -------------------------------------------------------------------
class LIBPROTOBUF_EXPORT Method : public ::google::protobuf::Message { class LIBPROTOBUF_EXPORT Method : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:google.protobuf.Method) */ {
public: public:
Method(); Method();
virtual ~Method(); virtual ~Method();
@ -230,7 +234,11 @@ class LIBPROTOBUF_EXPORT Method : public ::google::protobuf::Message {
::google::protobuf::io::CodedInputStream* input); ::google::protobuf::io::CodedInputStream* input);
void SerializeWithCachedSizes( void SerializeWithCachedSizes(
::google::protobuf::io::CodedOutputStream* output) const; ::google::protobuf::io::CodedOutputStream* output) const;
::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray(
bool deterministic, ::google::protobuf::uint8* output) const;
::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const {
return InternalSerializeWithCachedSizesToArray(false, output);
}
int GetCachedSize() const { return _cached_size_; } int GetCachedSize() const { return _cached_size_; }
private: private:
void SharedCtor(); void SharedCtor();
@ -337,7 +345,7 @@ class LIBPROTOBUF_EXPORT Method : public ::google::protobuf::Message {
}; };
// ------------------------------------------------------------------- // -------------------------------------------------------------------
class LIBPROTOBUF_EXPORT Mixin : public ::google::protobuf::Message { class LIBPROTOBUF_EXPORT Mixin : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:google.protobuf.Mixin) */ {
public: public:
Mixin(); Mixin();
virtual ~Mixin(); virtual ~Mixin();
@ -371,7 +379,11 @@ class LIBPROTOBUF_EXPORT Mixin : public ::google::protobuf::Message {
::google::protobuf::io::CodedInputStream* input); ::google::protobuf::io::CodedInputStream* input);
void SerializeWithCachedSizes( void SerializeWithCachedSizes(
::google::protobuf::io::CodedOutputStream* output) const; ::google::protobuf::io::CodedOutputStream* output) const;
::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray(
bool deterministic, ::google::protobuf::uint8* output) const;
::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const {
return InternalSerializeWithCachedSizesToArray(false, output);
}
int GetCachedSize() const { return _cached_size_; } int GetCachedSize() const { return _cached_size_; }
private: private:
void SharedCtor(); void SharedCtor();

@ -133,12 +133,7 @@ Arena::Block* Arena::NewBlock(void* me, Block* my_last_block, size_t n,
Block* b = reinterpret_cast<Block*>(options_.block_alloc(size)); Block* b = reinterpret_cast<Block*>(options_.block_alloc(size));
b->pos = kHeaderSize + n; b->pos = kHeaderSize + n;
b->size = size; b->size = size;
if (b->avail() == 0) {
// Do not attempt to reuse this block.
b->owner = NULL;
} else {
b->owner = me; b->owner = me;
}
#ifdef ADDRESS_SANITIZER #ifdef ADDRESS_SANITIZER
// Poison the rest of the block for ASAN. It was unpoisoned by the underlying // Poison the rest of the block for ASAN. It was unpoisoned by the underlying
// malloc but it's not yet usable until we return it as part of an allocation. // malloc but it's not yet usable until we return it as part of an allocation.
@ -223,9 +218,7 @@ void* Arena::SlowAlloc(size_t n) {
} }
b = NewBlock(me, b, n, options_.start_block_size, options_.max_block_size); b = NewBlock(me, b, n, options_.start_block_size, options_.max_block_size);
AddBlock(b); AddBlock(b);
if (b->owner == me) { // If this block can be reused (see NewBlock()).
SetThreadCacheBlock(b); SetThreadCacheBlock(b);
}
return reinterpret_cast<char*>(b) + kHeaderSize; return reinterpret_cast<char*>(b) + kHeaderSize;
} }

@ -801,7 +801,7 @@ class LIBPROTOBUF_EXPORT Arena {
template <typename T> template <typename T>
static void CreateInArenaStorageInternal( static void CreateInArenaStorageInternal(
T* ptr, Arena* arena, google::protobuf::internal::false_type) { T* ptr, Arena* arena, google::protobuf::internal::false_type) {
new (ptr) T; new (ptr) T();
} }
template <typename T> template <typename T>

@ -42,7 +42,6 @@
#include <google/protobuf/stubs/logging.h> #include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h> #include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/scoped_ptr.h>
#include <google/protobuf/arena_test_util.h> #include <google/protobuf/arena_test_util.h>
#include <google/protobuf/test_util.h> #include <google/protobuf/test_util.h>
#include <google/protobuf/unittest.pb.h> #include <google/protobuf/unittest.pb.h>

@ -641,11 +641,15 @@ CommandLineInterface::MemoryOutputStream::~MemoryOutputStream() {
return; return;
} }
if ((pos > 3) && (target->substr(pos - 3, 2) == "/*")) {
// Support for inline "/* @@protoc_insertion_point() */"
pos = pos - 3;
} else {
// Seek backwards to the beginning of the line, which is where we will // Seek backwards to the beginning of the line, which is where we will
// insert the data. Note that this has the effect of pushing the insertion // insert the data. Note that this has the effect of pushing the
// point down, so the data is inserted before it. This is intentional // insertion point down, so the data is inserted before it. This is
// because it means that multiple insertions at the same point will end // intentional because it means that multiple insertions at the same point
// up in the expected order in the final output. // will end up in the expected order in the final output.
pos = target->find_last_of('\n', pos); pos = target->find_last_of('\n', pos);
if (pos == string::npos) { if (pos == string::npos) {
// Insertion point is on the first line. // Insertion point is on the first line.
@ -654,6 +658,7 @@ CommandLineInterface::MemoryOutputStream::~MemoryOutputStream() {
// Advance to character after '\n'. // Advance to character after '\n'.
++pos; ++pos;
} }
}
// Extract indent. // Extract indent.
string indent_(*target, pos, target->find_first_not_of(" \t", pos) - pos); string indent_(*target, pos, target->find_first_not_of(" \t", pos) - pos);

@ -35,8 +35,8 @@
#include <google/protobuf/compiler/cpp/cpp_enum_field.h> #include <google/protobuf/compiler/cpp/cpp_enum_field.h>
#include <google/protobuf/compiler/cpp/cpp_helpers.h> #include <google/protobuf/compiler/cpp/cpp_helpers.h>
#include <google/protobuf/io/printer.h> #include <google/protobuf/io/printer.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/wire_format.h> #include <google/protobuf/wire_format.h>
#include <google/protobuf/stubs/strutil.h>
namespace google { namespace google {
namespace protobuf { namespace protobuf {

@ -182,33 +182,33 @@ GenerateConstructorCode(io::Printer* printer) const {
void MapFieldGenerator:: void MapFieldGenerator::
GenerateMergeFromCodedStream(io::Printer* printer) const { GenerateMergeFromCodedStream(io::Printer* printer) const {
const FieldDescriptor* key_field =
descriptor_->message_type()->FindFieldByName("key");
const FieldDescriptor* value_field = const FieldDescriptor* value_field =
descriptor_->message_type()->FindFieldByName("value"); descriptor_->message_type()->FindFieldByName("value");
printer->Print(variables_, bool using_entry = false;
"::google::protobuf::scoped_ptr<$map_classname$> entry($name$_.NewEntry());\n"); string key;
string value;
if (IsProto3Field(descriptor_) || if (IsProto3Field(descriptor_) ||
value_field->type() != FieldDescriptor::TYPE_ENUM) { value_field->type() != FieldDescriptor::TYPE_ENUM) {
printer->Print(variables_, printer->Print(variables_,
"$map_classname$::Parser< ::google::protobuf::internal::MapField$lite$<\n"
" $key_cpp$, $val_cpp$,\n"
" $key_wire_type$,\n"
" $val_wire_type$,\n"
" $default_enum_value$ >,\n"
" ::google::protobuf::Map< $key_cpp$, $val_cpp$ > >"
" parser(&$name$_);\n"
"DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(\n" "DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(\n"
" input, entry.get()));\n"); " input, &parser));\n");
switch (value_field->cpp_type()) { key = "parser.key()";
case FieldDescriptor::CPPTYPE_MESSAGE: value = "parser.value()";
printer->Print(variables_,
"(*mutable_$name$())[entry->key()].Swap("
"entry->mutable_value());\n");
break;
case FieldDescriptor::CPPTYPE_ENUM:
printer->Print(variables_,
"(*mutable_$name$())[entry->key()] =\n"
" static_cast< $val_cpp$ >(*entry->mutable_value());\n");
break;
default:
printer->Print(variables_,
"(*mutable_$name$())[entry->key()] = *entry->mutable_value();\n");
break;
}
} else { } else {
using_entry = true;
key = "entry->key()";
value = "entry->value()";
printer->Print(variables_,
"::google::protobuf::scoped_ptr<$map_classname$> entry($name$_.NewEntry());\n");
printer->Print(variables_, printer->Print(variables_,
"{\n" "{\n"
" ::std::string data;\n" " ::std::string data;\n"
@ -229,28 +229,23 @@ GenerateMergeFromCodedStream(io::Printer* printer) const {
" unknown_fields_stream.WriteString(data);\n"); " unknown_fields_stream.WriteString(data);\n");
} }
printer->Print(variables_, printer->Print(variables_,
" }\n" " }\n"
"}\n"); "}\n");
} }
const FieldDescriptor* key_field =
descriptor_->message_type()->FindFieldByName("key");
if (key_field->type() == FieldDescriptor::TYPE_STRING) { if (key_field->type() == FieldDescriptor::TYPE_STRING) {
GenerateUtf8CheckCodeForString( GenerateUtf8CheckCodeForString(
key_field, options_, true, variables_, key_field, options_, true, variables_,
"entry->key().data(), entry->key().length(),\n", printer); StrCat(key, ".data(), ", key, ".length(),\n").data(), printer);
} }
if (value_field->type() == FieldDescriptor::TYPE_STRING) { if (value_field->type() == FieldDescriptor::TYPE_STRING) {
GenerateUtf8CheckCodeForString(value_field, options_, true, variables_, GenerateUtf8CheckCodeForString(value_field, options_, true, variables_,
"entry->mutable_value()->data(),\n" StrCat(value, ".data(), ", value, ".length(),\n").data(), printer);
"entry->mutable_value()->length(),\n",
printer);
} }
// If entry is allocated by arena, its desctructor should be avoided. // If entry is allocated by arena, its desctructor should be avoided.
if (SupportsArenas(descriptor_)) { if (using_entry && SupportsArenas(descriptor_)) {
printer->Print(variables_, printer->Print(variables_,
"if (entry->GetArena() != NULL) entry.release();\n"); "if (entry->GetArena() != NULL) entry.release();\n");
} }
@ -333,8 +328,8 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const {
printer->Print(variables_, printer->Print(variables_,
" entry.reset($name$_.New$wrapper$(it->first, it->second));\n" " entry.reset($name$_.New$wrapper$(it->first, it->second));\n"
" target = ::google::protobuf::internal::WireFormatLite::\n" " target = ::google::protobuf::internal::WireFormatLite::\n"
" Write$declared_type$NoVirtualToArray(\n" " InternalWrite$declared_type$NoVirtualToArray(\n"
" $number$, *entry, target);\n"); " $number$, *entry, false, target);\n");
printer->Indent(); printer->Indent();
printer->Indent(); printer->Indent();

@ -838,11 +838,13 @@ GenerateDependentBaseClassDefinition(io::Printer* printer) {
map<string, string> vars; map<string, string> vars;
vars["classname"] = DependentBaseClassTemplateName(descriptor_); vars["classname"] = DependentBaseClassTemplateName(descriptor_);
vars["full_name"] = descriptor_->full_name();
vars["superclass"] = SuperClassName(descriptor_, options_); vars["superclass"] = SuperClassName(descriptor_, options_);
printer->Print(vars, printer->Print(vars,
"template <class T>\n" "template <class T>\n"
"class $classname$ : public $superclass$ {\n" "class $classname$ : public $superclass$ "
"/* @@protoc_insertion_point(dep_base_class_definition:$full_name$) */ {\n"
" public:\n"); " public:\n");
printer->Indent(); printer->Indent();
@ -878,6 +880,7 @@ GenerateClassDefinition(io::Printer* printer) {
map<string, string> vars; map<string, string> vars;
vars["classname"] = classname_; vars["classname"] = classname_;
vars["full_name"] = descriptor_->full_name();
vars["field_count"] = SimpleItoa(descriptor_->field_count()); vars["field_count"] = SimpleItoa(descriptor_->field_count());
vars["oneof_decl_count"] = SimpleItoa(descriptor_->oneof_decl_count()); vars["oneof_decl_count"] = SimpleItoa(descriptor_->oneof_decl_count());
if (options_.dllexport_decl.empty()) { if (options_.dllexport_decl.empty()) {
@ -892,7 +895,9 @@ GenerateClassDefinition(io::Printer* printer) {
vars["superclass"] = SuperClassName(descriptor_, options_); vars["superclass"] = SuperClassName(descriptor_, options_);
} }
printer->Print(vars, printer->Print(vars,
"class $dllexport$$classname$ : public $superclass$ {\n"); "class $dllexport$$classname$ : public $superclass$ "
"/* @@protoc_insertion_point(class_definition:$full_name$) */ "
"{\n");
printer->Annotate("classname", descriptor_); printer->Annotate("classname", descriptor_);
if (use_dependent_base_) { if (use_dependent_base_) {
printer->Print(vars, " friend class $superclass$;\n"); printer->Print(vars, " friend class $superclass$;\n");
@ -1076,7 +1081,11 @@ GenerateClassDefinition(io::Printer* printer) {
} }
if (HasFastArraySerialization(descriptor_->file(), options_)) { if (HasFastArraySerialization(descriptor_->file(), options_)) {
printer->Print( printer->Print(
"::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;\n"); "::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray(\n"
" bool deterministic, ::google::protobuf::uint8* output) const;\n"
"::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const {\n"
" return InternalSerializeWithCachedSizesToArray(false, output);\n"
"}\n");
} }
} }
@ -1095,6 +1104,13 @@ GenerateClassDefinition(io::Printer* printer) {
descriptors.push_back(descriptor_->oneof_decl(i)->field(j)); descriptors.push_back(descriptor_->oneof_decl(i)->field(j));
} }
} }
for (int i = 0; i < descriptor_->nested_type_count(); i++) {
const Descriptor* nested_type = descriptor_->nested_type(i);
if (IsMapEntryMessage(nested_type)) {
descriptors.push_back(nested_type->FindFieldByName("key"));
descriptors.push_back(nested_type->FindFieldByName("value"));
}
}
uses_string_ = false; uses_string_ = false;
if (PreserveUnknownFields(descriptor_) && if (PreserveUnknownFields(descriptor_) &&
!UseUnknownFieldSet(descriptor_->file(), options_)) { !UseUnknownFieldSet(descriptor_->file(), options_)) {
@ -3267,8 +3283,8 @@ void MessageGenerator::GenerateSerializeOneExtensionRange(
"// Extension range [$start$, $end$)\n"); "// Extension range [$start$, $end$)\n");
if (to_array) { if (to_array) {
printer->Print(vars, printer->Print(vars,
"target = _extensions_.SerializeWithCachedSizesToArray(\n" "target = _extensions_.InternalSerializeWithCachedSizesToArray(\n"
" $start$, $end$, target);\n\n"); " $start$, $end$, false, target);\n\n");
} else { } else {
printer->Print(vars, printer->Print(vars,
"_extensions_.SerializeWithCachedSizes(\n" "_extensions_.SerializeWithCachedSizes(\n"
@ -3320,10 +3336,11 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) {
if (descriptor_->options().message_set_wire_format()) { if (descriptor_->options().message_set_wire_format()) {
// Special-case MessageSet. // Special-case MessageSet.
printer->Print( printer->Print(
"::google::protobuf::uint8* $classname$::SerializeWithCachedSizesToArray(\n" "::google::protobuf::uint8* $classname$::InternalSerializeWithCachedSizesToArray(\n"
" ::google::protobuf::uint8* target) const {\n" " bool deterministic, ::google::protobuf::uint8* target) const {\n"
" target =\n" " target = _extensions_."
" _extensions_.SerializeMessageSetWithCachedSizesToArray(target);\n", "InternalSerializeMessageSetWithCachedSizesToArray(\n"
" deterministic, target);\n",
"classname", classname_); "classname", classname_);
GOOGLE_CHECK(UseUnknownFieldSet(descriptor_->file(), options_)); GOOGLE_CHECK(UseUnknownFieldSet(descriptor_->file(), options_));
printer->Print( printer->Print(
@ -3337,8 +3354,8 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) {
} }
printer->Print( printer->Print(
"::google::protobuf::uint8* $classname$::SerializeWithCachedSizesToArray(\n" "::google::protobuf::uint8* $classname$::InternalSerializeWithCachedSizesToArray(\n"
" ::google::protobuf::uint8* target) const {\n", " bool deterministic, ::google::protobuf::uint8* target) const {\n",
"classname", classname_); "classname", classname_);
printer->Indent(); printer->Indent();

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

Loading…
Cancel
Save