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/ExtensionLite.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/FieldSet.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/DynamicMessageTest.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/FloatArrayListTest.java \
java/core/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java \

@ -60,6 +60,34 @@ public abstract class AbstractMessage
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
public List<String> findInitializationErrors() {
@ -460,6 +488,31 @@ public abstract class AbstractMessage
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
// not produce weird errors like:
@ -550,4 +603,44 @@ public abstract class AbstractMessage
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());
return out.build();
} catch (IOException e) {
throw new RuntimeException(
"Serializing to a ByteString threw an IOException (should " +
"never happen).", e);
throw new RuntimeException(getSerializingExceptionMessage("ByteString"), e);
}
}
@ -72,9 +70,7 @@ public abstract class AbstractMessageLite<
output.checkNoSpaceLeft();
return result;
} catch (IOException e) {
throw new RuntimeException(
"Serializing to a byte array threw an IOException " +
"(should never happen).", e);
throw new RuntimeException(getSerializingExceptionMessage("byte array"), e);
}
}
@ -109,6 +105,11 @@ public abstract class AbstractMessageLite<
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)
throws IllegalArgumentException {
if (!byteString.isValidUtf8()) {
@ -156,9 +157,7 @@ public abstract class AbstractMessageLite<
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (IOException e) {
throw new RuntimeException(
"Reading from a ByteString threw an IOException (should " +
"never happen).", e);
throw new RuntimeException(getReadingExceptionMessage("ByteString"), e);
}
}
@ -174,9 +173,7 @@ public abstract class AbstractMessageLite<
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (IOException e) {
throw new RuntimeException(
"Reading from a ByteString threw an IOException (should " +
"never happen).", e);
throw new RuntimeException(getReadingExceptionMessage("ByteString"), e);
}
}
@ -197,9 +194,7 @@ public abstract class AbstractMessageLite<
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (IOException e) {
throw new RuntimeException(
"Reading from a byte array threw an IOException (should " +
"never happen).", e);
throw new RuntimeException(getReadingExceptionMessage("byte array"), e);
}
}
@ -225,9 +220,7 @@ public abstract class AbstractMessageLite<
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (IOException e) {
throw new RuntimeException(
"Reading from a byte array threw an IOException (should " +
"never happen).", e);
throw new RuntimeException(getReadingExceptionMessage("byte array"), e);
}
}
@ -335,6 +328,11 @@ public abstract class AbstractMessageLite<
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
* the given message.

@ -42,7 +42,8 @@ import java.util.RandomAccess;
* @author dweis@google.com (Daniel Weis)
*/
final class BooleanArrayList
extends AbstractProtobufList<Boolean> implements BooleanList, RandomAccess {
extends AbstractProtobufList<Boolean>
implements BooleanList, RandomAccess {
private static final BooleanArrayList EMPTY_LIST = new BooleanArrayList();
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) {
this.array = array;
private BooleanArrayList(boolean[] other, int size) {
array = other;
this.size = size;
}

@ -36,12 +36,9 @@ import com.google.protobuf.Utf8.UnpairedSurrogateException;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -59,9 +56,8 @@ import java.util.logging.Logger;
*/
public abstract class CodedOutputStream extends ByteOutput {
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 = supportsUnsafeArrayOperations();
private static final long ARRAY_BASE_OFFSET = byteArrayBaseOffset();
private static final boolean HAS_UNSAFE_ARRAY_OPERATIONS = UnsafeUtil.hasUnsafeArrayOperations();
private static final long ARRAY_BASE_OFFSET = UnsafeUtil.getArrayBaseOffset();
private static final int FIXED_32_SIZE = 4;
private static final int FIXED_64_SIZE = 8;
@ -869,7 +865,7 @@ public abstract class CodedOutputStream extends ByteOutput {
return computeLengthDelimitedFieldSize(value.getSerializedSize());
}
private static int computeLengthDelimitedFieldSize(int fieldLength) {
static int computeLengthDelimitedFieldSize(int fieldLength) {
return computeUInt32SizeNoTag(fieldLength) + fieldLength;
}
@ -948,6 +944,10 @@ public abstract class CodedOutputStream extends ByteOutput {
OutOfSpaceException(Throwable cause) {
super(MESSAGE, cause);
}
OutOfSpaceException(String explanationMessage, Throwable cause) {
super(MESSAGE + ": " + explanationMessage, cause);
}
}
/**
@ -1250,8 +1250,8 @@ public abstract class CodedOutputStream extends ByteOutput {
try {
buffer[position++] = value;
} catch (IndexOutOfBoundsException e) {
throw new OutOfSpaceException(new IndexOutOfBoundsException(
String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)));
throw new OutOfSpaceException(
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;
while (true) {
if ((value & ~0x7F) == 0) {
UNSAFE.putByte(buffer, pos++, (byte) value);
UnsafeUtil.putByte(buffer, pos++, (byte) value);
position++;
return;
} else {
UNSAFE.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80));
UnsafeUtil.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80));
position++;
value >>>= 7;
}
@ -1293,8 +1293,7 @@ public abstract class CodedOutputStream extends ByteOutput {
}
} catch (IndexOutOfBoundsException e) {
throw new OutOfSpaceException(
new IndexOutOfBoundsException(
String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)));
String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e);
}
}
}
@ -1308,8 +1307,7 @@ public abstract class CodedOutputStream extends ByteOutput {
buffer[position++] = (byte) ((value >> 24) & 0xFF);
} catch (IndexOutOfBoundsException e) {
throw new OutOfSpaceException(
new IndexOutOfBoundsException(
String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)));
String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e);
}
}
@ -1319,11 +1317,11 @@ public abstract class CodedOutputStream extends ByteOutput {
long pos = ARRAY_BASE_OFFSET + position;
while (true) {
if ((value & ~0x7FL) == 0) {
UNSAFE.putByte(buffer, pos++, (byte) value);
UnsafeUtil.putByte(buffer, pos++, (byte) value);
position++;
return;
} else {
UNSAFE.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80));
UnsafeUtil.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80));
position++;
value >>>= 7;
}
@ -1341,8 +1339,7 @@ public abstract class CodedOutputStream extends ByteOutput {
}
} catch (IndexOutOfBoundsException e) {
throw new OutOfSpaceException(
new IndexOutOfBoundsException(
String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)));
String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e);
}
}
}
@ -1360,8 +1357,7 @@ public abstract class CodedOutputStream extends ByteOutput {
buffer[position++] = (byte) ((int) (value >> 56) & 0xFF);
} catch (IndexOutOfBoundsException e) {
throw new OutOfSpaceException(
new IndexOutOfBoundsException(
String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)));
String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e);
}
}
@ -1372,8 +1368,7 @@ public abstract class CodedOutputStream extends ByteOutput {
position += length;
} catch (IndexOutOfBoundsException e) {
throw new OutOfSpaceException(
new IndexOutOfBoundsException(
String.format("Pos: %d, limit: %d, len: %d", position, limit, length)));
String.format("Pos: %d, limit: %d, len: %d", position, limit, length), e);
}
}
@ -1390,8 +1385,7 @@ public abstract class CodedOutputStream extends ByteOutput {
position += length;
} catch (IndexOutOfBoundsException e) {
throw new OutOfSpaceException(
new IndexOutOfBoundsException(
String.format("Pos: %d, limit: %d, len: %d", position, limit, length)));
String.format("Pos: %d, limit: %d, len: %d", position, limit, length), e);
}
}
@ -1855,10 +1849,10 @@ public abstract class CodedOutputStream extends ByteOutput {
long pos = originalPos;
while (true) {
if ((value & ~0x7F) == 0) {
UNSAFE.putByte(buffer, pos++, (byte) value);
UnsafeUtil.putByte(buffer, pos++, (byte) value);
break;
} else {
UNSAFE.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80));
UnsafeUtil.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80));
value >>>= 7;
}
}
@ -1890,10 +1884,10 @@ public abstract class CodedOutputStream extends ByteOutput {
long pos = originalPos;
while (true) {
if ((value & ~0x7FL) == 0) {
UNSAFE.putByte(buffer, pos++, (byte) value);
UnsafeUtil.putByte(buffer, pos++, (byte) value);
break;
} else {
UNSAFE.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80));
UnsafeUtil.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80));
value >>>= 7;
}
}
@ -2600,65 +2594,4 @@ public abstract class CodedOutputStream extends ByteOutput {
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));
}
for (int i = 0; i < oneofs.length; i++) {
oneofs[i].setProto(proto.getOneofDecl(i));
}
for (int i = 0; i < enumTypes.length; i++) {
enumTypes[i].setProto(proto.getEnumType(i));
}
@ -2513,6 +2517,10 @@ public final class Descriptors {
public int getFieldCount() { return fieldCount; }
public OneofOptions getOptions() {
return proto.getOptions();
}
/** Get a list of this message type's fields. */
public List<FieldDescriptor> getFields() {
return Collections.unmodifiableList(Arrays.asList(fields));
@ -2522,6 +2530,10 @@ public final class Descriptors {
return fields[index];
}
private void setProto(final OneofDescriptorProto proto) {
this.proto = proto;
}
private OneofDescriptor(final OneofDescriptorProto proto,
final FileDescriptor file,
final Descriptor parent,

@ -42,7 +42,8 @@ import java.util.RandomAccess;
* @author dweis@google.com (Daniel Weis)
*/
final class DoubleArrayList
extends AbstractProtobufList<Double> implements DoubleList, RandomAccess {
extends AbstractProtobufList<Double>
implements DoubleList, RandomAccess {
private static final DoubleArrayList EMPTY_LIST = new DoubleArrayList();
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) {
this.array = array;
private DoubleArrayList(double[] other, int size) {
array = other;
this.size = size;
}

@ -101,7 +101,7 @@ public class ExtensionRegistry extends ExtensionRegistryLite {
/** Get the unmodifiable singleton empty instance. */
public static ExtensionRegistry getEmptyRegistry() {
return EMPTY;
return EMPTY_REGISTRY;
}
@ -243,6 +243,11 @@ public class ExtensionRegistry extends ExtensionRegistryLite {
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) {
if (extension.getDescriptor().getJavaType() ==
FieldDescriptor.JavaType.MESSAGE) {
@ -311,7 +316,7 @@ public class ExtensionRegistry extends ExtensionRegistryLite {
private final Map<DescriptorIntPair, ExtensionInfo> mutableExtensionsByNumber;
ExtensionRegistry(boolean empty) {
super(ExtensionRegistryLite.getEmptyRegistry());
super(EMPTY_REGISTRY_LITE);
this.immutableExtensionsByName =
Collections.<String, ExtensionInfo>emptyMap();
this.mutableExtensionsByName =
@ -321,7 +326,7 @@ public class ExtensionRegistry extends ExtensionRegistryLite {
this.mutableExtensionsByNumber =
Collections.<DescriptorIntPair, ExtensionInfo>emptyMap();
}
private static final ExtensionRegistry EMPTY = new ExtensionRegistry(true);
static final ExtensionRegistry EMPTY_REGISTRY = new ExtensionRegistry(true);
private void add(
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.
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() {
return eagerlyParseMessageSets;
}
@ -87,14 +103,22 @@ public class ExtensionRegistryLite {
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() {
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() {
return EMPTY;
return ExtensionRegistryFactory.createEmpty();
}
/** Returns an unmodifiable view of the registry. */
@ -128,6 +152,23 @@ public class ExtensionRegistryLite {
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.
@ -139,9 +180,11 @@ public class ExtensionRegistryLite {
new HashMap<ObjectIntPair,
GeneratedMessageLite.GeneratedExtension<?, ?>>();
}
static final ExtensionRegistryLite EMPTY_REGISTRY_LITE =
new ExtensionRegistryLite(true);
ExtensionRegistryLite(ExtensionRegistryLite other) {
if (other == EMPTY) {
if (other == EMPTY_REGISTRY_LITE) {
this.extensionsByNumber = Collections.emptyMap();
} else {
this.extensionsByNumber =
@ -153,11 +196,9 @@ public class ExtensionRegistryLite {
GeneratedMessageLite.GeneratedExtension<?, ?>>
extensionsByNumber;
private ExtensionRegistryLite(boolean empty) {
ExtensionRegistryLite(boolean empty) {
this.extensionsByNumber = Collections.emptyMap();
}
private static final ExtensionRegistryLite EMPTY =
new ExtensionRegistryLite(true);
/** A (Object, int) pair, used as a map key. */
private static final class ObjectIntPair {

@ -132,7 +132,7 @@ final class FieldSet<FieldDescriptorType extends
}
FieldSet<?> other = (FieldSet<?>) o;
return other.fields.equals(other.fields);
return fields.equals(other.fields);
}
@Override
@ -638,7 +638,8 @@ final class FieldSet<FieldDescriptorType extends
* {@link Message#getField(Descriptors.FieldDescriptor)} for
* this field.
*/
private static void writeElement(final CodedOutputStream output,
static void writeElement(
final CodedOutputStream output,
final WireFormat.FieldType type,
final int number,
final Object value) throws IOException {
@ -804,9 +805,8 @@ final class FieldSet<FieldDescriptorType extends
* {@link Message#getField(Descriptors.FieldDescriptor)} for
* this field.
*/
private static int computeElementSize(
final WireFormat.FieldType type,
final int number, final Object value) {
static int computeElementSize(
final WireFormat.FieldType type, final int number, final Object value) {
int tagSize = CodedOutputStream.computeTagSize(number);
if (type == WireFormat.FieldType.GROUP) {
// 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)
*/
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();
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) {
this.array = array;
private FloatArrayList(float[] other, int size) {
array = other;
this.size = size;
}

@ -355,31 +355,30 @@ public abstract class GeneratedMessage extends AbstractMessage
// Noop for messages without extensions.
}
protected abstract Message.Builder newBuilderForType(BuilderParent parent);
/**
* Interface for the parent of a Builder that allows the builder to
* communicate invalidations back to the parent for use when using nested
* builders.
* TODO(xiaofeng): remove this after b/29368482 is fixed. We need to move this
* interface to AbstractMessage in order to versioning GeneratedMessage but
* 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
* 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.
* TODO(xiaofeng): remove this together with GeneratedMessage.BuilderParent.
*/
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")
public abstract static class Builder <BuilderType extends Builder<BuilderType>>
extends AbstractMessage.Builder<BuilderType> {
@ -403,6 +402,7 @@ public abstract class GeneratedMessage extends AbstractMessage
this.builderParent = builderParent;
}
@Override
void dispose() {
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
* built and may be cached and therefore invalidations are needed.
*/
@Override
protected void markClean() {
this.isClean = true;
}
@ -755,6 +756,33 @@ public abstract class GeneratedMessage extends AbstractMessage
<Type> Type getExtension(
ExtensionLite<MessageType, List<Type>> extension,
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));
}
/** 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. */
protected boolean extensionsAreInitialized() {
return extensions.isInitialized();
@ -1269,6 +1344,95 @@ public abstract class GeneratedMessage extends AbstractMessage
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. */
protected boolean extensionsAreInitialized() {
return extensions.isInitialized();

@ -1191,7 +1191,7 @@ public abstract class GeneratedMessageLite<
} catch (ClassNotFoundException e) {
throw new RuntimeException("Unable to find proto buffer class: " + messageClassName, e);
} catch (NoSuchFieldException e) {
throw new RuntimeException("Unable to find DEFAULT_INSTANCE in " + messageClassName, e);
return readResolveFallback();
} catch (SecurityException e) {
throw new RuntimeException("Unable to call DEFAULT_INSTANCE in " + messageClassName, e);
} catch (IllegalAccessException e) {
@ -1200,6 +1200,33 @@ public abstract class GeneratedMessageLite<
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.
*/
<T extends MessageLite> T visitMessage(T mine, T other);
LazyFieldLite visitLazyMessage(
boolean minePresent, LazyFieldLite mine, boolean otherPresent, LazyFieldLite other);
LazyFieldLite visitLazyMessage(LazyFieldLite mine, LazyFieldLite other);
<T> ProtobufList<T> visitList(ProtobufList<T> mine, ProtobufList<T> other);
BooleanList visitBooleanList(BooleanList mine, BooleanList other);
@ -1719,10 +1745,14 @@ public abstract class GeneratedMessageLite<
@Override
public LazyFieldLite visitLazyMessage(
boolean minePresent, LazyFieldLite mine, boolean otherPresent, LazyFieldLite other) {
if (!minePresent && !otherPresent) {
return mine;
} else if (minePresent && otherPresent && mine.equals(other)) {
LazyFieldLite mine, LazyFieldLite other) {
if (mine == null && other == null) {
return null;
}
if (mine == null || other == null) {
throw NOT_EQUALS;
}
if (mine.equals(other)) {
return mine;
}
throw NOT_EQUALS;
@ -1939,9 +1969,14 @@ public abstract class GeneratedMessageLite<
}
@Override
public LazyFieldLite visitLazyMessage(
boolean minePresent, LazyFieldLite mine, boolean otherPresent, LazyFieldLite other) {
hashCode = (53 * hashCode) + mine.hashCode();
public LazyFieldLite visitLazyMessage(LazyFieldLite mine, LazyFieldLite other) {
final int protoHash;
if (mine != null) {
protoHash = mine.hashCode();
} else {
protoHash = 37;
}
hashCode = (53 * hashCode) + protoHash;
return mine;
}
@ -2089,13 +2124,10 @@ public abstract class GeneratedMessageLite<
@Override
public Object visitOneofLazyMessage(boolean minePresent, Object mine, Object other) {
if (minePresent) {
LazyFieldLite lazy = (LazyFieldLite) mine;
LazyFieldLite lazy = minePresent ? (LazyFieldLite) mine : new LazyFieldLite();
lazy.merge((LazyFieldLite) other);
return lazy;
}
return other;
}
@Override
public Object visitOneofMessage(boolean minePresent, Object mine, Object other) {
@ -2121,12 +2153,13 @@ public abstract class GeneratedMessageLite<
}
@Override
public LazyFieldLite visitLazyMessage(
boolean minePresent, LazyFieldLite mine, boolean otherPresent, LazyFieldLite other) {
// LazyFieldLite's are never null so we can just copy across. Necessary to avoid leakage
// from builder into immutable message.
// TODO(dweis): Change to null sentinels?
public LazyFieldLite visitLazyMessage(LazyFieldLite mine, LazyFieldLite other) {
if (other != null) {
if (mine == null) {
mine = new LazyFieldLite();
}
mine.merge(other);
}
return mine;
}
@ -2235,7 +2268,12 @@ public abstract class GeneratedMessageLite<
@Override
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);
}
return mine;
}
}

@ -41,7 +41,9 @@ import java.util.RandomAccess;
*
* @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();
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) {
this.array = array;
private IntArrayList(int[] other, int size) {
array = other;
this.size = size;
}

@ -41,7 +41,9 @@ import java.util.RandomAccess;
*
* @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();
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) {
this.array = array;
private LongArrayList(long[] other, int size) {
array = other;
this.size = size;
}
@ -83,7 +86,7 @@ final class LongArrayList extends AbstractProtobufList<Long> implements LongList
if (this == o) {
return true;
}
if (!(o instanceof IntArrayList)) {
if (!(o instanceof LongArrayList)) {
return super.equals(o);
}
LongArrayList other = (LongArrayList) o;

@ -49,48 +49,68 @@ import java.util.TreeMap;
* Protobuf internal. Users shouldn't use this class.
*/
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 MapEntry<K, V> defaultInstance;
public final AbstractParser<MapEntry<K, V>> parser;
public final Parser<MapEntry<K, V>> parser;
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.defaultInstance = defaultInstance;
final Metadata<K, V> thisMetadata = this;
this.parser = new AbstractParser<MapEntry<K, V>>() {
private final Parser<MapEntryLite<K, V>> dataParser =
defaultInstance.data.getParserForType();
@Override
public MapEntry<K, V> parsePartialFrom(
CodedInputStream input, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
MapEntryLite<K, V> data =
dataParser.parsePartialFrom(input, extensionRegistry);
return new MapEntry<K, V>(thisMetadata, data);
return new MapEntry<K, V>(Metadata.this, input, extensionRegistry);
}
};
}
}
private final K key;
private final V value;
private final Metadata<K, V> metadata;
private final MapEntryLite<K, V> data;
/** Create a default MapEntry instance. */
private MapEntry(Descriptor descriptor,
private MapEntry(
Descriptor descriptor,
WireFormat.FieldType keyType, K defaultKey,
WireFormat.FieldType valueType, V defaultValue) {
this.data = MapEntryLite.newDefaultInstance(
keyType, defaultKey, valueType, defaultValue);
this.metadata = new Metadata<K, V>(descriptor, this);
this.key = defaultKey;
this.value = defaultValue;
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. */
private MapEntry(Metadata<K, V> metadata, MapEntryLite<K, V> data) {
/** Parsing constructor. */
private MapEntry(
Metadata<K, V> metadata,
CodedInputStream input,
ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
try {
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() {
return data.getKey();
return key;
}
public V getValue() {
return data.getValue();
return value;
}
private volatile int cachedSerializedSize = -1;
@Override
public int getSerializedSize() {
return data.getSerializedSize();
if (cachedSerializedSize != -1) {
return cachedSerializedSize;
}
int size = MapEntryLite.computeSerializedSize(metadata, key, value);
cachedSerializedSize = size;
return size;
}
@Override
public void writeTo(CodedOutputStream output) throws IOException {
data.writeTo(output);
MapEntryLite.writeTo(output, metadata, key, value);
}
@Override
public boolean isInitialized() {
return data.isInitialized();
return isInitialized(metadata, value);
}
@Override
@ -142,12 +170,12 @@ public final class MapEntry<K, V> extends AbstractMessage {
@Override
public Builder<K, V> toBuilder() {
return new Builder<K, V>(metadata, data);
return new Builder<K, V>(metadata, key, value);
}
@Override
public MapEntry<K, V> getDefaultInstanceForType() {
return metadata.defaultInstance;
return new MapEntry<K, V>(metadata, metadata.defaultKey, metadata.defaultValue);
}
@Override
@ -157,8 +185,7 @@ public final class MapEntry<K, V> extends AbstractMessage {
@Override
public Map<FieldDescriptor, Object> getAllFields() {
final TreeMap<FieldDescriptor, Object> result =
new TreeMap<FieldDescriptor, Object>();
TreeMap<FieldDescriptor, Object> result = new TreeMap<FieldDescriptor, Object>();
for (final FieldDescriptor field : metadata.descriptor.getFields()) {
if (hasField(field)) {
result.put(field, getField(field));
@ -217,56 +244,44 @@ public final class MapEntry<K, V> extends AbstractMessage {
public static class Builder<K, V>
extends AbstractMessage.Builder<Builder<K, V>> {
private final Metadata<K, V> metadata;
private MapEntryLite<K, V> data;
private MapEntryLite.Builder<K, V> dataBuilder;
private K key;
private V value;
private Builder(Metadata<K, V> metadata) {
this.metadata = metadata;
this.data = metadata.defaultInstance.data;
this.dataBuilder = null;
this(metadata, metadata.defaultKey, metadata.defaultValue);
}
private Builder(Metadata<K, V> metadata, MapEntryLite<K, V> data) {
private Builder(Metadata<K, V> metadata, K key, V value) {
this.metadata = metadata;
this.data = data;
this.dataBuilder = null;
this.key = key;
this.value = value;
}
public K getKey() {
return dataBuilder == null ? data.getKey() : dataBuilder.getKey();
return key;
}
public V getValue() {
return dataBuilder == null ? data.getValue() : dataBuilder.getValue();
}
private void ensureMutable() {
if (dataBuilder == null) {
dataBuilder = data.toBuilder();
}
return value;
}
public Builder<K, V> setKey(K key) {
ensureMutable();
dataBuilder.setKey(key);
this.key = key;
return this;
}
public Builder<K, V> clearKey() {
ensureMutable();
dataBuilder.clearKey();
this.key = metadata.defaultKey;
return this;
}
public Builder<K, V> setValue(V value) {
ensureMutable();
dataBuilder.setValue(value);
this.value = value;
return this;
}
public Builder<K, V> clearValue() {
ensureMutable();
dataBuilder.clearValue();
this.value = metadata.defaultValue;
return this;
}
@ -281,11 +296,7 @@ public final class MapEntry<K, V> extends AbstractMessage {
@Override
public MapEntry<K, V> buildPartial() {
if (dataBuilder != null) {
data = dataBuilder.buildPartial();
dataBuilder = null;
}
return new MapEntry<K, V>(metadata, data);
return new MapEntry<K, V>(metadata, key, value);
}
@Override
@ -302,8 +313,7 @@ public final class MapEntry<K, V> extends AbstractMessage {
}
@Override
public com.google.protobuf.Message.Builder newBuilderForField(
FieldDescriptor field) {
public Message.Builder newBuilderForField(FieldDescriptor field) {
checkFieldDescriptor(field);;
// This method should be called for message fields and in a MapEntry
// message only the value field can possibly be a message field.
@ -312,7 +322,7 @@ public final class MapEntry<K, V> extends AbstractMessage {
throw new RuntimeException(
"\"" + field.getFullName() + "\" is not a message value field.");
}
return ((Message) data.getValue()).newBuilderForType();
return ((Message) value).newBuilderForType();
}
@SuppressWarnings("unchecked")
@ -362,22 +372,17 @@ public final class MapEntry<K, V> extends AbstractMessage {
@Override
public MapEntry<K, V> getDefaultInstanceForType() {
return metadata.defaultInstance;
return new MapEntry<K, V>(metadata, metadata.defaultKey, metadata.defaultValue);
}
@Override
public boolean isInitialized() {
if (dataBuilder != null) {
return dataBuilder.isInitialized();
} else {
return data.isInitialized();
}
return MapEntry.isInitialized(metadata, value);
}
@Override
public Map<FieldDescriptor, Object> getAllFields() {
final TreeMap<FieldDescriptor, Object> result =
new TreeMap<FieldDescriptor, Object>();
final TreeMap<FieldDescriptor, Object> result = new TreeMap<FieldDescriptor, Object>();
for (final FieldDescriptor field : metadata.descriptor.getFields()) {
if (hasField(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();
// Convert enums to EnumValueDescriptor.
if (field.getType() == FieldDescriptor.Type.ENUM) {
result = field.getEnumType().findValueByNumberCreatingIfUnknown(
(java.lang.Integer) result);
result = field.getEnumType().findValueByNumberCreatingIfUnknown((Integer) result);
}
return result;
}
@ -423,11 +427,14 @@ public final class MapEntry<K, V> extends AbstractMessage {
@Override
public Builder<K, V> clone() {
if (dataBuilder == null) {
return new Builder<K, V>(metadata, data);
} else {
return new Builder<K, V>(metadata, dataBuilder.build());
return new Builder(metadata, key, value);
}
}
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;
import java.io.IOException;
import java.util.AbstractMap;
import java.util.Map;
/**
* Implements the lite version of map entry messages.
@ -41,29 +43,21 @@ import java.io.IOException;
*
* Protobuf internal. Users shouldn't use.
*/
public class MapEntryLite<K, V>
extends AbstractMessageLite<MapEntryLite<K, V>, MapEntryLite.Builder<K, V>> {
private static class Metadata<K, V> {
public final MapEntryLite<K, V> defaultInstance;
public class MapEntryLite<K, V> {
static class Metadata<K, V> {
public final WireFormat.FieldType keyType;
public final K defaultKey;
public final WireFormat.FieldType valueType;
public final Parser<MapEntryLite<K, V>> parser;
public final V defaultValue;
public Metadata(
MapEntryLite<K, V> defaultInstance,
WireFormat.FieldType keyType,
WireFormat.FieldType valueType) {
this.defaultInstance = defaultInstance;
WireFormat.FieldType keyType, K defaultKey,
WireFormat.FieldType valueType, V defaultValue) {
this.keyType = keyType;
this.defaultKey = defaultKey;
this.valueType = valueType;
final Metadata<K, V> finalThis = this;
this.parser = new AbstractParser<MapEntryLite<K, V>>() {
@Override
public MapEntryLite<K, V> parsePartialFrom(
CodedInputStream input, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
return new MapEntryLite<K, V>(finalThis, input, extensionRegistry);
}
};
this.defaultValue = defaultValue;
}
}
@ -78,7 +72,7 @@ public class MapEntryLite<K, V>
private MapEntryLite(
WireFormat.FieldType keyType, K defaultKey,
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.value = defaultValue;
}
@ -113,79 +107,19 @@ public class MapEntryLite<K, V>
keyType, defaultKey, valueType, defaultValue);
}
@Override
public void writeTo(CodedOutputStream output) throws IOException {
writeField(KEY_FIELD_NUMBER, metadata.keyType, key, output);
writeField(VALUE_FIELD_NUMBER, metadata.valueType, value, output);
}
private void writeField(
int number, WireFormat.FieldType type, Object value,
CodedOutputStream output) throws IOException {
output.writeTag(number, type.getWireType());
FieldSet.writeElementNoTag(output, type, value);
}
private volatile int cachedSerializedSize = -1;
@Override
public int getSerializedSize() {
if (cachedSerializedSize != -1) {
return cachedSerializedSize;
}
int size = 0;
size += getFieldSize(KEY_FIELD_NUMBER, metadata.keyType, key);
size += getFieldSize(VALUE_FIELD_NUMBER, metadata.valueType, value);
cachedSerializedSize = size;
return size;
}
private int getFieldSize(
int number, WireFormat.FieldType type, Object value) {
return CodedOutputStream.computeTagSize(number)
+ FieldSet.computeElementSizeNoTag(type, value);
static <K, V> void writeTo(CodedOutputStream output, Metadata<K, V> metadata, K key, V value)
throws IOException {
FieldSet.writeElement(output, metadata.keyType, KEY_FIELD_NUMBER, key);
FieldSet.writeElement(output, metadata.valueType, VALUE_FIELD_NUMBER, value);
}
/** Parsing constructor. */
private MapEntryLite(
Metadata<K, V> metadata,
CodedInputStream input,
ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
try {
K key = metadata.defaultInstance.key;
V value = metadata.defaultInstance.value;
while (true) {
int tag = input.readTag();
if (tag == 0) {
break;
}
if (tag == WireFormat.makeTag(
KEY_FIELD_NUMBER, metadata.keyType.getWireType())) {
key = mergeField(
input, extensionRegistry, metadata.keyType, key);
} else if (tag == WireFormat.makeTag(
VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) {
value = mergeField(
input, extensionRegistry, metadata.valueType, value);
} else {
if (!input.skipField(tag)) {
break;
}
}
}
this.metadata = metadata;
this.key = key;
this.value = value;
} catch (InvalidProtocolBufferException e) {
throw e.setUnfinishedMessage(this);
} catch (IOException e) {
throw new InvalidProtocolBufferException(e.getMessage())
.setUnfinishedMessage(this);
}
static <K, V> int computeSerializedSize(Metadata<K, V> metadata, K key, V value) {
return FieldSet.computeElementSize(metadata.keyType, KEY_FIELD_NUMBER, key)
+ FieldSet.computeElementSize(metadata.valueType, VALUE_FIELD_NUMBER, value);
}
@SuppressWarnings("unchecked")
private <T> T mergeField(
static <T> T parseField(
CodedInputStream input, ExtensionRegistryLite extensionRegistry,
WireFormat.FieldType type, T value) throws IOException {
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>
extends AbstractMessageLite.Builder<MapEntryLite<K, V>, Builder<K, V>> {
private final Metadata<K, V> metadata;
private K key;
private V value;
private Builder(Metadata<K, V> metadata) {
this.metadata = metadata;
this.key = metadata.defaultInstance.key;
this.value = metadata.defaultInstance.value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public Builder<K, V> setKey(K key) {
this.key = key;
return this;
public void serializeTo(CodedOutputStream output, int fieldNumber, K key, V value)
throws IOException {
output.writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
output.writeUInt32NoTag(computeSerializedSize(metadata, key, value));
writeTo(output, metadata, key, value);
}
public Builder<K, V> setValue(V value) {
this.value = value;
return this;
/**
* Computes the message size for the provided key and value as though they were wrapped
* 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;
return this;
/**
* Parses an entry off of the input as a {@link Map.Entry}. This helper requires an allocation
* 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() {
this.value = metadata.defaultInstance.value;
return this;
static <K, V> Map.Entry<K, V> parseEntry(
CodedInputStream input, Metadata<K, V> metadata, ExtensionRegistryLite extensionRegistry)
throws IOException{
K key = metadata.defaultKey;
V value = metadata.defaultValue;
while (true) {
int tag = input.readTag();
if (tag == 0) {
break;
}
@Override
public Builder<K, V> clear() {
this.key = metadata.defaultInstance.key;
this.value = metadata.defaultInstance.value;
return this;
if (tag == WireFormat.makeTag(KEY_FIELD_NUMBER, metadata.keyType.getWireType())) {
key = parseField(input, extensionRegistry, metadata.keyType, key);
} else if (tag == WireFormat.makeTag(VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) {
value = parseField(input, extensionRegistry, metadata.valueType, value);
} else {
if (!input.skipField(tag)) {
break;
}
@Override
public MapEntryLite<K, V> build() {
MapEntryLite<K, V> result = buildPartial();
if (!result.isInitialized()) {
throw newUninitializedMessageException(result);
}
return result;
}
@Override
public MapEntryLite<K, V> buildPartial() {
return new MapEntryLite<K, V>(metadata, key, value);
return new AbstractMap.SimpleImmutableEntry<K, V>(key, value);
}
@Override
public MessageLite getDefaultInstanceForType() {
return metadata.defaultInstance;
}
/**
* Parses an entry off of the input into the map. This helper avoids allocaton of a
* {@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
public boolean isInitialized() {
if (metadata.valueType.getJavaType() == WireFormat.JavaType.MESSAGE) {
return ((MessageLite) value).isInitialized();
}
return true;
while (true) {
int tag = input.readTag();
if (tag == 0) {
break;
}
private Builder(Metadata<K, V> metadata, K key, V value) {
this.metadata = metadata;
this.key = key;
this.value = value;
if (tag == WireFormat.makeTag(KEY_FIELD_NUMBER, metadata.keyType.getWireType())) {
key = parseField(input, extensionRegistry, metadata.keyType, key);
} else if (tag == WireFormat.makeTag(VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) {
value = parseField(input, extensionRegistry, metadata.valueType, 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
protected Builder<K, V> internalMergeFrom(MapEntryLite<K, V> message) {
throw new UnsupportedOperationException();
}
input.checkLastTagWas(0);
input.popLimit(oldLimit);
map.put(key, value);
}
}

@ -30,13 +30,14 @@
package com.google.protobuf;
import com.google.protobuf.MapFieldLite.MutatabilityAwareMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Internal representation of map fields in generated messages.
@ -286,4 +287,338 @@ public class MapField<K, V> implements MutabilityOracle {
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 java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
@ -46,18 +44,21 @@ import java.util.Set;
* This class is a protobuf implementation detail. Users shouldn't use this
* class directly.
*/
public final class MapFieldLite<K, V> implements MutabilityOracle {
private MutatabilityAwareMap<K, V> mapData;
public final class MapFieldLite<K, V> extends LinkedHashMap<K, V> {
private boolean isMutable;
private MapFieldLite() {
this.isMutable = true;
}
private MapFieldLite(Map<K, V> mapData) {
this.mapData = new MutatabilityAwareMap<K, V>(this, mapData);
super(mapData);
this.isMutable = true;
}
@SuppressWarnings({"rawtypes", "unchecked"})
private static final MapFieldLite EMPTY_MAP_FIELD =
new MapFieldLite(Collections.emptyMap());
private static final MapFieldLite EMPTY_MAP_FIELD = new MapFieldLite(Collections.emptyMap());
static {
EMPTY_MAP_FIELD.makeImmutable();
}
@ -68,27 +69,40 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
return (MapFieldLite<K, V>) EMPTY_MAP_FIELD;
}
/** Creates a new MapFieldLite instance. */
public static <K, V> MapFieldLite<K, V> newMapField() {
return new MapFieldLite<K, V>(new LinkedHashMap<K, V>());
public void mergeFrom(MapFieldLite<K, V> other) {
ensureMutable();
if (!other.isEmpty()) {
putAll(other);
}
}
/** Gets the content of this MapField as a read-only Map. */
public Map<K, V> getMap() {
return Collections.unmodifiableMap(mapData);
@SuppressWarnings({"unchecked", "cast"})
@Override public Set<Map.Entry<K, V>> entrySet() {
return isEmpty() ? Collections.<Map.Entry<K, V>>emptySet() : super.entrySet();
}
/** Gets a mutable Map view of this MapField. */
public Map<K, V> getMutableMap() {
return mapData;
@Override public void clear() {
ensureMutable();
clear();
}
public void mergeFrom(MapFieldLite<K, V> other) {
mapData.putAll(copy(other.mapData));
@Override public V put(K key, V value) {
ensureMutable();
return super.put(key, value);
}
public V put(Map.Entry<K, V> entry) {
return put(entry.getKey(), entry.getValue());
}
public void clear() {
mapData.clear();
@Override public void putAll(Map<? extends K, ? extends V> m) {
ensureMutable();
super.putAll(m);
}
@Override public V remove(Object key) {
ensureMutable();
return super.remove(key);
}
private static boolean equals(Object a, Object b) {
@ -127,11 +141,7 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object object) {
if (!(object instanceof MapFieldLite)) {
return false;
}
MapFieldLite<K, V> other = (MapFieldLite<K, V>) object;
return equals(mapData, other.mapData);
return (object instanceof Map) && equals(this, (Map<K, V>) object);
}
private static int calculateHashCodeForObject(Object a) {
@ -161,7 +171,7 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
@Override
public int hashCode() {
return calculateHashCodeForMap(mapData);
return calculateHashCodeForMap(this);
}
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. */
public MapFieldLite<K, V> copy() {
return new MapFieldLite<K, V>(copy(mapData));
public MapFieldLite<K, V> mutableCopy() {
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;
}
@Override
public void ensureMutable() {
private void ensureMutable() {
if (!isMutable()) {
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.
*/
Object finish();
}
static class BuilderAdapter implements MergeTarget {
@ -549,7 +548,6 @@ class MessageReflection {
public Object finish() {
return builder.buildPartial();
}
}
@ -713,7 +711,6 @@ class MessageReflection {
throw new UnsupportedOperationException(
"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();
}
int getPreviousLine() {
return previousLine;
}
int getPreviousColumn() {
return previousColumn;
}
int getLine() {
return line;
}
@ -1374,6 +1382,28 @@ public final class TextFormat {
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
* into {@code builder}. Extensions will be recognized if they are
@ -1387,9 +1417,13 @@ public final class TextFormat {
MessageReflection.BuilderAdapter target =
new MessageReflection.BuilderAdapter(builder);
List<String> unknownFields = new ArrayList<String>();
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,
final ExtensionRegistry extensionRegistry,
final MessageReflection.MergeTarget target)
final MessageReflection.MergeTarget target,
List<String> unknownFields)
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,
final ExtensionRegistry extensionRegistry,
final MessageReflection.MergeTarget target,
TextFormatParseInfoTree.Builder parseTreeBuilder)
TextFormatParseInfoTree.Builder parseTreeBuilder,
List<String> unknownFields)
throws ParseException {
FieldDescriptor field = null;
int startLine = tokenizer.getLine();
@ -1432,13 +1469,9 @@ public final class TextFormat {
extensionRegistry, name.toString());
if (extension == null) {
if (!allowUnknownFields) {
throw tokenizer.parseExceptionPreviousToken(
"Extension \"" + name + "\" not found in the ExtensionRegistry.");
} else {
logger.warning(
"Extension \"" + name + "\" not found in the ExtensionRegistry.");
}
unknownFields.add((tokenizer.getPreviousLine() + 1) + ":" +
(tokenizer.getPreviousColumn() + 1) + ":\t" +
type.getFullName() + ".[" + name + "]");
} else {
if (extension.descriptor.getContainingType() != type) {
throw tokenizer.parseExceptionPreviousToken(
@ -1473,16 +1506,9 @@ public final class TextFormat {
}
if (field == null) {
if (!allowUnknownFields) {
throw tokenizer.unknownFieldParseExceptionPreviousToken(
name,
"Message type \"" + type.getFullName()
+ "\" has no field named \"" + name + "\".");
} else {
logger.warning(
"Message type \"" + type.getFullName()
+ "\" has no field named \"" + name + "\".");
}
unknownFields.add((tokenizer.getPreviousLine() + 1) + ":" +
(tokenizer.getPreviousColumn() + 1) + ":\t" +
type.getFullName() + "." + name);
}
}
@ -1511,15 +1537,15 @@ public final class TextFormat {
TextFormatParseInfoTree.Builder childParseTreeBuilder =
parseTreeBuilder.getBuilderForSubMessageField(field);
consumeFieldValues(tokenizer, extensionRegistry, target, field, extension,
childParseTreeBuilder);
childParseTreeBuilder, unknownFields);
} else {
consumeFieldValues(tokenizer, extensionRegistry, target, field, extension,
parseTreeBuilder);
parseTreeBuilder, unknownFields);
}
} else {
tokenizer.consume(":"); // required
consumeFieldValues(
tokenizer, extensionRegistry, target, field, extension, parseTreeBuilder);
consumeFieldValues(tokenizer, extensionRegistry, target, field,
extension, parseTreeBuilder, unknownFields);
}
if (parseTreeBuilder != null) {
@ -1544,14 +1570,15 @@ public final class TextFormat {
final MessageReflection.MergeTarget target,
final FieldDescriptor field,
final ExtensionRegistry.ExtensionInfo extension,
final TextFormatParseInfoTree.Builder parseTreeBuilder)
final TextFormatParseInfoTree.Builder parseTreeBuilder,
List<String> unknownFields)
throws ParseException {
// Support specifying repeated field values as a comma-separated list.
// Ex."foo: [1, 2, 3]"
if (field.isRepeated() && tokenizer.tryConsume("[")) {
while (true) {
consumeFieldValue(tokenizer, extensionRegistry, target, field, extension,
parseTreeBuilder);
parseTreeBuilder, unknownFields);
if (tokenizer.tryConsume("]")) {
// End of list.
break;
@ -1559,8 +1586,8 @@ public final class TextFormat {
tokenizer.consume(",");
}
} else {
consumeFieldValue(
tokenizer, extensionRegistry, target, field, extension, parseTreeBuilder);
consumeFieldValue(tokenizer, extensionRegistry, target, field,
extension, parseTreeBuilder, unknownFields);
}
}
@ -1574,7 +1601,8 @@ public final class TextFormat {
final MessageReflection.MergeTarget target,
final FieldDescriptor field,
final ExtensionRegistry.ExtensionInfo extension,
final TextFormatParseInfoTree.Builder parseTreeBuilder)
final TextFormatParseInfoTree.Builder parseTreeBuilder,
List<String> unknownFields)
throws ParseException {
Object value = null;
@ -1596,7 +1624,8 @@ public final class TextFormat {
throw tokenizer.parseException(
"Expected \"" + endToken + "\".");
}
mergeField(tokenizer, extensionRegistry, subField, parseTreeBuilder);
mergeField(tokenizer, extensionRegistry, subField, parseTreeBuilder,
unknownFields);
}
value = subField.finish();

@ -57,6 +57,7 @@ import java.util.TreeMap;
* @author kenton@google.com Kenton Varda
*/
public final class UnknownFieldSet implements MessageLite {
private UnknownFieldSet() {}
/** Create a new {@link Builder}. */
@ -130,7 +131,8 @@ public final class UnknownFieldSet implements MessageLite {
@Override
public void writeTo(final CodedOutputStream output) throws IOException {
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
* {@link ByteString} can lead to unexpected and undesirable consequences in your application,
* 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
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;
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.MIN_SURROGATE;
import static java.lang.Character.isSurrogatePair;
import static java.lang.Character.toCodePoint;
import java.lang.reflect.Field;
import java.nio.Buffer;
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
@ -79,7 +77,6 @@ import java.util.logging.Logger;
*/
// TODO(nathanmittler): Copy changes in this class back to Guava
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
@ -237,7 +234,7 @@ final class Utf8 {
// fallback to more lenient behavior.
static class UnpairedSurrogateException extends IllegalArgumentException {
private UnpairedSurrogateException(int index, int length) {
UnpairedSurrogateException(int index, int 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.
*/
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.
*/
static boolean isAvailable() {
return AVAILABLE;
return hasUnsafeArrayOperations() && hasUnsafeByteBufferOperations();
}
@Override
@ -1016,8 +1001,8 @@ final class Utf8 {
throw new ArrayIndexOutOfBoundsException(
String.format("Array length=%d, index=%d, limit=%d", bytes.length, index, limit));
}
long offset = ARRAY_BASE_OFFSET + index;
final long offsetLimit = ARRAY_BASE_OFFSET + limit;
long offset = getArrayBaseOffset() + index;
final long offsetLimit = getArrayBaseOffset() + limit;
if (state != COMPLETE) {
// The previous decoding operation was incomplete (or malformed).
// 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.
if (byte1 < (byte) 0xC2
// byte2 trailing-byte test
|| UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) {
|| UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
return MALFORMED;
}
} else if (byte1 < (byte) 0xF0) {
@ -1047,7 +1032,7 @@ final class Utf8 {
// Get byte2 from saved state or array
int byte2 = (byte) ~(state >> 8);
if (byte2 == 0) {
byte2 = UNSAFE.getByte(bytes, offset++);
byte2 = UnsafeUtil.getByte(bytes, offset++);
if (offset >= offsetLimit) {
return incompleteStateFor(byte1, byte2);
}
@ -1058,7 +1043,7 @@ final class Utf8 {
// illegal surrogate codepoint?
|| (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
// byte3 trailing-byte test
|| UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) {
|| UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
return MALFORMED;
}
} else {
@ -1068,7 +1053,7 @@ final class Utf8 {
int byte2 = (byte) ~(state >> 8);
int byte3 = 0;
if (byte2 == 0) {
byte2 = UNSAFE.getByte(bytes, offset++);
byte2 = UnsafeUtil.getByte(bytes, offset++);
if (offset >= offsetLimit) {
return incompleteStateFor(byte1, byte2);
}
@ -1076,7 +1061,7 @@ final class Utf8 {
byte3 = (byte) (state >> 16);
}
if (byte3 == 0) {
byte3 = UNSAFE.getByte(bytes, offset++);
byte3 = UnsafeUtil.getByte(bytes, offset++);
if (offset >= offsetLimit) {
return incompleteStateFor(byte1, byte2, byte3);
}
@ -1095,7 +1080,7 @@ final class Utf8 {
// byte3 trailing-byte test
|| byte3 > (byte) 0xBF
// byte4 trailing-byte test
|| UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) {
|| UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
return MALFORMED;
}
}
@ -1134,7 +1119,7 @@ final class Utf8 {
// leading position and overlong 2-byte form.
if (byte1 < (byte) 0xC2
// byte2 trailing-byte test
|| UNSAFE.getByte(address++) > (byte) 0xBF) {
|| UnsafeUtil.getByte(address++) > (byte) 0xBF) {
return MALFORMED;
}
} else if (byte1 < (byte) 0xF0) {
@ -1143,7 +1128,7 @@ final class Utf8 {
// Get byte2 from saved state or array
int byte2 = (byte) ~(state >> 8);
if (byte2 == 0) {
byte2 = UNSAFE.getByte(address++);
byte2 = UnsafeUtil.getByte(address++);
if (address >= addressLimit) {
return incompleteStateFor(byte1, byte2);
}
@ -1154,7 +1139,7 @@ final class Utf8 {
// illegal surrogate codepoint?
|| (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
// byte3 trailing-byte test
|| UNSAFE.getByte(address++) > (byte) 0xBF) {
|| UnsafeUtil.getByte(address++) > (byte) 0xBF) {
return MALFORMED;
}
} else {
@ -1164,7 +1149,7 @@ final class Utf8 {
int byte2 = (byte) ~(state >> 8);
int byte3 = 0;
if (byte2 == 0) {
byte2 = UNSAFE.getByte(address++);
byte2 = UnsafeUtil.getByte(address++);
if (address >= addressLimit) {
return incompleteStateFor(byte1, byte2);
}
@ -1172,7 +1157,7 @@ final class Utf8 {
byte3 = (byte) (state >> 16);
}
if (byte3 == 0) {
byte3 = UNSAFE.getByte(address++);
byte3 = UnsafeUtil.getByte(address++);
if (address >= addressLimit) {
return incompleteStateFor(byte1, byte2, byte3);
}
@ -1191,7 +1176,7 @@ final class Utf8 {
// byte3 trailing-byte test
|| byte3 > (byte) 0xBF
// byte4 trailing-byte test
|| UNSAFE.getByte(address++) > (byte) 0xBF) {
|| UnsafeUtil.getByte(address++) > (byte) 0xBF) {
return MALFORMED;
}
}
@ -1202,7 +1187,7 @@ final class Utf8 {
@Override
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 int inLimit = in.length();
if (inLimit > length || out.length - length < offset) {
@ -1215,25 +1200,25 @@ final class Utf8 {
// https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination
int inIx = 0;
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) {
// We're done, it was ASCII encoded.
return (int) (outIx - ARRAY_BASE_OFFSET);
return (int) (outIx - getArrayBaseOffset());
}
for (char c; inIx < inLimit; ++inIx) {
c = in.charAt(inIx);
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
UNSAFE.putByte(out, outIx++, (byte) ((0xF << 6) | (c >>> 6)));
UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & c)));
UnsafeUtil.putByte(out, outIx++, (byte) ((0xF << 6) | (c >>> 6)));
UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & c)));
} else if ((c < MIN_SURROGATE || MAX_SURROGATE < c) && outIx <= outLimit - 3L) {
// Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes
UNSAFE.putByte(out, outIx++, (byte) ((0xF << 5) | (c >>> 12)));
UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & (c >>> 6))));
UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & c)));
UnsafeUtil.putByte(out, outIx++, (byte) ((0xF << 5) | (c >>> 12)));
UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & (c >>> 6))));
UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & c)));
} else if (outIx <= outLimit - 4L) {
// Minimum code point represented by a surrogate pair is 0x10000, 17 bits, four UTF-8
// bytes
@ -1242,10 +1227,10 @@ final class Utf8 {
throw new UnpairedSurrogateException((inIx - 1), inLimit);
}
int codePoint = toCodePoint(c, low);
UNSAFE.putByte(out, outIx++, (byte) ((0xF << 4) | (codePoint >>> 18)));
UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 12))));
UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 6))));
UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & codePoint)));
UnsafeUtil.putByte(out, outIx++, (byte) ((0xF << 4) | (codePoint >>> 18)));
UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 12))));
UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 6))));
UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & codePoint)));
} else {
if ((MIN_SURROGATE <= c && c <= MAX_SURROGATE)
&& (inIx + 1 == inLimit || !isSurrogatePair(c, in.charAt(inIx + 1)))) {
@ -1258,7 +1243,7 @@ final class Utf8 {
}
// All bytes have been encoded.
return (int) (outIx - ARRAY_BASE_OFFSET);
return (int) (outIx - getArrayBaseOffset());
}
@Override
@ -1277,7 +1262,7 @@ final class Utf8 {
// https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination
int inIx = 0;
for (char c; inIx < inLimit && (c = in.charAt(inIx)) < 0x80; ++inIx) {
UNSAFE.putByte(outIx++, (byte) c);
UnsafeUtil.putByte(outIx++, (byte) c);
}
if (inIx == inLimit) {
// We're done, it was ASCII encoded.
@ -1288,15 +1273,15 @@ final class Utf8 {
for (char c; inIx < inLimit; ++inIx) {
c = in.charAt(inIx);
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
UNSAFE.putByte(outIx++, (byte) ((0xF << 6) | (c >>> 6)));
UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & c)));
UnsafeUtil.putByte(outIx++, (byte) ((0xF << 6) | (c >>> 6)));
UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & c)));
} else if ((c < MIN_SURROGATE || MAX_SURROGATE < c) && outIx <= outLimit - 3L) {
// Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes
UNSAFE.putByte(outIx++, (byte) ((0xF << 5) | (c >>> 12)));
UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & (c >>> 6))));
UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & c)));
UnsafeUtil.putByte(outIx++, (byte) ((0xF << 5) | (c >>> 12)));
UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & (c >>> 6))));
UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & c)));
} else if (outIx <= outLimit - 4L) {
// Minimum code point represented by a surrogate pair is 0x10000, 17 bits, four UTF-8
// bytes
@ -1305,10 +1290,10 @@ final class Utf8 {
throw new UnpairedSurrogateException((inIx - 1), inLimit);
}
int codePoint = toCodePoint(c, low);
UNSAFE.putByte(outIx++, (byte) ((0xF << 4) | (codePoint >>> 18)));
UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 12))));
UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 6))));
UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & codePoint)));
UnsafeUtil.putByte(outIx++, (byte) ((0xF << 4) | (codePoint >>> 18)));
UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 12))));
UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 6))));
UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & codePoint)));
} else {
if ((MIN_SURROGATE <= c && c <= MAX_SURROGATE)
&& (inIx + 1 == inLimit || !isSurrogatePair(c, in.charAt(inIx + 1)))) {
@ -1349,7 +1334,7 @@ final class Utf8 {
// we're 8-byte aligned.
final int unaligned = (int) offset & 7;
for (int j = unaligned; j > 0; j--) {
if (UNSAFE.getByte(bytes, offset++) < 0) {
if (UnsafeUtil.getByte(bytes, offset++) < 0) {
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
// determine if any byte in the current long is non-ASCII.
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) {}
return maxChars - remaining;
}
@ -1379,7 +1364,7 @@ final class Utf8 {
// be read before we're 8-byte aligned.
final int unaligned = (int) address & 7;
for (int j = unaligned; j > 0; j--) {
if (UNSAFE.getByte(address++) < 0) {
if (UnsafeUtil.getByte(address++) < 0) {
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
// determine if any byte in the current long is non-ASCII.
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) {}
return maxChars - remaining;
}
@ -1404,7 +1389,7 @@ final class Utf8 {
// 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?
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) {
return COMPLETE;
@ -1423,7 +1408,7 @@ final class Utf8 {
// Simultaneously checks for illegal trailing-byte in
// leading position and overlong 2-byte form.
if (byte1 < (byte) 0xC2
|| UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) {
|| UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
return MALFORMED;
}
} else if (byte1 < (byte) 0xF0) {
@ -1435,13 +1420,13 @@ final class Utf8 {
remaining -= 2;
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
|| (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0)
// check for illegal surrogate codepoints
|| (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
// byte3 trailing-byte test
|| UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) {
|| UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
return MALFORMED;
}
} else {
@ -1453,16 +1438,16 @@ final class Utf8 {
remaining -= 3;
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:
// if (byte1 > (byte) 0xF4 ||
// byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
// byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
|| (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0
// byte3 trailing-byte test
|| UNSAFE.getByte(bytes, offset++) > (byte) 0xBF
|| UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF
// byte4 trailing-byte test
|| UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) {
|| UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
return MALFORMED;
}
}
@ -1480,7 +1465,7 @@ final class Utf8 {
// 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?
int byte1 = 0;
for (; remaining > 0 && (byte1 = UNSAFE.getByte(address++)) >= 0; --remaining) {
for (; remaining > 0 && (byte1 = UnsafeUtil.getByte(address++)) >= 0; --remaining) {
}
if (remaining == 0) {
return COMPLETE;
@ -1498,7 +1483,7 @@ final class Utf8 {
// Simultaneously checks for illegal trailing-byte in
// 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;
}
} else if (byte1 < (byte) 0xF0) {
@ -1510,14 +1495,14 @@ final class Utf8 {
}
remaining -= 2;
final byte byte2 = UNSAFE.getByte(address++);
final byte byte2 = UnsafeUtil.getByte(address++);
if (byte2 > (byte) 0xBF
// overlong? 5 most significant bits must not all be zero
|| (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0)
// check for illegal surrogate codepoints
|| (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
// byte3 trailing-byte test
|| UNSAFE.getByte(address++) > (byte) 0xBF) {
|| UnsafeUtil.getByte(address++) > (byte) 0xBF) {
return MALFORMED;
}
} else {
@ -1529,7 +1514,7 @@ final class Utf8 {
}
remaining -= 3;
final byte byte2 = UNSAFE.getByte(address++);
final byte byte2 = UnsafeUtil.getByte(address++);
if (byte2 > (byte) 0xBF
// Check that 1 <= plane <= 16. Tricky optimized form of:
// if (byte1 > (byte) 0xF4 ||
@ -1537,9 +1522,9 @@ final class Utf8 {
// byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
|| (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0
// byte3 trailing-byte test
|| UNSAFE.getByte(address++) > (byte) 0xBF
|| UnsafeUtil.getByte(address++) > (byte) 0xBF
// byte4 trailing-byte test
|| UNSAFE.getByte(address++) > (byte) 0xBF) {
|| UnsafeUtil.getByte(address++) > (byte) 0xBF) {
return MALFORMED;
}
}
@ -1553,11 +1538,11 @@ final class Utf8 {
return incompleteStateFor(byte1);
}
case 1: {
return incompleteStateFor(byte1, UNSAFE.getByte(bytes, offset));
return incompleteStateFor(byte1, UnsafeUtil.getByte(bytes, offset));
}
case 2: {
return incompleteStateFor(byte1, UNSAFE.getByte(bytes, offset),
UNSAFE.getByte(bytes, offset + 1));
return incompleteStateFor(byte1, UnsafeUtil.getByte(bytes, offset),
UnsafeUtil.getByte(bytes, offset + 1));
}
default: {
throw new AssertionError();
@ -1571,112 +1556,17 @@ final class Utf8 {
return incompleteStateFor(byte1);
}
case 1: {
return incompleteStateFor(byte1, UNSAFE.getByte(address));
return incompleteStateFor(byte1, UnsafeUtil.getByte(address));
}
case 2: {
return incompleteStateFor(byte1, UNSAFE.getByte(address), UNSAFE.getByte(address + 1));
return incompleteStateFor(byte1, UnsafeUtil.getByte(address),
UnsafeUtil.getByte(address + 1));
}
default: {
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() {}

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

@ -382,6 +382,14 @@ public class DescriptorsTest extends TestCase {
assertEquals(Long.valueOf(8765432109L),
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 =
UnittestCustomOptions.TestMessageWithCustomOptions.AnEnum.getDescriptor();

@ -45,7 +45,8 @@ import java.util.Iterator;
*/
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 =
newImmutableDoubleArrayList(1, 2, 3);
@ -65,10 +66,10 @@ public class DoubleArrayListTest extends TestCase {
}
public void testMakeImmutable() {
list.addDouble(2);
list.addDouble(3);
list.addDouble(4);
list.addDouble(6);
list.addDouble(8);
list.addDouble(5);
list.addDouble(7);
list.makeImmutable();
assertImmutable(list);
}
@ -120,7 +121,7 @@ public class DoubleArrayListTest extends TestCase {
}
}
public void testGetInt() {
public void testGetDouble() {
assertEquals(1D, TERTIARY_LIST.getDouble(0));
assertEquals(2D, TERTIARY_LIST.getDouble(1));
assertEquals(3D, TERTIARY_LIST.getDouble(2));
@ -145,7 +146,7 @@ public class DoubleArrayListTest extends TestCase {
assertEquals(1, UNARY_LIST.size());
assertEquals(3, TERTIARY_LIST.size());
list.addDouble(2);
list.addDouble(3);
list.addDouble(4);
list.addDouble(6);
list.addDouble(8);
@ -154,7 +155,7 @@ public class DoubleArrayListTest extends TestCase {
list.remove(0);
assertEquals(3, list.size());
list.add(16D);
list.add(17D);
assertEquals(4, list.size());
}
@ -162,8 +163,8 @@ public class DoubleArrayListTest extends TestCase {
list.addDouble(2);
list.addDouble(4);
assertEquals(2D, (double) list.set(0, 0D));
assertEquals(0D, list.getDouble(0));
assertEquals(2D, (double) list.set(0, 3D));
assertEquals(3D, list.getDouble(0));
assertEquals(4D, (double) list.set(1, 0D));
assertEquals(0D, list.getDouble(1));
@ -183,14 +184,14 @@ public class DoubleArrayListTest extends TestCase {
}
}
public void testSetInt() {
list.addDouble(2);
list.addDouble(4);
public void testSetDouble() {
list.addDouble(1);
list.addDouble(3);
assertEquals(2D, list.setDouble(0, 0));
assertEquals(1D, list.setDouble(0, 0));
assertEquals(0D, list.getDouble(0));
assertEquals(4D, list.setDouble(1, 0));
assertEquals(3D, list.setDouble(1, 0));
assertEquals(0D, list.getDouble(1));
try {
@ -224,7 +225,9 @@ public class DoubleArrayListTest extends TestCase {
for (int i = 0; i < 6; 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 {
list.add(-1, 5D);
@ -239,7 +242,7 @@ public class DoubleArrayListTest extends TestCase {
}
}
public void testAddInt() {
public void testAddDouble() {
assertEquals(0, list.size());
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));
}
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() {
// Optional non-message fields set to their default value are treated the
// same way as not set.

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

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

@ -251,6 +251,23 @@ public class LazyMessageLiteTest extends TestCase {
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 {
LazyNestedInnerMessageLite nested = LazyNestedInnerMessageLite.newBuilder()
.setNum(3)

@ -1643,6 +1643,18 @@ public class LiteTest extends TestCase {
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 {
TestAllTypesLite one = TestUtilLite.getAllLiteSetBuilder().build();
byte[] bytes = one.toByteArray();
@ -2203,6 +2215,21 @@ public class LiteTest extends TestCase {
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.
public void testEquals_oneOfMessages() {
TestAllTypesLite mine = TestAllTypesLite.newBuilder()

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

@ -30,12 +30,16 @@
package com.google.protobuf;
import map_lite_test.MapForProto2TestProto.BizarroTestMap;
import map_lite_test.MapForProto2TestProto.TestMap;
import map_lite_test.MapForProto2TestProto.TestMap.MessageValue;
import map_lite_test.MapForProto2TestProto.TestMapOrBuilder;
import map_lite_test.MapForProto2TestProto.TestUnknownEnumValue;
import junit.framework.TestCase;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@ -44,34 +48,40 @@ import java.util.Map;
/**
* Unit tests for map fields.
*/
public class MapForProto2LiteTest extends TestCase {
private void setMapValues(TestMap.Builder builder) {
builder.getMutableInt32ToInt32Field().put(1, 11);
builder.getMutableInt32ToInt32Field().put(2, 22);
builder.getMutableInt32ToInt32Field().put(3, 33);
builder.getMutableInt32ToStringField().put(1, "11");
builder.getMutableInt32ToStringField().put(2, "22");
builder.getMutableInt32ToStringField().put(3, "33");
builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("11"));
builder.getMutableInt32ToBytesField().put(2, TestUtil.toBytes("22"));
builder.getMutableInt32ToBytesField().put(3, TestUtil.toBytes("33"));
builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.FOO);
builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.BAR);
builder.getMutableInt32ToEnumField().put(3, TestMap.EnumValue.BAZ);
public final class MapForProto2LiteTest extends TestCase {
builder.getMutableInt32ToMessageField().put(
1, MessageValue.newBuilder().setValue(11).build());
builder.getMutableInt32ToMessageField().put(
2, MessageValue.newBuilder().setValue(22).build());
builder.getMutableInt32ToMessageField().put(
3, MessageValue.newBuilder().setValue(33).build());
private void setMapValues(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);
}
builder.getMutableStringToInt32Field().put("1", 11);
builder.getMutableStringToInt32Field().put("2", 22);
builder.getMutableStringToInt32Field().put("3", 33);
public void testSetMapValues() {
TestMap.Builder mapBuilder = TestMap.newBuilder();
setMapValues(mapBuilder);
TestMap map = mapBuilder.build();
assertMapValuesSet(map);
}
private void copyMapValues(TestMap source, TestMap.Builder destination) {
@ -117,31 +127,42 @@ public class MapForProto2LiteTest extends TestCase {
}
private void updateMapValues(TestMap.Builder builder) {
builder.getMutableInt32ToInt32Field().put(1, 111);
builder.getMutableInt32ToInt32Field().remove(2);
builder.getMutableInt32ToInt32Field().put(4, 44);
builder.getMutableInt32ToStringField().put(1, "111");
builder.getMutableInt32ToStringField().remove(2);
builder.getMutableInt32ToStringField().put(4, "44");
builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("111"));
builder.getMutableInt32ToBytesField().remove(2);
builder.getMutableInt32ToBytesField().put(4, TestUtil.toBytes("44"));
builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.BAR);
builder.getMutableInt32ToEnumField().remove(2);
builder.getMutableInt32ToEnumField().put(4, TestMap.EnumValue.QUX);
builder
.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);
}
builder.getMutableInt32ToMessageField().put(
1, MessageValue.newBuilder().setValue(111).build());
builder.getMutableInt32ToMessageField().remove(2);
builder.getMutableInt32ToMessageField().put(
4, MessageValue.newBuilder().setValue(44).build());
public void testUpdateMapValues() {
TestMap.Builder mapBuilder = TestMap.newBuilder();
setMapValues(mapBuilder);
TestMap map = mapBuilder.build();
assertMapValuesSet(map);
builder.getMutableStringToInt32Field().put("1", 111);
builder.getMutableStringToInt32Field().remove("2");
builder.getMutableStringToInt32Field().put("4", 44);
mapBuilder = map.toBuilder();
updateMapValues(mapBuilder);
map = mapBuilder.build();
assertMapValuesUpdated(map);
}
private void assertMapValuesUpdated(TestMap message) {
@ -176,13 +197,19 @@ public class MapForProto2LiteTest extends TestCase {
assertEquals(44, message.getStringToInt32Field().get("4").intValue());
}
private void assertMapValuesCleared(TestMap message) {
assertEquals(0, message.getInt32ToInt32Field().size());
assertEquals(0, message.getInt32ToStringField().size());
assertEquals(0, message.getInt32ToBytesField().size());
assertEquals(0, message.getInt32ToEnumField().size());
assertEquals(0, message.getInt32ToMessageField().size());
assertEquals(0, message.getStringToInt32Field().size());
private void assertMapValuesCleared(TestMapOrBuilder testMapOrBuilder) {
assertEquals(0, testMapOrBuilder.getInt32ToInt32Field().size());
assertEquals(0, testMapOrBuilder.getInt32ToInt32FieldCount());
assertEquals(0, testMapOrBuilder.getInt32ToStringField().size());
assertEquals(0, testMapOrBuilder.getInt32ToStringFieldCount());
assertEquals(0, testMapOrBuilder.getInt32ToBytesField().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 {
@ -192,133 +219,87 @@ public class MapForProto2LiteTest extends TestCase {
TestMap.Builder builder = TestMap.newBuilder();
TestMap message = builder.build();
Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field();
intMap.put(1, 2);
builder.putInt32ToInt32Field(1, 2);
assertTrue(message.getInt32ToInt32Field().isEmpty());
message = builder.build();
try {
intMap.put(2, 3);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
assertEquals(newMap(1, 2), message.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, 2, 3), builder.getInt32ToInt32Field());
}
public void testMutableMapLifecycle() {
public void testGetMapIsImmutable() {
TestMap.Builder builder = TestMap.newBuilder();
Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field();
intMap.put(1, 2);
assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
assertMapsAreImmutable(builder);
assertMapsAreImmutable(builder.build());
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 {
intMap.put(2, 3);
map.put(key, value);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
assertEquals(newMap(1, 2), builder.getInt32ToInt32Field());
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());
if (!map.isEmpty()) {
try {
enumMap.put(2, TestMap.EnumValue.FOO);
map.entrySet().remove(map.entrySet().iterator().next());
fail();
} catch (UnsupportedOperationException e) {
// 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());
builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.FOO);
builder.putInt32ToEnumField(2, TestMap.EnumValue.FOO);
assertEquals(
newMap(1, TestMap.EnumValue.BAR, 2, TestMap.EnumValue.FOO),
builder.getInt32ToEnumField());
Map<Integer, String> stringMap = builder.getMutableInt32ToStringField();
stringMap.put(1, "1");
builder.putInt32ToStringField(1, "1");
assertEquals(newMap(1, "1"), builder.build().getInt32ToStringField());
try {
stringMap.put(2, "2");
fail();
} catch (UnsupportedOperationException e) {
// expected
}
assertEquals(newMap(1, "1"), builder.getInt32ToStringField());
builder.getMutableInt32ToStringField().put(2, "2");
assertEquals(
newMap(1, "1", 2, "2"),
builder.getInt32ToStringField());
builder.putInt32ToStringField(2, "2");
assertEquals(newMap(1, "1", 2, "2"), builder.getInt32ToStringField());
Map<Integer, TestMap.MessageValue> messageMap = builder.getMutableInt32ToMessageField();
messageMap.put(1, TestMap.MessageValue.getDefaultInstance());
builder.putInt32ToMessageField(1, TestMap.MessageValue.getDefaultInstance());
assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()),
builder.build().getInt32ToMessageField());
try {
messageMap.put(2, TestMap.MessageValue.getDefaultInstance());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()),
builder.getInt32ToMessageField());
builder.getMutableInt32ToMessageField().put(2, TestMap.MessageValue.getDefaultInstance());
builder.putInt32ToMessageField(2, TestMap.MessageValue.getDefaultInstance());
assertEquals(
newMap(1, TestMap.MessageValue.getDefaultInstance(),
2, TestMap.MessageValue.getDefaultInstance()),
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 {
TestMap.Builder builder = TestMap.newBuilder();
TestMap message = builder.build();
@ -336,6 +317,7 @@ public class MapForProto2LiteTest extends TestCase {
builder = message.toBuilder();
builder.clear();
assertMapValuesCleared(builder);
message = builder.build();
assertMapValuesCleared(message);
}
@ -344,12 +326,52 @@ public class MapForProto2LiteTest extends TestCase {
TestMap.Builder sourceBuilder = TestMap.newBuilder();
setMapValues(sourceBuilder);
TestMap source = sourceBuilder.build();
assertMapValuesSet(source);
TestMap.Builder destination = TestMap.newBuilder();
copyMapValues(source, destination);
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 {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
@ -373,6 +395,55 @@ public class MapForProto2LiteTest extends TestCase {
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 {
TestMap.Builder builder = TestMap.newBuilder();
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
// here is to add elements in different order.
TestMap.Builder b1 = TestMap.newBuilder();
b1.getMutableInt32ToInt32Field().put(1, 2);
b1.getMutableInt32ToInt32Field().put(3, 4);
b1.getMutableInt32ToInt32Field().put(5, 6);
TestMap.Builder b1 = TestMap.newBuilder()
.putInt32ToInt32Field(1, 2)
.putInt32ToInt32Field(3, 4)
.putInt32ToInt32Field(5, 6);
TestMap m1 = b1.build();
TestMap.Builder b2 = TestMap.newBuilder();
b2.getMutableInt32ToInt32Field().put(5, 6);
b2.getMutableInt32ToInt32Field().put(1, 2);
b2.getMutableInt32ToInt32Field().put(3, 4);
TestMap.Builder b2 = TestMap.newBuilder()
.putInt32ToInt32Field(5, 6)
.putInt32ToInt32Field(1, 2)
.putInt32ToInt32Field(3, 4);
TestMap m2 = b2.build();
assertEquals(m1, m2);
assertEquals(m1.hashCode(), m2.hashCode());
// Make sure we did compare map fields.
b2.getMutableInt32ToInt32Field().put(1, 0);
b2.putInt32ToInt32Field(1, 0);
m2 = b2.build();
assertFalse(m1.equals(m2));
// 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 {
TestUnknownEnumValue.Builder builder =
TestUnknownEnumValue.newBuilder();
builder.getMutableInt32ToInt32Field().put(1, 1);
builder.getMutableInt32ToInt32Field().put(2, 54321);
TestUnknownEnumValue.Builder builder = TestUnknownEnumValue.newBuilder()
.putInt32ToInt32Field(1, 1)
.putInt32ToInt32Field(2, 54321);
ByteString data = builder.build().toByteString();
TestMap message = TestMap.parseFrom(data);
@ -455,4 +525,275 @@ public class MapForProto2LiteTest extends TestCase {
map.put(key2, value2);
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;
import com.google.protobuf.Descriptors.FieldDescriptor;
import map_test.MapForProto2TestProto.BizarroTestMap;
import map_test.MapForProto2TestProto.TestMap;
import map_test.MapForProto2TestProto.TestMap.MessageValue;
import map_test.MapForProto2TestProto.TestMap.MessageWithRequiredFields;
import map_test.MapForProto2TestProto.TestMapOrBuilder;
import map_test.MapForProto2TestProto.TestRecursiveMap;
import map_test.MapForProto2TestProto.TestUnknownEnumValue;
import junit.framework.TestCase;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@ -48,7 +52,8 @@ import java.util.Map;
* Unit tests for map fields in proto2 protos.
*/
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(2, 22);
builder.getMutableInt32ToInt32Field().put(3, 33);
@ -77,6 +82,46 @@ public class MapForProto2Test extends TestCase {
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) {
destination
.putAllInt32ToInt32Field(source.getInt32ToInt32Field())
@ -87,7 +132,7 @@ public class MapForProto2Test extends TestCase {
.putAllStringToInt32Field(source.getStringToInt32Field());
}
private void assertMapValuesSet(TestMap message) {
private void assertMapValuesSet(TestMapOrBuilder message) {
assertEquals(3, message.getInt32ToInt32Field().size());
assertEquals(11, message.getInt32ToInt32Field().get(1).intValue());
assertEquals(22, message.getInt32ToInt32Field().get(2).intValue());
@ -119,7 +164,7 @@ public class MapForProto2Test extends TestCase {
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().remove(2);
builder.getMutableInt32ToInt32Field().put(4, 44);
@ -147,6 +192,58 @@ public class MapForProto2Test extends TestCase {
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) {
assertEquals(3, message.getInt32ToInt32Field().size());
assertEquals(111, message.getInt32ToInt32Field().get(1).intValue());
@ -179,13 +276,48 @@ public class MapForProto2Test extends TestCase {
assertEquals(44, message.getStringToInt32Field().get("4").intValue());
}
private void assertMapValuesCleared(TestMap message) {
assertEquals(0, message.getInt32ToInt32Field().size());
assertEquals(0, message.getInt32ToStringField().size());
assertEquals(0, message.getInt32ToBytesField().size());
assertEquals(0, message.getInt32ToEnumField().size());
assertEquals(0, message.getInt32ToMessageField().size());
assertEquals(0, message.getStringToInt32Field().size());
private void assertMapValuesCleared(TestMapOrBuilder testMapOrBuilder) {
assertEquals(0, testMapOrBuilder.getInt32ToInt32Field().size());
assertEquals(0, testMapOrBuilder.getInt32ToInt32FieldCount());
assertEquals(0, testMapOrBuilder.getInt32ToStringField().size());
assertEquals(0, testMapOrBuilder.getInt32ToStringFieldCount());
assertEquals(0, testMapOrBuilder.getInt32ToBytesField().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() {
@ -304,41 +436,84 @@ public class MapForProto2Test extends TestCase {
assertMapValuesCleared(message);
builder = message.toBuilder();
setMapValues(builder);
setMapValuesUsingMutableMap(builder);
message = builder.build();
assertMapValuesSet(message);
builder = message.toBuilder();
updateMapValues(builder);
updateMapValuesUsingMutableMap(builder);
message = builder.build();
assertMapValuesUpdated(message);
builder = message.toBuilder();
builder.clear();
assertMapValuesCleared(builder);
message = builder.build();
assertMapValuesCleared(message);
}
public void testPutAll() throws Exception {
TestMap.Builder sourceBuilder = TestMap.newBuilder();
setMapValues(sourceBuilder);
setMapValuesUsingMutableMap(sourceBuilder);
TestMap source = sourceBuilder.build();
assertMapValuesSet(source);
TestMap.Builder destination = TestMap.newBuilder();
copyMapValues(source, destination);
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 {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
setMapValuesUsingMutableMap(builder);
TestMap message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesSet(message);
builder = message.toBuilder();
updateMapValues(builder);
updateMapValuesUsingMutableMap(builder);
message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.parser().parseFrom(message.toByteString());
@ -352,9 +527,58 @@ public class MapForProto2Test extends TestCase {
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 {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
setMapValuesUsingMutableMap(builder);
TestMap message = builder.build();
TestMap.Builder other = TestMap.newBuilder();
@ -449,8 +673,7 @@ public class MapForProto2Test extends TestCase {
builder.setField(field, entryList);
}
private static <KeyType, ValueType>
Map<KeyType, ValueType> mapForValues(
private static <KeyType, ValueType> Map<KeyType, ValueType> mapForValues(
KeyType key1, ValueType value1, KeyType key2, ValueType value2) {
Map<KeyType, ValueType> map = new HashMap<KeyType, ValueType>();
map.put(key1, value1);
@ -536,7 +759,7 @@ public class MapForProto2Test extends TestCase {
public void testTextFormat() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
setMapValuesUsingMutableMap(builder);
TestMap message = builder.build();
String textData = TextFormat.printToString(message);
@ -550,7 +773,7 @@ public class MapForProto2Test extends TestCase {
public void testDynamicMessage() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
setMapValuesUsingMutableMap(builder);
TestMap message = builder.build();
Message dynamicDefaultInstance =
@ -646,13 +869,266 @@ public class MapForProto2Test extends TestCase {
public void testIterationOrder() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
setMapValuesUsingMutableMap(builder);
TestMap message = builder.build();
assertEquals(Arrays.asList("1", "2", "3"),
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
public void testMapInitializationOrder() throws Exception {
assertEquals("RedactAllTypes", map_test.RedactAllTypes
@ -679,5 +1155,23 @@ public class MapForProto2Test extends TestCase {
map.put(key2, value2);
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;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.EnumDescriptor;
import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import map_test.MapTestProto.BizarroTestMap;
import map_test.MapTestProto.TestMap;
import map_test.MapTestProto.TestMap.MessageValue;
import map_test.MapTestProto.TestMapOrBuilder;
import map_test.MapTestProto.TestOnChangeEventPropagation;
import junit.framework.TestCase;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@ -49,7 +54,8 @@ import java.util.Map;
* Unit tests for map fields.
*/
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(2, 22);
builder.getMutableInt32ToInt32Field().put(3, 33);
@ -78,6 +84,46 @@ public class MapTest extends TestCase {
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) {
destination
.putAllInt32ToInt32Field(source.getInt32ToInt32Field())
@ -120,7 +166,7 @@ public class MapTest extends TestCase {
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().remove(2);
builder.getMutableInt32ToInt32Field().put(4, 44);
@ -148,6 +194,58 @@ public class MapTest extends TestCase {
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) {
assertEquals(3, message.getInt32ToInt32Field().size());
assertEquals(111, message.getInt32ToInt32Field().get(1).intValue());
@ -180,13 +278,48 @@ public class MapTest extends TestCase {
assertEquals(44, message.getStringToInt32Field().get("4").intValue());
}
private void assertMapValuesCleared(TestMap message) {
assertEquals(0, message.getInt32ToInt32Field().size());
assertEquals(0, message.getInt32ToStringField().size());
assertEquals(0, message.getInt32ToBytesField().size());
assertEquals(0, message.getInt32ToEnumField().size());
assertEquals(0, message.getInt32ToMessageField().size());
assertEquals(0, message.getStringToInt32Field().size());
private void assertMapValuesCleared(TestMapOrBuilder testMapOrBuilder) {
assertEquals(0, testMapOrBuilder.getInt32ToInt32Field().size());
assertEquals(0, testMapOrBuilder.getInt32ToInt32FieldCount());
assertEquals(0, testMapOrBuilder.getInt32ToStringField().size());
assertEquals(0, testMapOrBuilder.getInt32ToStringFieldCount());
assertEquals(0, testMapOrBuilder.getInt32ToBytesField().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() {
@ -305,25 +438,27 @@ public class MapTest extends TestCase {
assertMapValuesCleared(message);
builder = message.toBuilder();
setMapValues(builder);
setMapValuesUsingMutableMap(builder);
message = builder.build();
assertMapValuesSet(message);
builder = message.toBuilder();
updateMapValues(builder);
updateMapValuesUsingMutableMap(builder);
message = builder.build();
assertMapValuesUpdated(message);
builder = message.toBuilder();
builder.clear();
assertMapValuesCleared(builder);
message = builder.build();
assertMapValuesCleared(message);
}
public void testPutAll() throws Exception {
TestMap.Builder sourceBuilder = TestMap.newBuilder();
setMapValues(sourceBuilder);
setMapValuesUsingMutableMap(sourceBuilder);
TestMap source = sourceBuilder.build();
assertMapValuesSet(source);
TestMap.Builder destination = TestMap.newBuilder();
copyMapValues(source, destination);
@ -344,18 +479,76 @@ public class MapTest extends TestCase {
assertEquals(0, destination.getInt32ToEnumFieldValue().get(0).intValue());
assertEquals(1, destination.getInt32ToEnumFieldValue().get(1).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 {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
setMapValuesUsingMutableMap(builder);
TestMap message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesSet(message);
builder = message.toBuilder();
updateMapValues(builder);
updateMapValuesUsingMutableMap(builder);
message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.parser().parseFrom(message.toByteString());
@ -369,9 +562,58 @@ public class MapTest extends TestCase {
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 {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
setMapValuesUsingMutableMap(builder);
TestMap message = builder.build();
TestMap.Builder other = TestMap.newBuilder();
@ -629,7 +871,7 @@ public class MapTest extends TestCase {
public void testTextFormat() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
setMapValuesUsingMutableMap(builder);
TestMap message = builder.build();
String textData = TextFormat.printToString(message);
@ -643,7 +885,7 @@ public class MapTest extends TestCase {
public void testDynamicMessage() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
setMapValuesUsingMutableMap(builder);
TestMap message = builder.build();
Message dynamicDefaultInstance =
@ -760,13 +1002,311 @@ public class MapTest extends TestCase {
public void testIterationOrder() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
setMapValuesUsingMutableMap(builder);
TestMap message = builder.build();
assertEquals(Arrays.asList("1", "2", "3"),
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) {
Map<K, V> map = new HashMap<K, V>();
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() {
// 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 {
ancestor = ancestor.getCanonicalFile();
} catch (IOException e) {
@ -3781,7 +3782,7 @@ public final class TestUtil {
throw new RuntimeException(
"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 " +
"C++ source tree.");
"C++ source tree: " + initialPath);
}
/**

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

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

@ -70,6 +70,17 @@ message TestRecursiveMap {
optional int32 value = 1;
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;
option java_package = "map_lite_test";
option optimize_for = LITE_RUNTIME;

@ -72,3 +72,14 @@ message TestRecursiveMap {
optional int32 value = 1;
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;
}
// 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
// is made to a map field.
message TestOnChangeEventPropagation {
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.List;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;
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
* FieldMask in a message tree.
*/
class FieldMaskTree {
final class FieldMaskTree {
private static final Logger logger = Logger.getLogger(FieldMaskTree.class.getName());
private static final String FIELD_PATH_SEPARATOR_REGEX = "\\.";
private static class Node {
public TreeMap<String, Node> children = new TreeMap<String, Node>();
private static final class Node {
final SortedMap<String, Node> children = new TreeMap<String, 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);
}
@ -93,7 +98,7 @@ class FieldMaskTree {
* Likewise, if the field path to add is a sub-path of an existing leaf node,
* nothing will be changed in the tree.
*/
public FieldMaskTree addFieldPath(String path) {
FieldMaskTree addFieldPath(String path) {
String[] parts = path.split(FIELD_PATH_SEPARATOR_REGEX);
if (parts.length == 0) {
return this;
@ -124,15 +129,17 @@ class FieldMaskTree {
/**
* Merges all field paths in a FieldMask into this tree.
*/
public FieldMaskTree mergeFromFieldMask(FieldMask mask) {
FieldMaskTree mergeFromFieldMask(FieldMask mask) {
for (String path : mask.getPathsList()) {
addFieldPath(path);
}
return this;
}
/** Converts this tree to a FieldMask. */
public FieldMask toFieldMask() {
/**
* Converts this tree to a FieldMask.
*/
FieldMask toFieldMask() {
if (root.children.isEmpty()) {
return FieldMask.getDefaultInstance();
}
@ -141,7 +148,9 @@ class FieldMaskTree {
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) {
if (node.children.isEmpty()) {
paths.add(path);
@ -154,10 +163,9 @@ class FieldMaskTree {
}
/**
* Adds the intersection of this tree with the given {@code path} to
* {@code output}.
* Adds the intersection of this tree with the given {@code path} to {@code output}.
*/
public void intersectFieldPath(String path, FieldMaskTree output) {
void intersectFieldPath(String path, FieldMaskTree output) {
if (root.children.isEmpty()) {
return;
}
@ -188,11 +196,9 @@ class FieldMaskTree {
}
/**
* Merges all fields specified by this FieldMaskTree from {@code source} to
* {@code destination}.
* Merges all fields specified by this FieldMaskTree from {@code source} to {@code destination}.
*/
public void merge(
Message source, Message.Builder destination, FieldMaskUtil.MergeOptions options) {
void merge(Message source, Message.Builder destination, FieldMaskUtil.MergeOptions options) {
if (source.getDescriptorForType() != destination.getDescriptorForType()) {
throw new IllegalArgumentException("Cannot merge messages of different types.");
}
@ -202,8 +208,8 @@ class FieldMaskTree {
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(
Node node,

@ -32,6 +32,9 @@ package com.google.protobuf.util;
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.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
@ -39,7 +42,9 @@ import com.google.protobuf.FieldMask;
import com.google.protobuf.Internal;
import com.google.protobuf.Message;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Utility helper functions to work with {@link com.google.protobuf.FieldMask}.
@ -78,8 +83,7 @@ public class FieldMaskUtil {
*/
public static FieldMask fromString(String value) {
// TODO(xiaofeng): Consider using com.google.common.base.Splitter here instead.
return fromStringList(
null, Arrays.asList(value.split(FIELD_PATH_SEPARATOR_REGEX)));
return fromStringList(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) {
// TODO(xiaofeng): Consider using com.google.common.base.Splitter here instead.
return fromStringList(
type, Arrays.asList(value.split(FIELD_PATH_SEPARATOR_REGEX)));
return fromStringList(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.
*/
// TODO(xiaofeng): Consider renaming fromStrings()
public static FieldMask fromStringList(
Class<? extends Message> type, Iterable<String> paths) {
public static FieldMask fromStringList(Class<? extends Message> type, Iterable<String> paths) {
FieldMask.Builder builder = FieldMask.newBuilder();
for (String path : paths) {
if (path.isEmpty()) {
@ -108,8 +110,7 @@ public class FieldMaskUtil {
continue;
}
if (type != null && !isValid(type, path)) {
throw new IllegalArgumentException(
path + " is not a valid path for " + type);
throw new IllegalArgumentException(path + " is not a valid path for " + type);
}
builder.addPaths(path);
}
@ -145,12 +146,42 @@ public class FieldMaskUtil {
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.
*/
public static boolean isValid(Class<? extends Message> type, FieldMask fieldMask) {
Descriptor descriptor =
Internal.getDefaultInstance(type).getDescriptorForType();
Descriptor descriptor = Internal.getDefaultInstance(type).getDescriptorForType();
return isValid(descriptor, fieldMask);
}
@ -171,8 +202,7 @@ public class FieldMaskUtil {
* Checks whether a given field path is valid.
*/
public static boolean isValid(Class<? extends Message> type, String path) {
Descriptor descriptor =
Internal.getDefaultInstance(type).getDescriptorForType();
Descriptor descriptor = Internal.getDefaultInstance(type).getDescriptorForType();
return isValid(descriptor, path);
}
@ -193,8 +223,7 @@ public class FieldMaskUtil {
if (field == null) {
return false;
}
if (!field.isRepeated()
&& field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
if (!field.isRepeated() && field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
descriptor = field.getMessageType();
} else {
descriptor = null;
@ -299,16 +328,15 @@ public class FieldMaskUtil {
* Merges fields specified by a FieldMask from one message to another with the
* specified merge options.
*/
public static void merge(FieldMask mask, Message source,
Message.Builder destination, MergeOptions options) {
public static void merge(
FieldMask mask, Message source, Message.Builder destination, MergeOptions options) {
new FieldMaskTree(mask).merge(source, destination, options);
}
/**
* Merges fields specified by a FieldMask from one message to another.
*/
public static void merge(FieldMask mask, Message source,
Message.Builder destination) {
public static void merge(FieldMask mask, Message source, Message.Builder destination) {
merge(mask, source, destination, new MergeOptions());
}
}

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

@ -35,15 +35,14 @@ import com.google.protobuf.Timestamp;
import java.math.BigInteger;
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
*
* @deprecated Use {@link Durations} and {@link Timestamps} instead.
*/
public class TimeUtil {
@Deprecated
public final class TimeUtil {
// Timestamp for "0001-01-01T00:00:00Z"
public static final long TIMESTAMP_SECONDS_MIN = -62135596800L;
@ -53,28 +52,6 @@ public class TimeUtil {
public static final long DURATION_SECONDS_MAX = 315576000000L;
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() {}
@ -90,27 +67,11 @@ public class TimeUtil {
* @return The string representation of the given timestamp.
* @throws IllegalArgumentException if the given timestamp is not in the
* valid range.
* @deprecated Use {@link Timestamps#toString} instead.
*/
public static String toString(Timestamp timestamp)
throws IllegalArgumentException {
StringBuilder result = new StringBuilder();
// 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();
@Deprecated
public static String toString(Timestamp timestamp) {
return Timestamps.toString(timestamp);
}
/**
@ -123,59 +84,11 @@ public class TimeUtil {
*
* @return A Timestamp parsed from the string.
* @throws ParseException if parsing fails.
* @deprecated Use {@link Timestamps#parse} instead.
*/
@Deprecated
public static Timestamp parseTimestamp(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);
}
return Timestamps.parse(value);
}
/**
@ -188,33 +101,11 @@ public class TimeUtil {
* @return The string representation of the given duration.
* @throws IllegalArgumentException if the given duration is not in the valid
* range.
* @deprecated Use {@link Durations#toString} instead.
*/
public static String toString(Duration duration)
throws IllegalArgumentException {
if (duration.getSeconds() < DURATION_SECONDS_MIN
|| 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();
@Deprecated
public static String toString(Duration duration) {
return Durations.toString(duration);
}
/**
@ -222,54 +113,31 @@ public class TimeUtil {
*
* @return A Duration parsed from the string.
* @throws ParseException if parsing fails.
* @deprecated Use {@link Durations#parse} instead.
*/
@Deprecated
public static Duration parseDuration(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 : 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);
}
return Durations.parse(value);
}
/**
* 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) {
return normalizedTimestamp(milliseconds / MILLIS_PER_SECOND,
(int) (milliseconds % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND));
return Timestamps.fromMillis(milliseconds);
}
/**
* Create a Duration from the number of milliseconds.
*
* @deprecated Use {@link Durations#fromMillis} instead.
*/
@Deprecated
public static Duration createDurationFromMillis(long milliseconds) {
return normalizedDuration(milliseconds / MILLIS_PER_SECOND,
(int) (milliseconds % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND));
return Durations.fromMillis(milliseconds);
}
/**
@ -278,36 +146,44 @@ public class TimeUtil {
* <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.
*
* @deprecated Use {@link Timestamps#toMillis} instead.
*/
@Deprecated
public static long toMillis(Timestamp timestamp) {
return timestamp.getSeconds() * MILLIS_PER_SECOND + timestamp.getNanos()
/ NANOS_PER_MILLISECOND;
return Timestamps.toMillis(timestamp);
}
/**
* 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.
*
* @deprecated Use {@link Durations#toMillis} instead.
*/
@Deprecated
public static long toMillis(Duration duration) {
return duration.getSeconds() * MILLIS_PER_SECOND + duration.getNanos()
/ NANOS_PER_MILLISECOND;
return Durations.toMillis(duration);
}
/**
* 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) {
return normalizedTimestamp(microseconds / MICROS_PER_SECOND,
(int) (microseconds % MICROS_PER_SECOND * NANOS_PER_MICROSECOND));
return Timestamps.fromMicros(microseconds);
}
/**
* Create a Duration from the number of microseconds.
*
* @deprecated Use {@link Durations#fromMicros} instead.
*/
@Deprecated
public static Duration createDurationFromMicros(long microseconds) {
return normalizedDuration(microseconds / MICROS_PER_SECOND,
(int) (microseconds % MICROS_PER_SECOND * NANOS_PER_MICROSECOND));
return Durations.fromMicros(microseconds);
}
/**
@ -316,111 +192,141 @@ public class TimeUtil {
* <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.
*
* @deprecated Use {@link Timestamps#toMicros} instead.
*/
@Deprecated
public static long toMicros(Timestamp timestamp) {
return timestamp.getSeconds() * MICROS_PER_SECOND + timestamp.getNanos()
/ NANOS_PER_MICROSECOND;
return Timestamps.toMicros(timestamp);
}
/**
* 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.
*
* @deprecated Use {@link Durations#toMicros} instead.
*/
@Deprecated
public static long toMicros(Duration duration) {
return duration.getSeconds() * MICROS_PER_SECOND + duration.getNanos()
/ NANOS_PER_MICROSECOND;
return Durations.toMicros(duration);
}
/**
* 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) {
return normalizedTimestamp(nanoseconds / NANOS_PER_SECOND,
(int) (nanoseconds % NANOS_PER_SECOND));
return Timestamps.fromNanos(nanoseconds);
}
/**
* Create a Duration from the number of nanoseconds.
*
* @deprecated Use {@link Durations#fromNanos} instead.
*/
@Deprecated
public static Duration createDurationFromNanos(long nanoseconds) {
return normalizedDuration(nanoseconds / NANOS_PER_SECOND,
(int) (nanoseconds % NANOS_PER_SECOND));
return Durations.fromNanos(nanoseconds);
}
/**
* 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) {
return timestamp.getSeconds() * NANOS_PER_SECOND + timestamp.getNanos();
return Timestamps.toNanos(timestamp);
}
/**
* Convert a Duration to the number of nanoseconds.
*
* @deprecated Use {@link Durations#toNanos} instead.
*/
@Deprecated
public static long toNanos(Duration duration) {
return duration.getSeconds() * NANOS_PER_SECOND + duration.getNanos();
return Durations.toNanos(duration);
}
/**
* Get the current time.
*
* @deprecated Use {@code Timestamps.fromMillis(System.currentTimeMillis())} instead.
*/
@Deprecated
public static Timestamp getCurrentTime() {
return createTimestampFromMillis(System.currentTimeMillis());
return Timestamps.fromMillis(System.currentTimeMillis());
}
/**
* Get the epoch.
*
* @deprecated Use {@code Timestamps.fromMillis(0)} instead.
*/
@Deprecated
public static Timestamp getEpoch() {
return Timestamp.getDefaultInstance();
}
/**
* Calculate the difference between two timestamps.
*
* @deprecated Use {@link Timestamps#between} instead.
*/
@Deprecated
public static Duration distance(Timestamp from, Timestamp to) {
return normalizedDuration(to.getSeconds() - from.getSeconds(),
to.getNanos() - from.getNanos());
return Timestamps.between(from, to);
}
/**
* Add a duration to a timestamp.
*
* @deprecated Use {@link Timestamps#add} instead.
*/
@Deprecated
public static Timestamp add(Timestamp start, Duration length) {
return normalizedTimestamp(start.getSeconds() + length.getSeconds(),
start.getNanos() + length.getNanos());
return Timestamps.add(start, length);
}
/**
* Subtract a duration from a timestamp.
*
* @deprecated Use {@link Timestamps#subtract} instead.
*/
@Deprecated
public static Timestamp subtract(Timestamp start, Duration length) {
return normalizedTimestamp(start.getSeconds() - length.getSeconds(),
start.getNanos() - length.getNanos());
return Timestamps.subtract(start, length);
}
/**
* Add two durations.
*
* @deprecated Use {@link Durations#add} instead.
*/
@Deprecated
public static Duration add(Duration d1, Duration d2) {
return normalizedDuration(d1.getSeconds() + d2.getSeconds(),
d1.getNanos() + d2.getNanos());
return Durations.add(d1, d2);
}
/**
* Subtract a duration from another.
*
* @deprecated Use {@link Durations#subtract} instead.
*/
@Deprecated
public static Duration subtract(Duration d1, Duration d2) {
return normalizedDuration(d1.getSeconds() - d2.getSeconds(),
d1.getNanos() - d2.getNanos());
return Durations.subtract(d1, d2);
}
// Multiplications and divisions.
// TODO(kak): Delete this.
public static Duration multiply(Duration duration, double times) {
double result = duration.getSeconds() * times + duration.getNanos() * times
/ 1000000000.0;
double result = duration.getSeconds() * times + duration.getNanos() * times / 1000000000.0;
if (result < Long.MIN_VALUE || result > Long.MAX_VALUE) {
throw new IllegalArgumentException("Result is out of valid range.");
}
@ -429,27 +335,29 @@ public class TimeUtil {
return normalizedDuration(seconds, nanos);
}
// TODO(kak): Delete this.
public static Duration divide(Duration duration, double value) {
return multiply(duration, 1.0 / value);
}
// TODO(kak): Delete this.
public static Duration multiply(Duration duration, long times) {
return createDurationFromBigInteger(
toBigInteger(duration).multiply(toBigInteger(times)));
return createDurationFromBigInteger(toBigInteger(duration).multiply(toBigInteger(times)));
}
// TODO(kak): Delete this.
public static Duration divide(Duration duration, long times) {
return createDurationFromBigInteger(
toBigInteger(duration).divide(toBigInteger(times)));
return createDurationFromBigInteger(toBigInteger(duration).divide(toBigInteger(times)));
}
// TODO(kak): Delete this.
public static long divide(Duration d1, Duration d2) {
return toBigInteger(d1).divide(toBigInteger(d2)).longValue();
}
// TODO(kak): Delete this.
public static Duration remainder(Duration d1, Duration d2) {
return createDurationFromBigInteger(
toBigInteger(d1).remainder(toBigInteger(d2)));
return createDurationFromBigInteger(toBigInteger(d1).remainder(toBigInteger(d2)));
}
private static final BigInteger NANOS_PER_SECOND_BIG_INTEGER =
@ -466,12 +374,9 @@ public class TimeUtil {
}
private static Duration createDurationFromBigInteger(BigInteger value) {
long seconds = value.divide(
new BigInteger(String.valueOf(NANOS_PER_SECOND))).longValue();
int nanos = value.remainder(
new BigInteger(String.valueOf(NANOS_PER_SECOND))).intValue();
long seconds = value.divide(new BigInteger(String.valueOf(NANOS_PER_SECOND))).longValue();
int nanos = value.remainder(new BigInteger(String.valueOf(NANOS_PER_SECOND))).intValue();
return normalizedDuration(seconds, nanos);
}
private static Duration normalizedDuration(long seconds, int nanos) {
@ -492,58 +397,4 @@ public class TimeUtil {
}
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 {
assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload"));
assertFalse(FieldMaskUtil.isValid(NestedTestAllTypes.class, "nonexist"));
assertTrue(FieldMaskUtil.isValid(
NestedTestAllTypes.class, "payload.optional_int32"));
assertTrue(FieldMaskUtil.isValid(
NestedTestAllTypes.class, "payload.repeated_int32"));
assertTrue(FieldMaskUtil.isValid(
NestedTestAllTypes.class, "payload.optional_nested_message"));
assertTrue(FieldMaskUtil.isValid(
NestedTestAllTypes.class, "payload.repeated_nested_message"));
assertFalse(FieldMaskUtil.isValid(
NestedTestAllTypes.class, "payload.nonexist"));
assertTrue(FieldMaskUtil.isValid(
NestedTestAllTypes.class, FieldMaskUtil.fromString("payload")));
assertFalse(FieldMaskUtil.isValid(
NestedTestAllTypes.class, FieldMaskUtil.fromString("nonexist")));
assertFalse(FieldMaskUtil.isValid(
assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.optional_int32"));
assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.repeated_int32"));
assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.optional_nested_message"));
assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.repeated_nested_message"));
assertFalse(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.nonexist"));
assertTrue(
FieldMaskUtil.isValid(NestedTestAllTypes.class, FieldMaskUtil.fromString("payload")));
assertFalse(
FieldMaskUtil.isValid(NestedTestAllTypes.class, FieldMaskUtil.fromString("nonexist")));
assertFalse(
FieldMaskUtil.isValid(
NestedTestAllTypes.class, FieldMaskUtil.fromString("payload,nonexist")));
assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.getDescriptor(), "payload"));
assertFalse(FieldMaskUtil.isValid(NestedTestAllTypes.getDescriptor(), "nonexist"));
assertTrue(FieldMaskUtil.isValid(
assertTrue(
FieldMaskUtil.isValid(
NestedTestAllTypes.getDescriptor(), FieldMaskUtil.fromString("payload")));
assertFalse(FieldMaskUtil.isValid(
assertFalse(
FieldMaskUtil.isValid(
NestedTestAllTypes.getDescriptor(), FieldMaskUtil.fromString("nonexist")));
assertTrue(FieldMaskUtil.isValid(
NestedTestAllTypes.class, "payload.optional_nested_message.bb"));
assertTrue(
FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.optional_nested_message.bb"));
// Repeated fields cannot have sub-paths.
assertFalse(FieldMaskUtil.isValid(
NestedTestAllTypes.class, "payload.repeated_nested_message.bb"));
assertFalse(
FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.repeated_nested_message.bb"));
// Non-message fields cannot have sub-paths.
assertFalse(FieldMaskUtil.isValid(
NestedTestAllTypes.class, "payload.optional_int32.bb"));
assertFalse(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.optional_int32.bb"));
}
public void testToString() throws Exception {
@ -85,8 +82,14 @@ public class FieldMaskUtilTest extends TestCase {
assertEquals("foo,bar", FieldMaskUtil.toString(mask));
// Empty field paths are ignored.
mask = FieldMask.newBuilder().addPaths("").addPaths("foo").addPaths("").
addPaths("bar").addPaths("").build();
mask =
FieldMask.newBuilder()
.addPaths("")
.addPaths("foo")
.addPaths("")
.addPaths("bar")
.addPaths("")
.build();
assertEquals("foo,bar", FieldMaskUtil.toString(mask));
}
@ -111,8 +114,7 @@ public class FieldMaskUtilTest extends TestCase {
mask = FieldMaskUtil.fromString(NestedTestAllTypes.class, ",payload");
try {
mask = FieldMaskUtil.fromString(
NestedTestAllTypes.class, "payload,nonexist");
mask = FieldMaskUtil.fromString(NestedTestAllTypes.class, "payload,nonexist");
fail("Exception is expected.");
} catch (IllegalArgumentException e) {
// 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 {
// Only test a simple case here and expect
// {@link FieldMaskTreeTest#testAddFieldPath} to cover all scenarios.
@ -174,7 +202,8 @@ public class FieldMaskUtilTest extends TestCase {
public void testMerge() throws Exception {
// Only test a simple case here and expect
// {@link FieldMaskTreeTest#testMerge} to cover all scenarios.
NestedTestAllTypes source = NestedTestAllTypes.newBuilder()
NestedTestAllTypes source =
NestedTestAllTypes.newBuilder()
.setPayload(TestAllTypes.newBuilder().setOptionalInt32(1234))
.build();
NestedTestAllTypes.Builder builder = NestedTestAllTypes.newBuilder();

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

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

@ -124,6 +124,7 @@ message TestOneof {
oneof oneof_field {
int32 oneof_int32 = 1;
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.
* @typedef {!function(!jspb.Message, !jspb.BinaryWriter):void |
* !function(!jspb.ConstBinaryMessage, !jspb.BinaryWriter):void}
* @typedef {function((!jspb.Message|!jspb.ConstBinaryMessage),
* !jspb.BinaryWriter):void}
*/
jspb.WriterFunction;

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

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

@ -717,11 +717,19 @@ jspb.BinaryWriter.prototype.writeBytes = function(field, value) {
/**
* Writes a message to the buffer.
* @template MessageType
* @param {number} field The field number.
* @param {?MessageType} value The message to write.
* @param {!jspb.WriterFunction} writerCallback Will be invoked with the value
* to write and the writer to write it with.
* @param {function(MessageTypeNonNull, !jspb.BinaryWriter)} writerCallback
* 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(
field, value, writerCallback) {
@ -735,12 +743,20 @@ jspb.BinaryWriter.prototype.writeMessage = function(
/**
* Writes a group message to the buffer.
*
* @template MessageType
* @param {number} field The field number.
* @param {?MessageType} value The message to write, wrapped with START_GROUP /
* END_GROUP tags. Will be a no-op if 'value' is null.
* @param {!jspb.WriterFunction} writerCallback Will be invoked with the value
* to write and the writer to write it with.
* @param {function(MessageTypeNonNull, !jspb.BinaryWriter)} writerCallback
* 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(
field, value, writerCallback) {
@ -1122,8 +1138,8 @@ jspb.BinaryWriter.prototype.writeRepeatedBytes = function(field, value) {
* @param {number} field The field number.
* @param {?Array.<MessageType>} value The array of messages to
* write.
* @param {!jspb.WriterFunction} writerCallback Will be invoked with the value
* to write and the writer to write it with.
* @param {function(MessageType, !jspb.BinaryWriter)} writerCallback
* Will be invoked with the value to write and the writer to write it with.
*/
jspb.BinaryWriter.prototype.writeRepeatedMessage = function(
field, value, writerCallback) {
@ -1142,8 +1158,8 @@ jspb.BinaryWriter.prototype.writeRepeatedMessage = function(
* @param {number} field The field number.
* @param {?Array.<MessageType>} value The array of messages to
* write.
* @param {!jspb.WriterFunction} writerCallback Will be invoked with the value
* to write and the writer to write it with.
* @param {function(MessageType, !jspb.BinaryWriter)} writerCallback
* Will be invoked with the value to write and the writer to write it with.
*/
jspb.BinaryWriter.prototype.writeRepeatedGroup = function(
field, value, writerCallback) {

@ -41,6 +41,7 @@ goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.crypt.base64');
goog.require('goog.json');
goog.require('jspb.Map');
// Not needed in compilation units that have no protos with xids.
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.
// 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.
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.extensionObject_ = obj;
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.
* @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.
* <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.
*/
jspb.Message.prototype.toArray = function() {
this.syncMapFields_();
return this.array;
};
@ -972,6 +1063,7 @@ jspb.Message.prototype.toArray = function() {
* @override
*/
jspb.Message.prototype.toString = function() {
this.syncMapFields_();
return this.array.toString();
};
@ -1293,6 +1385,9 @@ jspb.Message.clone_ = function(obj) {
}
return clonedArray;
}
if (jspb.Message.SUPPORTS_UINT8ARRAY_ && obj instanceof Uint8Array) {
return new Uint8Array(obj);
}
var clone = {};
for (var key in obj) {
if ((o = obj[key]) != null) {

@ -34,6 +34,7 @@ goog.setTestOnly();
goog.require('goog.json');
goog.require('goog.testing.asserts');
goog.require('goog.userAgent');
// CommonJS-LoadFromFile: google-protobuf jspb
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.SpecialCases');
goog.require('proto.jspb.test.TestClone');
goog.require('proto.jspb.test.TestEndsWithBytes');
goog.require('proto.jspb.test.TestGroup');
goog.require('proto.jspb.test.TestGroup1');
goog.require('proto.jspb.test.TestMessageWithOneof');
@ -438,6 +440,8 @@ describe('Message test suite', function() {
});
it('testClone', function() {
var supportsUint8Array =
!goog.userAgent.IE || goog.userAgent.isVersionOrHigher('10');
var original = new proto.jspb.test.TestClone();
original.setStr('v1');
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']]);
original.setSimple1(simple1);
original.setSimple2List([simple2, simple3]);
var bytes1 = supportsUint8Array ? new Uint8Array([1, 2, 3]) : '123';
original.setBytesField(bytes1);
var extension = new proto.jspb.test.CloneExtension();
extension.setExt('e1');
original.setExtension(proto.jspb.test.IsExtension.extField, extension);
var clone = original.cloneMessage();
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.setStr('v2');
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']]);
clone.setSimple1(simple4);
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();
newExtension.setExt('e2');
clone.setExtension(proto.jspb.test.CloneExtension.extField, newExtension);
assertArrayEquals(['v2',, ['a1', ['b1', 'c1']],,
[['a2', ['b2', 'c2']], ['a3', ['b3', 'c3']]],,, { 100: [, 'e2'] }],
[['a2', ['b2', 'c2']], ['a3', ['b3', 'c3']]], bytes2,, { 100: [, 'e2'] }],
clone.toArray());
assertArrayEquals(['v1',, ['x1', ['y1', 'z1']],,
[['x2', ['y2', 'z2']], ['x3', ['y3', 'z3']]],,, { 100: [, 'e1'] }],
[['x2', ['y2', 'z2']], ['x3', ['y3', 'z3']]], bytes1,, { 100: [, 'e1'] }],
original.toArray());
});
it('testCopyInto', function() {
var supportsUint8Array =
!goog.userAgent.IE || goog.userAgent.isVersionOrHigher('10');
var original = new proto.jspb.test.TestClone();
original.setStr('v1');
var dest = new proto.jspb.test.TestClone();
@ -484,6 +498,10 @@ describe('Message test suite', function() {
original.setSimple2List([simple2, simple3]);
dest.setSimple1(destSimple1);
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();
extension.setExt('e1');
original.setExtension(proto.jspb.test.CloneExtension.extField, extension);
@ -496,6 +514,15 @@ describe('Message test suite', function() {
dest.getSimple1().setAString('new value');
assertNotEquals(dest.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).
setExt('new value');
assertNotEquals(

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

@ -183,3 +183,32 @@ extend TestExtendable {
[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,
nested_types, enum_types, extensions, options=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):
_message.Message._CheckCalledFromGeneratedFile()
return _message.default_pool.FindMessageTypeByName(full_name)
@ -269,8 +269,8 @@ class Descriptor(_NestedDescriptorBase):
def __init__(self, name, full_name, filename, containing_type, fields,
nested_types, enum_types, extensions, options=None,
is_extendable=True, extension_ranges=None, oneofs=None,
file=None, serialized_start=None, serialized_end=None,
syntax=None): # pylint:disable=redefined-builtin
file=None, serialized_start=None, serialized_end=None, # pylint: disable=redefined-builtin
syntax=None):
"""Arguments to __init__() are as described in the description
of Descriptor fields above.
@ -665,7 +665,7 @@ class EnumValueDescriptor(DescriptorBase):
self.type = type
class OneofDescriptor(object):
class OneofDescriptor(DescriptorBase):
"""Descriptor for a oneof field.
name: (str) Name of the oneof field.
@ -682,12 +682,15 @@ class OneofDescriptor(object):
if _USE_C_DESCRIPTORS:
_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()
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."""
super(OneofDescriptor, self).__init__(options, 'OneofOptions')
self.name = name
self.full_name = full_name
self.index = index
@ -705,11 +708,22 @@ class ServiceDescriptor(_NestedDescriptorBase):
definition appears withing the .proto file.
methods: (list of MethodDescriptor) List of methods provided by this
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
None to use default service options.
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,
serialized_start=None, serialized_end=None):
super(ServiceDescriptor, self).__init__(
@ -718,16 +732,14 @@ class ServiceDescriptor(_NestedDescriptorBase):
serialized_end=serialized_end)
self.index = index
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.
for method in self.methods:
method.containing_service = self
def FindMethodByName(self, name):
"""Searches for the specified method, and returns its descriptor."""
for method in self.methods:
if name == method.name:
return method
return None
return self.methods_by_name.get(name, None)
def CopyToProto(self, proto):
"""Copies this to a descriptor_pb2.ServiceDescriptorProto.
@ -754,6 +766,14 @@ class MethodDescriptor(DescriptorBase):
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,
input_type, output_type, options=None):
"""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.
enum_types_by_name: Dict of enum 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
constructor, the global default pool is used.
"""
@ -825,6 +846,7 @@ class FileDescriptor(DescriptorBase):
self.enum_types_by_name = {}
self.extensions_by_name = {}
self.services_by_name = {}
self.dependencies = (dependencies or [])
self.public_dependencies = (public_dependencies or [])

@ -394,6 +394,11 @@ class DescriptorPool(object):
desc_proto_prefix, desc_proto.name, scope)
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._file_descriptors[file_proto.name] = file_descriptor
@ -441,7 +446,7 @@ class DescriptorPool(object):
for index, extension in enumerate(desc_proto.extension)]
oneofs = [
descriptor.OneofDescriptor(desc.name, '.'.join((desc_name, desc.name)),
index, None, [])
index, None, [], desc.options)
for index, desc in enumerate(desc_proto.oneof_decl)]
extension_ranges = [(r.start, r.end) for r in desc_proto.extension_range]
if extension_ranges:
@ -679,6 +684,64 @@ class DescriptorPool(object):
options=value_proto.options,
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):
"""Pulls out all the symbols from descriptor protos.

@ -594,7 +594,11 @@ class MessageMap(MutableMapping):
def MergeFrom(self, 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
# 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 factory_test1_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 import descriptor
from google.protobuf import descriptor_database
@ -630,6 +631,23 @@ class AddDescriptorTest(unittest.TestCase):
self.assertEqual(pool.FindMessageTypeByName('package.Message').name,
'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(
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_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.Add(file_proto)
self.my_file = self.pool.FindFileByName(file_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_method = descriptor.MethodDescriptor(
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
])
self.my_service = self.my_file.services_by_name[service_proto.name]
self.my_method = self.my_service.methods_by_name[method_proto.name]
def GetDescriptorPool(self):
return symbol_database.Default().pool
@ -139,13 +136,14 @@ class DescriptorTest(unittest.TestCase):
file_descriptor = unittest_custom_options_pb2.DESCRIPTOR
message_descriptor =\
unittest_custom_options_pb2.TestMessageWithCustomOptions.DESCRIPTOR
field_descriptor = message_descriptor.fields_by_name["field1"]
enum_descriptor = message_descriptor.enum_types_by_name["AnEnum"]
field_descriptor = message_descriptor.fields_by_name['field1']
oneof_descriptor = message_descriptor.oneofs_by_name['AnOneof']
enum_descriptor = message_descriptor.enum_types_by_name['AnEnum']
enum_value_descriptor =\
message_descriptor.enum_values_by_name["ANENUM_VAL2"]
message_descriptor.enum_values_by_name['ANENUM_VAL2']
service_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_opt1 = unittest_custom_options_pb2.file_opt1
@ -158,6 +156,9 @@ class DescriptorTest(unittest.TestCase):
self.assertEqual(8765432109, field_options.Extensions[field_opt1])
field_opt2 = unittest_custom_options_pb2.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_opt1 = unittest_custom_options_pb2.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 '
'"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):
# Duplicate key check is not supported for python2.6
if sys.version_info < (2, 7):

@ -1435,6 +1435,8 @@ class Proto3Test(unittest.TestCase):
msg2.map_int32_int32[12] = 55
msg2.map_int64_int64[88] = 99
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)
@ -1444,6 +1446,8 @@ class Proto3Test(unittest.TestCase):
self.assertEqual(99, msg2.map_int64_int64[88])
self.assertEqual(5, msg2.map_int32_foreign_message[111].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
# may have internally created multiple entries for a single key in the

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

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

@ -172,12 +172,16 @@ template<>
const FileDescriptor* GetFileDescriptor(const OneofDescriptor* descriptor) {
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.
//
// 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
// 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.
template<class DescriptorClass>
@ -204,11 +208,12 @@ static PyObject* GetOrBuildOptions(const DescriptorClass *descriptor) {
cdescriptor_pool::GetMessageClass(pool, message_type));
if (message_class == NULL) {
// The Options message was not found in the current DescriptorPool.
// In this case, there cannot be extensions to these options, and we can
// try to use the basic pool instead.
// This means that the pool cannot contain any extensions to the Options
// message either, so falling back to the basic pool we can only increase
// the chances of successfully parsing the options.
PyErr_Clear();
message_class = cdescriptor_pool::GetMessageClass(
GetDefaultDescriptorPool(), message_type);
pool = GetDefaultDescriptorPool();
message_class = cdescriptor_pool::GetMessageClass(pool, message_type);
}
if (message_class == NULL) {
PyErr_Format(PyExc_TypeError, "Could not retrieve class for Options: %s",
@ -248,7 +253,7 @@ static PyObject* GetOrBuildOptions(const DescriptorClass *descriptor) {
// Cache the result.
Py_INCREF(value.get());
(*pool->descriptor_options)[descriptor] = value.get();
(*descriptor_options)[descriptor] = value.get();
return value.release();
}
@ -1091,7 +1096,7 @@ PyTypeObject PyEnumDescriptor_Type = {
0, // tp_weaklistoffset
0, // tp_iter
0, // tp_iternext
enum_descriptor::Methods, // tp_getset
enum_descriptor::Methods, // tp_methods
0, // tp_members
enum_descriptor::Getters, // tp_getset
&descriptor::PyBaseDescriptor_Type, // tp_base
@ -1275,6 +1280,10 @@ static PyObject* GetExtensionsByName(PyFileDescriptor* self, void *closure) {
return NewFileExtensionsByName(_GetDescriptor(self));
}
static PyObject* GetServicesByName(PyFileDescriptor* self, void *closure) {
return NewFileServicesByName(_GetDescriptor(self));
}
static PyObject* GetDependencies(PyFileDescriptor* self, void *closure) {
return NewFileDependencies(_GetDescriptor(self));
}
@ -1324,6 +1333,7 @@ static PyGetSetDef Getters[] = {
{ "enum_types_by_name", (getter)GetEnumTypesByName, NULL, "Enums by name"},
{ "extensions_by_name", (getter)GetExtensionsByName, NULL,
"Extensions by name"},
{ "services_by_name", (getter)GetServicesByName, NULL, "Services by name"},
{ "dependencies", (getter)GetDependencies, 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[] = {
{ "name", (getter)GetName, NULL, "Name"},
{ "full_name", (getter)GetFullName, NULL, "Full name"},
{ "index", (getter)GetIndex, NULL, "Index"},
{ "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"},
{NULL}
};
static PyMethodDef Methods[] = {
{ "GetOptions", (PyCFunction)GetOptions, METH_NOARGS },
{NULL}
};
} // namespace oneof_descriptor
PyTypeObject PyOneofDescriptor_Type = {
@ -1492,7 +1531,7 @@ PyTypeObject PyOneofDescriptor_Type = {
0, // tp_weaklistoffset
0, // tp_iter
0, // tp_iternext
0, // tp_methods
oneof_descriptor::Methods, // tp_methods
0, // tp_members
oneof_descriptor::Getters, // tp_getset
&descriptor::PyBaseDescriptor_Type, // tp_base
@ -1504,6 +1543,222 @@ PyObject* PyOneofDescriptor_FromDescriptor(
&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.
static bool AddEnumValues(PyTypeObject *type,
const EnumDescriptor* enum_descriptor) {
@ -1573,6 +1828,12 @@ bool InitDescriptor() {
if (PyType_Ready(&PyOneofDescriptor_Type) < 0)
return false;
if (PyType_Ready(&PyServiceDescriptor_Type) < 0)
return false;
if (PyType_Ready(&PyMethodDescriptor_Type) < 0)
return false;
if (!InitDescriptorMappingTypes())
return false;

@ -47,6 +47,8 @@ extern PyTypeObject PyEnumDescriptor_Type;
extern PyTypeObject PyEnumValueDescriptor_Type;
extern PyTypeObject PyFileDescriptor_Type;
extern PyTypeObject PyOneofDescriptor_Type;
extern PyTypeObject PyServiceDescriptor_Type;
extern PyTypeObject PyMethodDescriptor_Type;
// Wraps a Descriptor in a Python object.
// The C++ pointer is usually borrowed from the global DescriptorPool.
@ -60,6 +62,10 @@ PyObject* PyEnumValueDescriptor_FromDescriptor(
PyObject* PyOneofDescriptor_FromDescriptor(const OneofDescriptor* descriptor);
PyObject* PyFileDescriptor_FromDescriptor(
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
// serialized FileDescriptorProto that can be cached.

@ -608,6 +608,24 @@ static PyObject* GetItem(PyContainer* self, Py_ssize_t 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.
// This function never fails.
int Find(PyContainer* self, PyObject* item) {
@ -713,6 +731,12 @@ static PySequenceMethods SeqSequenceMethods = {
(objobjproc)SeqContains, // sq_contains
};
static PyMappingMethods SeqMappingMethods = {
(lenfunc)Length, // mp_length
(binaryfunc)SeqSubscript, // mp_subscript
0, // mp_ass_subscript
};
PyTypeObject DescriptorSequence_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"DescriptorSequence", // tp_name
@ -726,7 +750,7 @@ PyTypeObject DescriptorSequence_Type = {
(reprfunc)ContainerRepr, // tp_repr
0, // tp_as_number
&SeqSequenceMethods, // tp_as_sequence
0, // tp_as_mapping
&SeqMappingMethods, // tp_as_mapping
0, // tp_hash
0, // tp_call
0, // tp_str
@ -1407,6 +1431,68 @@ PyObject* NewOneofFieldsSeq(ParentDescriptor 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 {
typedef const FileDescriptor* ParentDescriptor;
@ -1459,7 +1545,7 @@ static DescriptorContainerDef ContainerDef = {
} // namespace messages
PyObject* NewFileMessageTypesByName(const FileDescriptor* descriptor) {
PyObject* NewFileMessageTypesByName(ParentDescriptor descriptor) {
return descriptor::NewMappingByName(&messages::ContainerDef, descriptor);
}
@ -1507,7 +1593,7 @@ static DescriptorContainerDef ContainerDef = {
} // namespace enums
PyObject* NewFileEnumTypesByName(const FileDescriptor* descriptor) {
PyObject* NewFileEnumTypesByName(ParentDescriptor descriptor) {
return descriptor::NewMappingByName(&enums::ContainerDef, descriptor);
}
@ -1555,10 +1641,58 @@ static DescriptorContainerDef ContainerDef = {
} // namespace extensions
PyObject* NewFileExtensionsByName(const FileDescriptor* descriptor) {
PyObject* NewFileExtensionsByName(ParentDescriptor 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 {
typedef const FileDescriptor* ItemDescriptor;

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

@ -305,6 +305,40 @@ PyObject* FindOneofByName(PyDescriptorPool* self, PyObject* arg) {
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) {
Py_ssize_t name_size;
char* name;
@ -491,6 +525,10 @@ static PyMethodDef Methods[] = {
"Searches for enum type descriptor by full name." },
{ "FindOneofByName", (PyCFunction)FindOneofByName, METH_O,
"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,
"Gets the FileDescriptor containing the specified symbol." },

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

@ -1593,23 +1593,20 @@ struct ReleaseChild : public ChildVisitor {
parent_(parent) {}
int VisitRepeatedCompositeContainer(RepeatedCompositeContainer* container) {
return repeated_composite_container::Release(
reinterpret_cast<RepeatedCompositeContainer*>(container));
return repeated_composite_container::Release(container);
}
int VisitRepeatedScalarContainer(RepeatedScalarContainer* container) {
return repeated_scalar_container::Release(
reinterpret_cast<RepeatedScalarContainer*>(container));
return repeated_scalar_container::Release(container);
}
int VisitMapContainer(MapContainer* container) {
return reinterpret_cast<MapContainer*>(container)->Release();
return container->Release();
}
int VisitCMessage(CMessage* cmessage,
const FieldDescriptor* field_descriptor) {
return ReleaseSubMessage(parent_, field_descriptor,
reinterpret_cast<CMessage*>(cmessage));
return ReleaseSubMessage(parent_, field_descriptor, cmessage);
}
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
// 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)) {
PyErr_SetString(PyExc_TypeError,
"Argument to SetAllowOversizeProtos must be boolean");
@ -3044,6 +3041,10 @@ bool InitProto2MessageModule(PyObject *m) {
&PyFileDescriptor_Type));
PyModule_AddObject(m, "OneofDescriptor", reinterpret_cast<PyObject*>(
&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(
"google.protobuf.internal.enum_type_wrapper");
@ -3081,53 +3082,4 @@ bool InitProto2MessageModule(PyObject *m) {
} // namespace python
} // 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

@ -54,7 +54,7 @@ class MessageFactory;
#ifdef _SHARED_PTR_H
using std::shared_ptr;
using ::std::string;
using std::string;
#else
using internal::shared_ptr;
#endif
@ -269,6 +269,8 @@ int AssureWritable(CMessage* self);
// even in the case of extensions.
PyDescriptorPool* GetDescriptorPoolForMessage(CMessage* message);
PyObject* SetAllowOversizeProtos(PyObject* m, PyObject* arg);
} // namespace cmessage
@ -354,6 +356,8 @@ bool CheckFieldBelongsToMessage(const FieldDescriptor* field_descriptor,
extern PyObject* PickleError_class;
bool InitProto2MessageModule(PyObject *m);
} // namespace python
} // 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
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 import descriptor
from google.protobuf import text_encoding
__all__ = ['MessageToString', 'PrintMessage', 'PrintField',
'PrintFieldValue', 'Merge']
__all__ = ['MessageToString', 'PrintMessage', 'PrintField', 'PrintFieldValue',
'Merge']
_INTEGER_CHECKERS = (type_checkers.Uint32ValueChecker(),
type_checkers.Int32ValueChecker(),
@ -67,6 +67,7 @@ _FLOAT_NAN = re.compile('nanf?', re.IGNORECASE)
_FLOAT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_FLOAT,
descriptor.FieldDescriptor.CPPTYPE_DOUBLE])
_QUOTES = frozenset(("'", '"'))
_ANY_FULL_TYPE_NAME = 'google.protobuf.Any'
class Error(Exception):
@ -74,10 +75,30 @@ class Error(Exception):
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):
def __init__(self, as_utf8):
if six.PY2:
self._writer = io.BytesIO()
@ -97,9 +118,15 @@ class TextWriter(object):
return self._writer.getvalue()
def MessageToString(message, as_utf8=False, as_one_line=False,
pointy_brackets=False, use_index_order=False,
float_format=None, use_field_number=False):
def MessageToString(message,
as_utf8=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.
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
(per the "Format Specification Mini-Language"); otherwise, str() is used.
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:
A string of the text formatted protocol buffer message.
"""
out = TextWriter(as_utf8)
printer = _Printer(out, 0, as_utf8, as_one_line,
pointy_brackets, use_index_order, float_format,
use_field_number)
printer = _Printer(out, indent, as_utf8, as_one_line, pointy_brackets,
use_index_order, float_format, use_field_number,
descriptor_pool)
printer.PrintMessage(message)
result = out.getvalue()
out.close()
@ -141,39 +170,87 @@ def _IsMapEntry(field):
field.message_type.GetOptions().map_entry)
def PrintMessage(message, out, indent=0, as_utf8=False, as_one_line=False,
pointy_brackets=False, use_index_order=False,
float_format=None, use_field_number=False):
printer = _Printer(out, indent, as_utf8, as_one_line,
pointy_brackets, use_index_order, float_format,
use_field_number)
def PrintMessage(message,
out,
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):
printer = _Printer(out, indent, as_utf8, as_one_line, pointy_brackets,
use_index_order, float_format, use_field_number,
descriptor_pool)
printer.PrintMessage(message)
def PrintField(field, value, out, indent=0, as_utf8=False, as_one_line=False,
pointy_brackets=False, use_index_order=False, float_format=None):
def PrintField(field,
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."""
printer = _Printer(out, indent, as_utf8, as_one_line,
pointy_brackets, use_index_order, float_format)
printer = _Printer(out, indent, as_utf8, as_one_line, pointy_brackets,
use_index_order, float_format)
printer.PrintField(field, value)
def PrintFieldValue(field, value, out, indent=0, as_utf8=False,
as_one_line=False, pointy_brackets=False,
def PrintFieldValue(field,
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 value (not including name)."""
printer = _Printer(out, indent, as_utf8, as_one_line,
pointy_brackets, use_index_order, float_format)
printer = _Printer(out, indent, as_utf8, as_one_line, pointy_brackets,
use_index_order, float_format)
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):
"""Text format printer for protocol message."""
def __init__(self, out, indent=0, as_utf8=False, as_one_line=False,
pointy_brackets=False, use_index_order=False, float_format=None,
use_field_number=False):
def __init__(self,
out,
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.
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
used.
use_field_number: If True, print field numbers instead of names.
descriptor_pool: A DescriptorPool used to resolve Any types.
"""
self.out = out
self.indent = indent
@ -204,6 +282,20 @@ class _Printer(object):
self.use_index_order = use_index_order
self.float_format = float_format
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):
"""Convert protobuf message to text format.
@ -211,6 +303,9 @@ class _Printer(object):
Args:
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()
if self.use_index_order:
fields.sort(key=lambda x: x[0].index)
@ -222,8 +317,8 @@ class _Printer(object):
# of this file to work around.
#
# TODO(haberman): refactor and optimize if this becomes an issue.
entry_submsg = field.message_type._concrete_class(
key=key, value=value[key])
entry_submsg = field.message_type._concrete_class(key=key,
value=value[key])
self.PrintField(field, entry_submsg)
elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
for element in value:
@ -264,16 +359,7 @@ class _Printer(object):
else:
out.write('\n')
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
def _PrintMessageFieldValue(self, value):
if self.pointy_brackets:
openb = '<'
closeb = '>'
@ -281,17 +367,29 @@ class _Printer(object):
openb = '{'
closeb = '}'
if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
if self.as_one_line:
out.write(' %s ' % openb)
self.out.write(' %s ' % openb)
self.PrintMessage(value)
out.write(closeb)
self.out.write(closeb)
else:
out.write(' %s\n' % openb)
self.out.write(' %s\n' % openb)
self.indent += 2
self.PrintMessage(value)
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:
enum_value = field.enum_type.values_by_number.get(value, None)
if enum_value is not None:
@ -322,9 +420,11 @@ class _Printer(object):
out.write(str(value))
def Parse(text, message,
allow_unknown_extension=False, allow_field_number=False):
"""Parses an text representation of a protocol message into a message.
def Parse(text,
message,
allow_unknown_extension=False,
allow_field_number=False):
"""Parses a text representation of a protocol message into a message.
Args:
text: Message text representation.
@ -341,13 +441,16 @@ def Parse(text, message,
"""
if not isinstance(text, str):
text = text.decode('utf-8')
return ParseLines(text.split('\n'), message, allow_unknown_extension,
allow_field_number)
return ParseLines(
text.split('\n'), message, allow_unknown_extension, allow_field_number)
def Merge(text, message, allow_unknown_extension=False,
allow_field_number=False):
"""Parses an text representation of a protocol message into a message.
def Merge(text,
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
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
parsing
allow_field_number: if True, both field number and field name are allowed.
descriptor_pool: A DescriptorPool used to resolve Any types.
Returns:
The same message passed as argument.
@ -365,13 +469,19 @@ def Merge(text, message, allow_unknown_extension=False,
Raises:
ParseError: On text parsing problems.
"""
return MergeLines(text.split('\n'), message, allow_unknown_extension,
allow_field_number)
return MergeLines(
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):
"""Parses an text representation of a protocol message into a message.
"""Parses a text representation of a protocol message into a message.
Args:
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
parsing
allow_field_number: if True, both field number and field name are allowed.
descriptor_pool: A DescriptorPool used to resolve Any types.
Returns:
The same message passed as argument.
@ -390,9 +501,12 @@ def ParseLines(lines, message, allow_unknown_extension=False,
return parser.ParseLines(lines, message)
def MergeLines(lines, message, allow_unknown_extension=False,
allow_field_number=False):
"""Parses an text representation of a protocol message into a message.
def MergeLines(lines,
message,
allow_unknown_extension=False,
allow_field_number=False,
descriptor_pool=None):
"""Parses a text representation of a protocol message into a message.
Args:
lines: An iterable of lines of a message's text representation.
@ -407,41 +521,47 @@ def MergeLines(lines, message, allow_unknown_extension=False,
Raises:
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)
class _Parser(object):
"""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_field_number = allow_field_number
self.descriptor_pool = descriptor_pool
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):
text = text.decode('utf-8')
return self.ParseLines(text.split('\n'), 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._ParseOrMerge(lines, message)
return 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)
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._ParseOrMerge(lines, message)
return 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:
lines: Lines of a message's text representation.
@ -450,7 +570,7 @@ class _Parser(object):
Raises:
ParseError: On text parsing problems.
"""
tokenizer = _Tokenizer(lines)
tokenizer = Tokenizer(lines)
while not tokenizer.AtEnd():
self._MergeField(tokenizer, message)
@ -491,13 +611,13 @@ class _Parser(object):
'Extension "%s" not registered.' % name)
elif message_descriptor != field.containing_type:
raise tokenizer.ParseErrorPreviousToken(
'Extension "%s" does not extend message type "%s".' % (
name, message_descriptor.full_name))
'Extension "%s" does not extend message type "%s".' %
(name, message_descriptor.full_name))
tokenizer.Consume(']')
else:
name = tokenizer.ConsumeIdentifier()
name = tokenizer.ConsumeIdentifierOrNumber()
if self.allow_field_number and name.isdigit():
number = ParseInteger(name, True, True)
field = message_descriptor.fields_by_number.get(number, None)
@ -520,8 +640,8 @@ class _Parser(object):
if not field:
raise tokenizer.ParseErrorPreviousToken(
'Message type "%s" has no field named "%s".' % (
message_descriptor.full_name, name))
'Message type "%s" has no field named "%s".' %
(message_descriptor.full_name, name))
if field:
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:
raise tokenizer.ParseErrorPreviousToken(
'Field "%s" is specified along with field "%s", another member '
'of oneof "%s" for message type "%s".' % (
field.name, which_oneof, field.containing_oneof.name,
'of oneof "%s" for message type "%s".' %
(field.name, which_oneof, field.containing_oneof.name,
message_descriptor.full_name))
if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
@ -543,12 +663,13 @@ class _Parser(object):
tokenizer.Consume(':')
merger = self._MergeScalarField
if (field.label == descriptor.FieldDescriptor.LABEL_REPEATED
and tokenizer.TryConsume('[')):
if (field.label == descriptor.FieldDescriptor.LABEL_REPEATED and
tokenizer.TryConsume('[')):
# Short repeated format, e.g. "foo: [1, 2, 3]"
while True:
merger(tokenizer, message, field)
if tokenizer.TryConsume(']'): break
if tokenizer.TryConsume(']'):
break
tokenizer.Consume(',')
else:
@ -563,6 +684,21 @@ class _Parser(object):
if not 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):
"""Merges a single scalar field into a message.
@ -582,7 +718,34 @@ class _Parser(object):
tokenizer.Consume('{')
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:
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:
sub_message = message.Extensions[field].add()
elif is_map_entry:
@ -628,17 +791,17 @@ class _Parser(object):
if field.type in (descriptor.FieldDescriptor.TYPE_INT32,
descriptor.FieldDescriptor.TYPE_SINT32,
descriptor.FieldDescriptor.TYPE_SFIXED32):
value = tokenizer.ConsumeInt32()
value = _ConsumeInt32(tokenizer)
elif field.type in (descriptor.FieldDescriptor.TYPE_INT64,
descriptor.FieldDescriptor.TYPE_SINT64,
descriptor.FieldDescriptor.TYPE_SFIXED64):
value = tokenizer.ConsumeInt64()
value = _ConsumeInt64(tokenizer)
elif field.type in (descriptor.FieldDescriptor.TYPE_UINT32,
descriptor.FieldDescriptor.TYPE_FIXED32):
value = tokenizer.ConsumeUint32()
value = _ConsumeUint32(tokenizer)
elif field.type in (descriptor.FieldDescriptor.TYPE_UINT64,
descriptor.FieldDescriptor.TYPE_FIXED64):
value = tokenizer.ConsumeUint64()
value = _ConsumeUint64(tokenizer)
elif field.type in (descriptor.FieldDescriptor.TYPE_FLOAT,
descriptor.FieldDescriptor.TYPE_DOUBLE):
value = tokenizer.ConsumeFloat()
@ -753,13 +916,12 @@ def _SkipFieldValue(tokenizer):
return
if (not tokenizer.TryConsumeIdentifier() and
not tokenizer.TryConsumeInt64() and
not tokenizer.TryConsumeUint64() and
not _TryConsumeInt64(tokenizer) and not _TryConsumeUint64(tokenizer) and
not tokenizer.TryConsumeFloat()):
raise ParseError('Invalid field value: ' + tokenizer.token)
class _Tokenizer(object):
class Tokenizer(object):
"""Protocol buffer text representation tokenizer.
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.
"""
_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([
r'[a-zA-Z_][0-9a-zA-Z_+-]*', # an identifier
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
]))
_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._line = -1
self._column = 0
@ -789,6 +954,9 @@ class _Tokenizer(object):
self._previous_line = 0
self._previous_column = 0
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.NextToken()
@ -818,7 +986,7 @@ class _Tokenizer(object):
def _SkipWhitespace(self):
while True:
self._PopLine()
match = self._WHITESPACE.match(self._current_line, self._column)
match = self._whitespace_pattern.match(self._current_line, self._column)
if not match:
break
length = len(match.group(0))
@ -848,7 +1016,14 @@ class _Tokenizer(object):
ParseError: If the text couldn't be consumed.
"""
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):
try:
@ -868,85 +1043,55 @@ class _Tokenizer(object):
"""
result = self.token
if not self._IDENTIFIER.match(result):
raise self._ParseError('Expected identifier.')
raise self.ParseError('Expected identifier.')
self.NextToken()
return result
def ConsumeInt32(self):
"""Consumes a signed 32bit integer number.
Returns:
The integer parsed.
Raises:
ParseError: If a signed 32bit integer couldn't be consumed.
"""
def TryConsumeIdentifierOrNumber(self):
try:
result = ParseInteger(self.token, is_signed=True, is_long=False)
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()
self.ConsumeIdentifierOrNumber()
return True
except ParseError:
return False
def ConsumeInt64(self):
"""Consumes a signed 64bit integer number.
def ConsumeIdentifierOrNumber(self):
"""Consumes protocol message field identifier.
Returns:
The integer parsed.
Identifier string.
Raises:
ParseError: If a signed 64bit integer couldn't be consumed.
ParseError: If an identifier couldn't be consumed.
"""
try:
result = ParseInteger(self.token, is_signed=True, is_long=True)
except ValueError as e:
raise self._ParseError(str(e))
result = self.token
if not self._IDENTIFIER_OR_NUMBER.match(result):
raise self.ParseError('Expected identifier or number.')
self.NextToken()
return result
def TryConsumeUint64(self):
def TryConsumeInteger(self):
try:
self.ConsumeUint64()
# Note: is_long only affects value type, not whether an error is raised.
self.ConsumeInteger()
return True
except ParseError:
return False
def ConsumeUint64(self):
"""Consumes an unsigned 64bit integer number.
def ConsumeInteger(self, is_long=False):
"""Consumes an integer number.
Args:
is_long: True if the value should be returned as a long integer.
Returns:
The integer parsed.
Raises:
ParseError: If an unsigned 64bit integer couldn't be consumed.
ParseError: If an integer couldn't be consumed.
"""
try:
result = ParseInteger(self.token, is_signed=False, is_long=True)
result = _ParseAbstractInteger(self.token, is_long=is_long)
except ValueError as e:
raise self._ParseError(str(e))
raise self.ParseError(str(e))
self.NextToken()
return result
@ -969,7 +1114,7 @@ class _Tokenizer(object):
try:
result = ParseFloat(self.token)
except ValueError as e:
raise self._ParseError(str(e))
raise self.ParseError(str(e))
self.NextToken()
return result
@ -985,7 +1130,7 @@ class _Tokenizer(object):
try:
result = ParseBool(self.token)
except ValueError as e:
raise self._ParseError(str(e))
raise self.ParseError(str(e))
self.NextToken()
return result
@ -1039,15 +1184,15 @@ class _Tokenizer(object):
"""
text = self.token
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]:
raise self._ParseError('String missing ending quote: %r' % (text,))
raise self.ParseError('String missing ending quote: %r' % (text,))
try:
result = text_encoding.CUnescape(text[1:-1])
except ValueError as e:
raise self._ParseError(str(e))
raise self.ParseError(str(e))
self.NextToken()
return result
@ -1055,7 +1200,7 @@ class _Tokenizer(object):
try:
result = ParseEnum(field, self.token)
except ValueError as e:
raise self._ParseError(str(e))
raise self.ParseError(str(e))
self.NextToken()
return result
@ -1068,16 +1213,15 @@ class _Tokenizer(object):
Returns:
A ParseError instance.
"""
return ParseError('%d:%d : %s' % (
self._previous_line + 1, self._previous_column + 1, message))
return ParseError(message, self._previous_line + 1,
self._previous_column + 1)
def _ParseError(self, message):
def ParseError(self, message):
"""Creates and *returns* a ParseError for the current token."""
return ParseError('%d:%d : %s' % (
self._line + 1, self._column + 1, message))
return ParseError(message, self._line + 1, self._column + 1)
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):
"""Reads the next meaningful token."""
@ -1092,12 +1236,124 @@ class _Tokenizer(object):
return
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:
token = match.group(0)
self.token = token
else:
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):
"""Parses an integer.
@ -1110,6 +1366,28 @@ def ParseInteger(text, is_signed=False, is_long=False):
Returns:
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:
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
# (e.g. the C++ implementation) simpler.
if is_long:
result = long(text, 0)
return long(text, 0)
else:
result = int(text, 0)
return int(text, 0)
except ValueError:
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):
"""Parse a floating point number.
@ -1206,14 +1479,12 @@ def ParseEnum(field, value):
# Identifier.
enum_value = enum_descriptor.values_by_name.get(value, None)
if enum_value is None:
raise ValueError(
'Enum type "%s" has no value named %s.' % (
enum_descriptor.full_name, value))
raise ValueError('Enum type "%s" has no value named %s.' %
(enum_descriptor.full_name, value))
else:
# Numeric value.
enum_value = enum_descriptor.values_by_number.get(number, None)
if enum_value is None:
raise ValueError(
'Enum type "%s" has no value with number %d.' % (
enum_descriptor.full_name, number))
raise ValueError('Enum type "%s" has no value with number %d.' %
(enum_descriptor.full_name, number))
return enum_value.number

@ -76,6 +76,7 @@ def generate_proto(source, require = True):
sys.exit(-1)
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/unittest_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/factory_test1.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/outer.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)
}
::google::protobuf::uint8* Any::SerializeWithCachedSizesToArray(
::google::protobuf::uint8* target) const {
::google::protobuf::uint8* Any::InternalSerializeWithCachedSizesToArray(
bool deterministic, ::google::protobuf::uint8* target) const {
// @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Any)
// optional string type_url = 1;
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:
Any();
virtual ~Any();
@ -86,7 +86,11 @@ class LIBPROTOBUF_EXPORT Any : public ::google::protobuf::Message {
::google::protobuf::io::CodedInputStream* input);
void SerializeWithCachedSizes(
::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_; }
private:
void SharedCtor();

@ -65,6 +65,16 @@ option objc_class_prefix = "GPB";
// 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
// 'type.googleapis.com/full.type.name' as the type URL and the unpack
// 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
// 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:
//
// * 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
// qualified name of the type (as in `path/google.protobuf.Duration`).
// 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
// 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.
//
string type_url = 1;

@ -475,8 +475,8 @@ void Api::SerializeWithCachedSizes(
// @@protoc_insertion_point(serialize_end:google.protobuf.Api)
}
::google::protobuf::uint8* Api::SerializeWithCachedSizesToArray(
::google::protobuf::uint8* target) const {
::google::protobuf::uint8* Api::InternalSerializeWithCachedSizesToArray(
bool deterministic, ::google::protobuf::uint8* target) const {
// @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Api)
// optional string name = 1;
if (this->name().size() > 0) {
@ -492,15 +492,15 @@ void Api::SerializeWithCachedSizes(
// repeated .google.protobuf.Method methods = 2;
for (unsigned int i = 0, n = this->methods_size(); i < n; i++) {
target = ::google::protobuf::internal::WireFormatLite::
WriteMessageNoVirtualToArray(
2, this->methods(i), target);
InternalWriteMessageNoVirtualToArray(
2, this->methods(i), false, target);
}
// repeated .google.protobuf.Option options = 3;
for (unsigned int i = 0, n = this->options_size(); i < n; i++) {
target = ::google::protobuf::internal::WireFormatLite::
WriteMessageNoVirtualToArray(
3, this->options(i), target);
InternalWriteMessageNoVirtualToArray(
3, this->options(i), false, target);
}
// optional string version = 4;
@ -517,15 +517,15 @@ void Api::SerializeWithCachedSizes(
// optional .google.protobuf.SourceContext source_context = 5;
if (this->has_source_context()) {
target = ::google::protobuf::internal::WireFormatLite::
WriteMessageNoVirtualToArray(
5, *this->source_context_, target);
InternalWriteMessageNoVirtualToArray(
5, *this->source_context_, false, target);
}
// repeated .google.protobuf.Mixin mixins = 6;
for (unsigned int i = 0, n = this->mixins_size(); i < n; i++) {
target = ::google::protobuf::internal::WireFormatLite::
WriteMessageNoVirtualToArray(
6, this->mixins(i), target);
InternalWriteMessageNoVirtualToArray(
6, this->mixins(i), false, target);
}
// optional .google.protobuf.Syntax syntax = 7;
@ -1225,8 +1225,8 @@ void Method::SerializeWithCachedSizes(
// @@protoc_insertion_point(serialize_end:google.protobuf.Method)
}
::google::protobuf::uint8* Method::SerializeWithCachedSizesToArray(
::google::protobuf::uint8* target) const {
::google::protobuf::uint8* Method::InternalSerializeWithCachedSizesToArray(
bool deterministic, ::google::protobuf::uint8* target) const {
// @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Method)
// optional string name = 1;
if (this->name().size() > 0) {
@ -1274,8 +1274,8 @@ void Method::SerializeWithCachedSizes(
// repeated .google.protobuf.Option options = 6;
for (unsigned int i = 0, n = this->options_size(); i < n; i++) {
target = ::google::protobuf::internal::WireFormatLite::
WriteMessageNoVirtualToArray(
6, this->options(i), target);
InternalWriteMessageNoVirtualToArray(
6, this->options(i), false, target);
}
// optional .google.protobuf.Syntax syntax = 7;
@ -1803,8 +1803,8 @@ void Mixin::SerializeWithCachedSizes(
// @@protoc_insertion_point(serialize_end:google.protobuf.Mixin)
}
::google::protobuf::uint8* Mixin::SerializeWithCachedSizesToArray(
::google::protobuf::uint8* target) const {
::google::protobuf::uint8* Mixin::InternalSerializeWithCachedSizesToArray(
bool deterministic, ::google::protobuf::uint8* target) const {
// @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Mixin)
// optional string name = 1;
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:
Api();
virtual ~Api();
@ -79,7 +79,11 @@ class LIBPROTOBUF_EXPORT Api : public ::google::protobuf::Message {
::google::protobuf::io::CodedInputStream* input);
void SerializeWithCachedSizes(
::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_; }
private:
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:
Method();
virtual ~Method();
@ -230,7 +234,11 @@ class LIBPROTOBUF_EXPORT Method : public ::google::protobuf::Message {
::google::protobuf::io::CodedInputStream* input);
void SerializeWithCachedSizes(
::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_; }
private:
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:
Mixin();
virtual ~Mixin();
@ -371,7 +379,11 @@ class LIBPROTOBUF_EXPORT Mixin : public ::google::protobuf::Message {
::google::protobuf::io::CodedInputStream* input);
void SerializeWithCachedSizes(
::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_; }
private:
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));
b->pos = kHeaderSize + n;
b->size = size;
if (b->avail() == 0) {
// Do not attempt to reuse this block.
b->owner = NULL;
} else {
b->owner = me;
}
#ifdef ADDRESS_SANITIZER
// 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.
@ -223,9 +218,7 @@ void* Arena::SlowAlloc(size_t n) {
}
b = NewBlock(me, b, n, options_.start_block_size, options_.max_block_size);
AddBlock(b);
if (b->owner == me) { // If this block can be reused (see NewBlock()).
SetThreadCacheBlock(b);
}
return reinterpret_cast<char*>(b) + kHeaderSize;
}

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

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

@ -641,11 +641,15 @@ CommandLineInterface::MemoryOutputStream::~MemoryOutputStream() {
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
// insert the data. Note that this has the effect of pushing the insertion
// point down, so the data is inserted before it. This is intentional
// because it means that multiple insertions at the same point will end
// up in the expected order in the final output.
// insert the data. Note that this has the effect of pushing the
// insertion point down, so the data is inserted before it. This is
// intentional because it means that multiple insertions at the same point
// will end up in the expected order in the final output.
pos = target->find_last_of('\n', pos);
if (pos == string::npos) {
// Insertion point is on the first line.
@ -654,6 +658,7 @@ CommandLineInterface::MemoryOutputStream::~MemoryOutputStream() {
// Advance to character after '\n'.
++pos;
}
}
// Extract indent.
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_helpers.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/wire_format.h>
#include <google/protobuf/stubs/strutil.h>
namespace google {
namespace protobuf {

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

@ -838,11 +838,13 @@ GenerateDependentBaseClassDefinition(io::Printer* printer) {
map<string, string> vars;
vars["classname"] = DependentBaseClassTemplateName(descriptor_);
vars["full_name"] = descriptor_->full_name();
vars["superclass"] = SuperClassName(descriptor_, options_);
printer->Print(vars,
"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");
printer->Indent();
@ -878,6 +880,7 @@ GenerateClassDefinition(io::Printer* printer) {
map<string, string> vars;
vars["classname"] = classname_;
vars["full_name"] = descriptor_->full_name();
vars["field_count"] = SimpleItoa(descriptor_->field_count());
vars["oneof_decl_count"] = SimpleItoa(descriptor_->oneof_decl_count());
if (options_.dllexport_decl.empty()) {
@ -892,7 +895,9 @@ GenerateClassDefinition(io::Printer* printer) {
vars["superclass"] = SuperClassName(descriptor_, options_);
}
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_);
if (use_dependent_base_) {
printer->Print(vars, " friend class $superclass$;\n");
@ -1076,7 +1081,11 @@ GenerateClassDefinition(io::Printer* printer) {
}
if (HasFastArraySerialization(descriptor_->file(), options_)) {
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));
}
}
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;
if (PreserveUnknownFields(descriptor_) &&
!UseUnknownFieldSet(descriptor_->file(), options_)) {
@ -3267,8 +3283,8 @@ void MessageGenerator::GenerateSerializeOneExtensionRange(
"// Extension range [$start$, $end$)\n");
if (to_array) {
printer->Print(vars,
"target = _extensions_.SerializeWithCachedSizesToArray(\n"
" $start$, $end$, target);\n\n");
"target = _extensions_.InternalSerializeWithCachedSizesToArray(\n"
" $start$, $end$, false, target);\n\n");
} else {
printer->Print(vars,
"_extensions_.SerializeWithCachedSizes(\n"
@ -3320,10 +3336,11 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) {
if (descriptor_->options().message_set_wire_format()) {
// Special-case MessageSet.
printer->Print(
"::google::protobuf::uint8* $classname$::SerializeWithCachedSizesToArray(\n"
" ::google::protobuf::uint8* target) const {\n"
" target =\n"
" _extensions_.SerializeMessageSetWithCachedSizesToArray(target);\n",
"::google::protobuf::uint8* $classname$::InternalSerializeWithCachedSizesToArray(\n"
" bool deterministic, ::google::protobuf::uint8* target) const {\n"
" target = _extensions_."
"InternalSerializeMessageSetWithCachedSizesToArray(\n"
" deterministic, target);\n",
"classname", classname_);
GOOGLE_CHECK(UseUnknownFieldSet(descriptor_->file(), options_));
printer->Print(
@ -3337,8 +3354,8 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) {
}
printer->Print(
"::google::protobuf::uint8* $classname$::SerializeWithCachedSizesToArray(\n"
" ::google::protobuf::uint8* target) const {\n",
"::google::protobuf::uint8* $classname$::InternalSerializeWithCachedSizesToArray(\n"
" bool deterministic, ::google::protobuf::uint8* target) const {\n",
"classname", classname_);
printer->Indent();

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

Loading…
Cancel
Save