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. 95
      java/core/src/main/java/com/google/protobuf/AbstractMessage.java
  3. 46
      java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java
  4. 46
      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. 50
      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. 26
      java/core/src/main/java/com/google/protobuf/FieldSet.java
  12. 51
      java/core/src/main/java/com/google/protobuf/FloatArrayList.java
  13. 202
      java/core/src/main/java/com/google/protobuf/GeneratedMessage.java
  14. 238
      java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java
  15. 51
      java/core/src/main/java/com/google/protobuf/IntArrayList.java
  16. 53
      java/core/src/main/java/com/google/protobuf/LongArrayList.java
  17. 219
      java/core/src/main/java/com/google/protobuf/MapEntry.java
  18. 319
      java/core/src/main/java/com/google/protobuf/MapEntryLite.java
  19. 413
      java/core/src/main/java/com/google/protobuf/MapField.java
  20. 445
      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. 190
      java/core/src/test/java/com/google/protobuf/BooleanArrayListTest.java
  30. 8
      java/core/src/test/java/com/google/protobuf/DescriptorsTest.java
  31. 149
      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. 149
      java/core/src/test/java/com/google/protobuf/FloatArrayListTest.java
  35. 143
      java/core/src/test/java/com/google/protobuf/IntArrayListTest.java
  36. 17
      java/core/src/test/java/com/google/protobuf/LazyMessageLiteTest.java
  37. 31
      java/core/src/test/java/com/google/protobuf/LiteTest.java
  38. 183
      java/core/src/test/java/com/google/protobuf/LongArrayListTest.java
  39. 717
      java/core/src/test/java/com/google/protobuf/MapForProto2LiteTest.java
  40. 654
      java/core/src/test/java/com/google/protobuf/MapForProto2Test.java
  41. 588
      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. 78
      java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java
  53. 715
      java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
  54. 381
      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. 113
      java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java
  57. 733
      java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
  58. 77
      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. 624
      python/google/protobuf/internal/text_format_test.py
  77. 762
      python/google/protobuf/json_format.py
  78. 277
      python/google/protobuf/pyext/descriptor.cc
  79. 6
      python/google/protobuf/pyext/descriptor.h
  80. 158
      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. 613
      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. 11
      src/google/protobuf/arena.cc
  95. 2
      src/google/protobuf/arena.h
  96. 1
      src/google/protobuf/arena_unittest.cc
  97. 27
      src/google/protobuf/compiler/command_line_interface.cc
  98. 2
      src/google/protobuf/compiler/cpp/cpp_enum_field.cc
  99. 57
      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 \

@ -54,12 +54,40 @@ public abstract class AbstractMessage
// TODO(dweis): Update GeneratedMessage to parameterize with MessageType and BuilderType.
extends AbstractMessageLite
implements Message {
@Override
public boolean isInitialized() {
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;
}
}

@ -45,10 +45,10 @@ import java.util.Collection;
*/
public abstract class AbstractMessageLite<
MessageType extends AbstractMessageLite<MessageType, BuilderType>,
BuilderType extends AbstractMessageLite.Builder<MessageType, BuilderType>>
BuilderType extends AbstractMessageLite.Builder<MessageType, BuilderType>>
implements MessageLite {
protected int memoizedHashCode = 0;
@Override
public ByteString toByteString() {
try {
@ -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()) {
@ -120,7 +121,7 @@ public abstract class AbstractMessageLite<
final Collection<? super T> list) {
Builder.addAll(values, list);
}
/**
* A partial implementation of the {@link Message.Builder} interface which
* implements as many methods of that interface as possible in terms of
@ -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);
}
}
@ -321,7 +314,7 @@ public abstract class AbstractMessageLite<
return mergeDelimitedFrom(input,
ExtensionRegistryLite.getEmptyRegistry());
}
@Override
@SuppressWarnings("unchecked") // isInstance takes care of this
public BuilderType mergeFrom(final MessageLite other) {
@ -329,12 +322,17 @@ public abstract class AbstractMessageLite<
throw new IllegalArgumentException(
"mergeFrom(MessageLite) can only merge messages of the same type.");
}
return internalMergeFrom((MessageType) other);
}
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.

@ -38,21 +38,22 @@ import java.util.RandomAccess;
/**
* An implementation of {@link BooleanList} on top of a primitive array.
*
*
* @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 {
EMPTY_LIST.makeImmutable();
}
public static BooleanArrayList emptyList() {
return EMPTY_LIST;
}
/**
* The backing store for the list.
*/
@ -72,13 +73,14 @@ 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;
}
@Override
public boolean equals(Object o) {
if (this == o) {
@ -91,14 +93,14 @@ final class BooleanArrayList
if (size != other.size) {
return false;
}
final boolean[] arr = other.array;
for (int i = 0; i < size; i++) {
if (array[i] != arr[i]) {
return false;
}
}
return true;
}
@ -170,7 +172,7 @@ final class BooleanArrayList
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index));
}
if (size < array.length) {
// Shift everything over to make room
System.arraycopy(array, index, array, index + 1, size - index);
@ -178,10 +180,10 @@ final class BooleanArrayList
// Resize to 1.5x the size
int length = ((size * 3) / 2) + 1;
boolean[] newArray = new boolean[length];
// Copy the first part directly
System.arraycopy(array, 0, newArray, 0, index);
// Copy the rest shifted over by one to make room
System.arraycopy(array, index, newArray, index + 1, size - index);
array = newArray;
@ -195,38 +197,38 @@ final class BooleanArrayList
@Override
public boolean addAll(Collection<? extends Boolean> collection) {
ensureIsMutable();
if (collection == null) {
throw new NullPointerException();
}
// We specialize when adding another BooleanArrayList to avoid boxing elements.
if (!(collection instanceof BooleanArrayList)) {
return super.addAll(collection);
}
BooleanArrayList list = (BooleanArrayList) collection;
if (list.size == 0) {
return false;
}
int overflow = Integer.MAX_VALUE - size;
if (overflow < list.size) {
// We can't actually represent a list this large.
throw new OutOfMemoryError();
}
int newSize = size + list.size;
if (newSize > array.length) {
array = Arrays.copyOf(array, newSize);
}
System.arraycopy(list.array, 0, array, size, list.size);
size = newSize;
modCount++;
return true;
}
@Override
public boolean remove(Object o) {
ensureIsMutable();
@ -255,7 +257,7 @@ final class BooleanArrayList
/**
* Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an
* {@link IndexOutOfBoundsException} if it is not.
*
*
* @param index the index to verify is in range
*/
private void ensureIndexInRange(int index) {

@ -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,

@ -38,26 +38,27 @@ import java.util.RandomAccess;
/**
* An implementation of {@link DoubleList} on top of a primitive array.
*
*
* @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 {
EMPTY_LIST.makeImmutable();
}
public static DoubleArrayList emptyList() {
return EMPTY_LIST;
}
/**
* The backing store for the list.
*/
private double[] array;
/**
* The size of the list distinct from the length of the array. That is, it is the number of
* elements set in the list.
@ -72,13 +73,14 @@ 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;
}
@Override
public boolean equals(Object o) {
if (this == o) {
@ -91,14 +93,14 @@ final class DoubleArrayList
if (size != other.size) {
return false;
}
final double[] arr = other.array;
for (int i = 0; i < size; i++) {
if (array[i] != arr[i]) {
return false;
}
}
return true;
}
@ -119,7 +121,7 @@ final class DoubleArrayList
}
return new DoubleArrayList(Arrays.copyOf(array, capacity), size);
}
@Override
public Double get(int index) {
return getDouble(index);
@ -171,7 +173,7 @@ final class DoubleArrayList
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index));
}
if (size < array.length) {
// Shift everything over to make room
System.arraycopy(array, index, array, index + 1, size - index);
@ -179,10 +181,10 @@ final class DoubleArrayList
// Resize to 1.5x the size
int length = ((size * 3) / 2) + 1;
double[] newArray = new double[length];
// Copy the first part directly
System.arraycopy(array, 0, newArray, 0, index);
// Copy the rest shifted over by one to make room
System.arraycopy(array, index, newArray, index + 1, size - index);
array = newArray;
@ -196,38 +198,38 @@ final class DoubleArrayList
@Override
public boolean addAll(Collection<? extends Double> collection) {
ensureIsMutable();
if (collection == null) {
throw new NullPointerException();
}
// We specialize when adding another DoubleArrayList to avoid boxing elements.
if (!(collection instanceof DoubleArrayList)) {
return super.addAll(collection);
}
DoubleArrayList list = (DoubleArrayList) collection;
if (list.size == 0) {
return false;
}
int overflow = Integer.MAX_VALUE - size;
if (overflow < list.size) {
// We can't actually represent a list this large.
throw new OutOfMemoryError();
}
int newSize = size + list.size;
if (newSize > array.length) {
array = Arrays.copyOf(array, newSize);
}
System.arraycopy(list.array, 0, array, size, list.size);
size = newSize;
modCount++;
return true;
}
@Override
public boolean remove(Object o) {
ensureIsMutable();
@ -256,7 +258,7 @@ final class DoubleArrayList
/**
* Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an
* {@link IndexOutOfBoundsException} if it is not.
*
*
* @param index the index to verify is in range
*/
private void ensureIndexInRange(int index) {

@ -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 {

@ -120,21 +120,21 @@ final class FieldSet<FieldDescriptorType extends
public boolean isImmutable() {
return isImmutable;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof FieldSet)) {
return false;
}
FieldSet<?> other = (FieldSet<?>) o;
return other.fields.equals(other.fields);
return fields.equals(other.fields);
}
@Override
public int hashCode() {
return fields.hashCode();
@ -493,7 +493,7 @@ final class FieldSet<FieldDescriptorType extends
}
/**
* Like {@link Message.Builder#mergeFrom(Message)}, but merges from another
* Like {@link Message.Builder#mergeFrom(Message)}, but merges from another
* {@link FieldSet}.
*/
public void mergeFrom(final FieldSet<FieldDescriptorType> other) {
@ -638,10 +638,11 @@ final class FieldSet<FieldDescriptorType extends
* {@link Message#getField(Descriptors.FieldDescriptor)} for
* this field.
*/
private static void writeElement(final CodedOutputStream output,
final WireFormat.FieldType type,
final int number,
final Object value) throws IOException {
static void writeElement(
final CodedOutputStream output,
final WireFormat.FieldType type,
final int number,
final Object value) throws IOException {
// Special case for groups, which need a start and end tag; other fields
// can just use writeTag() and writeFieldNoTag().
if (type == WireFormat.FieldType.GROUP) {
@ -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

@ -38,25 +38,27 @@ import java.util.RandomAccess;
/**
* An implementation of {@link FloatList} on top of a primitive array.
*
*
* @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 {
EMPTY_LIST.makeImmutable();
}
public static FloatArrayList emptyList() {
return EMPTY_LIST;
}
/**
* The backing store for the list.
*/
private float[] array;
/**
* The size of the list distinct from the length of the array. That is, it is the number of
* elements set in the list.
@ -71,13 +73,14 @@ 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;
}
@Override
public boolean equals(Object o) {
if (this == o) {
@ -90,14 +93,14 @@ final class FloatArrayList extends AbstractProtobufList<Float> implements FloatL
if (size != other.size) {
return false;
}
final float[] arr = other.array;
for (int i = 0; i < size; i++) {
if (array[i] != arr[i]) {
return false;
}
}
return true;
}
@ -117,7 +120,7 @@ final class FloatArrayList extends AbstractProtobufList<Float> implements FloatL
}
return new FloatArrayList(Arrays.copyOf(array, capacity), size);
}
@Override
public Float get(int index) {
return getFloat(index);
@ -169,7 +172,7 @@ final class FloatArrayList extends AbstractProtobufList<Float> implements FloatL
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index));
}
if (size < array.length) {
// Shift everything over to make room
System.arraycopy(array, index, array, index + 1, size - index);
@ -177,10 +180,10 @@ final class FloatArrayList extends AbstractProtobufList<Float> implements FloatL
// Resize to 1.5x the size
int length = ((size * 3) / 2) + 1;
float[] newArray = new float[length];
// Copy the first part directly
System.arraycopy(array, 0, newArray, 0, index);
// Copy the rest shifted over by one to make room
System.arraycopy(array, index, newArray, index + 1, size - index);
array = newArray;
@ -194,38 +197,38 @@ final class FloatArrayList extends AbstractProtobufList<Float> implements FloatL
@Override
public boolean addAll(Collection<? extends Float> collection) {
ensureIsMutable();
if (collection == null) {
throw new NullPointerException();
}
// We specialize when adding another FloatArrayList to avoid boxing elements.
if (!(collection instanceof FloatArrayList)) {
return super.addAll(collection);
}
FloatArrayList list = (FloatArrayList) collection;
if (list.size == 0) {
return false;
}
int overflow = Integer.MAX_VALUE - size;
if (overflow < list.size) {
// We can't actually represent a list this large.
throw new OutOfMemoryError();
}
int newSize = size + list.size;
if (newSize > array.length) {
array = Arrays.copyOf(array, newSize);
}
System.arraycopy(list.array, 0, array, size, list.size);
size = newSize;
modCount++;
return true;
}
@Override
public boolean remove(Object o) {
ensureIsMutable();
@ -254,7 +257,7 @@ final class FloatArrayList extends AbstractProtobufList<Float> implements FloatL
/**
* Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an
* {@link IndexOutOfBoundsException} if it is not.
*
*
* @param index the index to verify is in range
*/
private void ensureIndexInRange(int index) {

@ -355,31 +355,30 @@ public abstract class GeneratedMessage extends AbstractMessage
// Noop for messages without extensions.
}
protected abstract Message.Builder newBuilderForType(BuilderParent parent);
/**
* 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 extends AbstractMessage.BuilderParent {}
/**
* 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 together with GeneratedMessage.BuilderParent.
*/
protected interface BuilderParent {
protected abstract Message.Builder newBuilderForType(BuilderParent parent);
/**
* 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.
*/
void markDirty();
@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();

@ -59,15 +59,15 @@ import java.util.Map;
*/
public abstract class GeneratedMessageLite<
MessageType extends GeneratedMessageLite<MessageType, BuilderType>,
BuilderType extends GeneratedMessageLite.Builder<MessageType, BuilderType>>
BuilderType extends GeneratedMessageLite.Builder<MessageType, BuilderType>>
extends AbstractMessageLite<MessageType, BuilderType> {
/** For use by generated code only. Lazily initialized to reduce allocations. */
protected UnknownFieldSetLite unknownFields = UnknownFieldSetLite.getDefaultInstance();
/** For use by generated code only. */
protected int memoizedSerializedSize = -1;
@Override
@SuppressWarnings("unchecked") // Guaranteed by runtime.
public final Parser<MessageType> getParserForType() {
@ -113,7 +113,7 @@ public abstract class GeneratedMessageLite<
}
return memoizedHashCode;
}
@SuppressWarnings("unchecked") // Guaranteed by runtime
int hashCode(HashCodeVisitor visitor) {
if (memoizedHashCode == 0) {
@ -125,18 +125,18 @@ public abstract class GeneratedMessageLite<
}
return memoizedHashCode;
}
@SuppressWarnings("unchecked") // Guaranteed by isInstance + runtime
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!getDefaultInstanceForType().getClass().isInstance(other)) {
return false;
}
try {
visit(EqualsVisitor.INSTANCE, (MessageType) other);
} catch (NotEqualsException e) {
@ -144,7 +144,7 @@ public abstract class GeneratedMessageLite<
}
return true;
}
/**
* Same as {@link #equals(Object)} but throws {@code NotEqualsException}.
*/
@ -153,7 +153,7 @@ public abstract class GeneratedMessageLite<
if (this == other) {
return true;
}
if (!getDefaultInstanceForType().getClass().isInstance(other)) {
return false;
}
@ -161,7 +161,7 @@ public abstract class GeneratedMessageLite<
visit(visitor, (MessageType) other);
return true;
}
// The general strategy for unknown fields is to use an UnknownFieldSetLite that is treated as
// mutable during the parsing constructor and immutable after. This allows us to avoid
// any unnecessary intermediary allocations while reducing the generated code size.
@ -174,10 +174,10 @@ public abstract class GeneratedMessageLite<
unknownFields = UnknownFieldSetLite.newInstance();
}
}
/**
* Called by subclasses to parse an unknown field. For use by generated code only.
*
*
* @return {@code true} unless the tag is an end-group tag.
*/
protected boolean parseUnknownField(int tag, CodedInputStream input) throws IOException {
@ -185,7 +185,7 @@ public abstract class GeneratedMessageLite<
if (WireFormat.getTagWireType(tag) == WireFormat.WIRETYPE_END_GROUP) {
return false;
}
ensureUnknownFieldsInitialized();
return unknownFields.mergeFieldFrom(tag, input);
}
@ -197,7 +197,7 @@ public abstract class GeneratedMessageLite<
ensureUnknownFieldsInitialized();
unknownFields.mergeVarintField(tag, value);
}
/**
* Called by subclasses to parse an unknown field. For use by generated code only.
*/
@ -205,7 +205,7 @@ public abstract class GeneratedMessageLite<
ensureUnknownFieldsInitialized();
unknownFields.mergeLengthDelimitedField(fieldNumber, value);
}
/**
* Called by subclasses to complete parsing. For use by generated code only.
*/
@ -292,7 +292,7 @@ public abstract class GeneratedMessageLite<
dynamicMethod(MethodToInvoke.VISIT, visitor, other);
unknownFields = visitor.visitUnknownFields(unknownFields, other.unknownFields);
}
/**
* Merge some unknown fields into the {@link UnknownFieldSetLite} for this
* message.
@ -359,9 +359,9 @@ public abstract class GeneratedMessageLite<
if (isBuilt) {
return instance;
}
instance.makeImmutable();
isBuilt = true;
return instance;
}
@ -374,24 +374,24 @@ public abstract class GeneratedMessageLite<
}
return result;
}
@Override
protected BuilderType internalMergeFrom(MessageType message) {
return mergeFrom(message);
}
/** All subclasses implement this. */
public BuilderType mergeFrom(MessageType message) {
copyOnWrite();
instance.visit(MergeFromVisitor.INSTANCE, message);
return (BuilderType) this;
}
@Override
public MessageType getDefaultInstanceForType() {
return defaultInstance;
}
@Override
public BuilderType mergeFrom(
com.google.protobuf.CodedInputStream input,
@ -466,12 +466,12 @@ public abstract class GeneratedMessageLite<
super.visit(visitor, other);
extensions = visitor.visitExtensions(extensions, other.extensions);
}
/**
* Parse an unknown field or an extension. For use by generated code only.
*
*
* <p>For use by generated code only.
*
*
* @return {@code true} unless the tag is an end-group tag.
*/
protected <MessageType extends MessageLite> boolean parseUnknownField(
@ -590,7 +590,7 @@ public abstract class GeneratedMessageLite<
return true;
}
private void verifyExtensionContainingType(
final GeneratedExtension<MessageType, ?> extension) {
if (extension.getContainingTypeDefaultInstance() !=
@ -607,7 +607,7 @@ public abstract class GeneratedMessageLite<
public final <Type> boolean hasExtension(final ExtensionLite<MessageType, Type> extension) {
GeneratedExtension<MessageType, Type> extensionLite =
checkIsLite(extension);
verifyExtensionContainingType(extensionLite);
return extensions.hasField(extensionLite.descriptor);
}
@ -618,7 +618,7 @@ public abstract class GeneratedMessageLite<
final ExtensionLite<MessageType, List<Type>> extension) {
GeneratedExtension<MessageType, List<Type>> extensionLite =
checkIsLite(extension);
verifyExtensionContainingType(extensionLite);
return extensions.getRepeatedFieldCount(extensionLite.descriptor);
}
@ -629,7 +629,7 @@ public abstract class GeneratedMessageLite<
public final <Type> Type getExtension(final ExtensionLite<MessageType, Type> extension) {
GeneratedExtension<MessageType, Type> extensionLite =
checkIsLite(extension);
verifyExtensionContainingType(extensionLite);
final Object value = extensions.getField(extensionLite.descriptor);
if (value == null) {
@ -660,7 +660,7 @@ public abstract class GeneratedMessageLite<
@Override
protected final void makeImmutable() {
super.makeImmutable();
extensions.makeImmutable();
}
@ -734,7 +734,7 @@ public abstract class GeneratedMessageLite<
implements ExtendableMessageOrBuilder<MessageType, BuilderType> {
protected ExtendableBuilder(MessageType defaultInstance) {
super(defaultInstance);
// TODO(dweis): This is kind of an unnecessary clone since we construct a
// new instance in the parent constructor which makes the extensions
// immutable. This extra allocation shouldn't matter in practice
@ -753,7 +753,7 @@ public abstract class GeneratedMessageLite<
if (!isBuilt) {
return;
}
super.copyOnWrite();
instance.extensions = instance.extensions.clone();
}
@ -814,14 +814,14 @@ public abstract class GeneratedMessageLite<
public BuilderType clone() {
return super.clone();
}
/** Set the value of an extension. */
public final <Type> BuilderType setExtension(
final ExtensionLite<MessageType, Type> extension,
final Type value) {
GeneratedExtension<MessageType, Type> extensionLite =
checkIsLite(extension);
verifyExtensionContainingType(extensionLite);
copyOnWrite();
instance.extensions.setField(extensionLite.descriptor, extensionLite.toFieldSetType(value));
@ -834,7 +834,7 @@ public abstract class GeneratedMessageLite<
final int index, final Type value) {
GeneratedExtension<MessageType, List<Type>> extensionLite =
checkIsLite(extension);
verifyExtensionContainingType(extensionLite);
copyOnWrite();
instance.extensions.setRepeatedField(
@ -848,7 +848,7 @@ public abstract class GeneratedMessageLite<
final Type value) {
GeneratedExtension<MessageType, List<Type>> extensionLite =
checkIsLite(extension);
verifyExtensionContainingType(extensionLite);
copyOnWrite();
instance.extensions.addRepeatedField(
@ -860,7 +860,7 @@ public abstract class GeneratedMessageLite<
public final <Type> BuilderType clearExtension(
final ExtensionLite<MessageType, ?> extension) {
GeneratedExtension<MessageType, ?> extensionLite = checkIsLite(extension);
verifyExtensionContainingType(extensionLite);
copyOnWrite();
instance.extensions.clearField(extensionLite.descriptor);
@ -1157,7 +1157,7 @@ public abstract class GeneratedMessageLite<
public static SerializedForm of(MessageLite message) {
return new SerializedForm(message);
}
private static final long serialVersionUID = 0L;
private final String messageClassName;
@ -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,8 +1200,35 @@ 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);
}
}
}
/**
* Checks that the {@link Extension} is Lite and returns it as a
* {@link GeneratedExtension}.
@ -1215,7 +1242,7 @@ public abstract class GeneratedMessageLite<
if (!extension.isLite()) {
throw new IllegalArgumentException("Expected a lite extension.");
}
return (GeneratedExtension<MessageType, T>) extension;
}
@ -1227,8 +1254,8 @@ public abstract class GeneratedMessageLite<
protected static final <T extends GeneratedMessageLite<T, ?>> boolean isInitialized(
T message, boolean shouldMemoize) {
return message.dynamicMethod(MethodToInvoke.IS_INITIALIZED, shouldMemoize) != null;
}
}
protected static final <T extends GeneratedMessageLite<T, ?>> void makeImmutable(T message) {
message.dynamicMethod(MethodToInvoke.MAKE_IMMUTABLE);
}
@ -1246,7 +1273,7 @@ public abstract class GeneratedMessageLite<
protected static LongList emptyLongList() {
return LongArrayList.emptyList();
}
protected static LongList mutableCopy(LongList list) {
int size = list.size();
return list.mutableCopyWithCapacity(
@ -1256,7 +1283,7 @@ public abstract class GeneratedMessageLite<
protected static FloatList emptyFloatList() {
return FloatArrayList.emptyList();
}
protected static FloatList mutableCopy(FloatList list) {
int size = list.size();
return list.mutableCopyWithCapacity(
@ -1266,7 +1293,7 @@ public abstract class GeneratedMessageLite<
protected static DoubleList emptyDoubleList() {
return DoubleArrayList.emptyList();
}
protected static DoubleList mutableCopy(DoubleList list) {
int size = list.size();
return list.mutableCopyWithCapacity(
@ -1276,7 +1303,7 @@ public abstract class GeneratedMessageLite<
protected static BooleanList emptyBooleanList() {
return BooleanArrayList.emptyList();
}
protected static BooleanList mutableCopy(BooleanList list) {
int size = list.size();
return list.mutableCopyWithCapacity(
@ -1286,7 +1313,7 @@ public abstract class GeneratedMessageLite<
protected static <E> ProtobufList<E> emptyProtobufList() {
return ProtobufArrayList.emptyList();
}
protected static <E> ProtobufList<E> mutableCopy(ProtobufList<E> list) {
int size = list.size();
return list.mutableCopyWithCapacity(
@ -1300,20 +1327,20 @@ public abstract class GeneratedMessageLite<
*/
protected static class DefaultInstanceBasedParser<T extends GeneratedMessageLite<T, ?>>
extends AbstractParser<T> {
private T defaultInstance;
public DefaultInstanceBasedParser(T defaultInstance) {
this.defaultInstance = defaultInstance;
}
@Override
public T parsePartialFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
return GeneratedMessageLite.parsePartialFrom(defaultInstance, input, extensionRegistry);
}
}
/**
* A static helper method for parsing a partial from input using the extension registry and the
* instance.
@ -1335,14 +1362,14 @@ public abstract class GeneratedMessageLite<
}
return result;
}
protected static <T extends GeneratedMessageLite<T, ?>> T parsePartialFrom(
T defaultInstance,
CodedInputStream input)
throws InvalidProtocolBufferException {
return parsePartialFrom(defaultInstance, input, ExtensionRegistryLite.getEmptyRegistry());
}
/**
* Helper method to check if message is initialized.
*
@ -1373,7 +1400,7 @@ public abstract class GeneratedMessageLite<
throws InvalidProtocolBufferException {
return checkMessageInitialized(parsePartialFrom(defaultInstance, data, extensionRegistry));
}
// This is a special case since we want to verify that the last tag is 0. We assume we exhaust the
// ByteString.
private static <T extends GeneratedMessageLite<T, ?>> T parsePartialFrom(
@ -1393,7 +1420,7 @@ public abstract class GeneratedMessageLite<
throw e;
}
}
// This is a special case since we want to verify that the last tag is 0. We assume we exhaust the
// ByteString.
private static <T extends GeneratedMessageLite<T, ?>> T parsePartialFrom(
@ -1477,7 +1504,7 @@ public abstract class GeneratedMessageLite<
return checkMessageInitialized(
parsePartialDelimitedFrom(defaultInstance, input, extensionRegistry));
}
private static <T extends GeneratedMessageLite<T, ?>> T parsePartialDelimitedFrom(
T defaultInstance,
InputStream input,
@ -1530,13 +1557,12 @@ public abstract class GeneratedMessageLite<
Object visitOneofLazyMessage(boolean minePresent, Object mine, Object other);
Object visitOneofMessage(boolean minePresent, Object mine, Object other);
void visitOneofNotSet(boolean minePresent);
/**
* 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);
@ -1686,7 +1712,7 @@ public abstract class GeneratedMessageLite<
}
throw NOT_EQUALS;
}
@Override
public Object visitOneofMessage(boolean minePresent, Object mine, Object other) {
if (minePresent && ((GeneratedMessageLite<?, ?>) mine).equals(this, (MessageLite) other)) {
@ -1694,7 +1720,7 @@ public abstract class GeneratedMessageLite<
}
throw NOT_EQUALS;
}
@Override
public void visitOneofNotSet(boolean minePresent) {
if (minePresent) {
@ -1716,13 +1742,17 @@ public abstract class GeneratedMessageLite<
return mine;
}
@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;
@ -1813,7 +1843,7 @@ public abstract class GeneratedMessageLite<
// The caller must ensure that the visitor is invoked parameterized with this and this such that
// other is this. This is required due to how oneof cases are handled. See the class comment
// on Visitor for more information.
private int hashCode = 0;
@Override
@ -1909,7 +1939,7 @@ public abstract class GeneratedMessageLite<
hashCode = (53 * hashCode) + mine.hashCode();
return mine;
}
@Override
public Object visitOneofMessage(boolean minePresent, Object mine, Object other) {
return visitMessage((MessageLite) mine, (MessageLite) other);
@ -1918,7 +1948,7 @@ public abstract class GeneratedMessageLite<
@Override
public void visitOneofNotSet(boolean minePresent) {
if (minePresent) {
throw new IllegalStateException(); // Can't happen if other == this.
throw new IllegalStateException(); // Can't happen if other == this.
}
}
@ -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;
}
@ -1996,7 +2031,7 @@ public abstract class GeneratedMessageLite<
hashCode = (53 * hashCode) + mine.hashCode();
return mine;
}
@Override
public <K, V> MapFieldLite<K, V> visitMap(MapFieldLite<K, V> mine, MapFieldLite<K, V> other) {
hashCode = (53 * hashCode) + mine.hashCode();
@ -2064,7 +2099,7 @@ public abstract class GeneratedMessageLite<
@Override
public Object visitOneofDouble(boolean minePresent, Object mine, Object other) {
return other;
return other;
}
@Override
@ -2074,29 +2109,26 @@ public abstract class GeneratedMessageLite<
@Override
public Object visitOneofLong(boolean minePresent, Object mine, Object other) {
return other;
return other;
}
@Override
public Object visitOneofString(boolean minePresent, Object mine, Object other) {
return other;
return other;
}
@Override
public Object visitOneofByteString(boolean minePresent, Object mine, Object other) {
return other;
return other;
}
@Override
public Object visitOneofLazyMessage(boolean minePresent, Object mine, Object other) {
if (minePresent) {
LazyFieldLite lazy = (LazyFieldLite) mine;
lazy.merge((LazyFieldLite) other);
return lazy;
}
return other;
LazyFieldLite lazy = minePresent ? (LazyFieldLite) mine : new LazyFieldLite();
lazy.merge((LazyFieldLite) other);
return lazy;
}
@Override
public Object visitOneofMessage(boolean minePresent, Object mine, Object other) {
if (minePresent) {
@ -2104,7 +2136,7 @@ public abstract class GeneratedMessageLite<
}
return other;
}
@Override
public void visitOneofNotSet(boolean minePresent) {
return;
@ -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?
mine.merge(other);
public LazyFieldLite visitLazyMessage(LazyFieldLite mine, LazyFieldLite other) {
if (other != null) {
if (mine == null) {
mine = new LazyFieldLite();
}
mine.merge(other);
}
return mine;
}
@ -2140,7 +2173,7 @@ public abstract class GeneratedMessageLite<
}
mine.addAll(other);
}
return size > 0 ? mine : other;
}
@ -2154,7 +2187,7 @@ public abstract class GeneratedMessageLite<
}
mine.addAll(other);
}
return size > 0 ? mine : other;
}
@ -2168,7 +2201,7 @@ public abstract class GeneratedMessageLite<
}
mine.addAll(other);
}
return size > 0 ? mine : other;
}
@ -2182,7 +2215,7 @@ public abstract class GeneratedMessageLite<
}
mine.addAll(other);
}
return size > 0 ? mine : other;
}
@ -2196,7 +2229,7 @@ public abstract class GeneratedMessageLite<
}
mine.addAll(other);
}
return size > 0 ? mine : other;
}
@ -2210,7 +2243,7 @@ public abstract class GeneratedMessageLite<
}
mine.addAll(other);
}
return size > 0 ? mine : other;
}
@ -2232,10 +2265,15 @@ public abstract class GeneratedMessageLite<
return other == UnknownFieldSetLite.getDefaultInstance()
? mine : UnknownFieldSetLite.mutableCopyOf(mine, other);
}
@Override
public <K, V> MapFieldLite<K, V> visitMap(MapFieldLite<K, V> mine, MapFieldLite<K, V> other) {
mine.mergeFrom(other);
if (!other.isEmpty()) {
if (!mine.isMutable()) {
mine = mine.mutableCopy();
}
mine.mergeFrom(other);
}
return mine;
}
}

@ -38,25 +38,27 @@ import java.util.RandomAccess;
/**
* An implementation of {@link IntList} on top of a primitive array.
*
*
* @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 {
EMPTY_LIST.makeImmutable();
}
public static IntArrayList emptyList() {
return EMPTY_LIST;
}
/**
* The backing store for the list.
*/
private int[] array;
/**
* The size of the list distinct from the length of the array. That is, it is the number of
* elements set in the list.
@ -71,13 +73,14 @@ 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;
}
@Override
public boolean equals(Object o) {
if (this == o) {
@ -90,14 +93,14 @@ final class IntArrayList extends AbstractProtobufList<Integer> implements IntLis
if (size != other.size) {
return false;
}
final int[] arr = other.array;
for (int i = 0; i < size; i++) {
if (array[i] != arr[i]) {
return false;
}
}
return true;
}
@ -117,7 +120,7 @@ final class IntArrayList extends AbstractProtobufList<Integer> implements IntLis
}
return new IntArrayList(Arrays.copyOf(array, capacity), size);
}
@Override
public Integer get(int index) {
return getInt(index);
@ -169,7 +172,7 @@ final class IntArrayList extends AbstractProtobufList<Integer> implements IntLis
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index));
}
if (size < array.length) {
// Shift everything over to make room
System.arraycopy(array, index, array, index + 1, size - index);
@ -177,10 +180,10 @@ final class IntArrayList extends AbstractProtobufList<Integer> implements IntLis
// Resize to 1.5x the size
int length = ((size * 3) / 2) + 1;
int[] newArray = new int[length];
// Copy the first part directly
System.arraycopy(array, 0, newArray, 0, index);
// Copy the rest shifted over by one to make room
System.arraycopy(array, index, newArray, index + 1, size - index);
array = newArray;
@ -194,38 +197,38 @@ final class IntArrayList extends AbstractProtobufList<Integer> implements IntLis
@Override
public boolean addAll(Collection<? extends Integer> collection) {
ensureIsMutable();
if (collection == null) {
throw new NullPointerException();
}
// We specialize when adding another IntArrayList to avoid boxing elements.
if (!(collection instanceof IntArrayList)) {
return super.addAll(collection);
}
IntArrayList list = (IntArrayList) collection;
if (list.size == 0) {
return false;
}
int overflow = Integer.MAX_VALUE - size;
if (overflow < list.size) {
// We can't actually represent a list this large.
throw new OutOfMemoryError();
}
int newSize = size + list.size;
if (newSize > array.length) {
array = Arrays.copyOf(array, newSize);
}
System.arraycopy(list.array, 0, array, size, list.size);
size = newSize;
modCount++;
return true;
}
@Override
public boolean remove(Object o) {
ensureIsMutable();
@ -254,7 +257,7 @@ final class IntArrayList extends AbstractProtobufList<Integer> implements IntLis
/**
* Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an
* {@link IndexOutOfBoundsException} if it is not.
*
*
* @param index the index to verify is in range
*/
private void ensureIndexInRange(int index) {

@ -38,25 +38,27 @@ import java.util.RandomAccess;
/**
* An implementation of {@link LongList} on top of a primitive array.
*
*
* @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 {
EMPTY_LIST.makeImmutable();
}
public static LongArrayList emptyList() {
return EMPTY_LIST;
}
/**
* The backing store for the list.
*/
private long[] array;
/**
* The size of the list distinct from the length of the array. That is, it is the number of
* elements set in the list.
@ -71,33 +73,34 @@ 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;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof IntArrayList)) {
if (!(o instanceof LongArrayList)) {
return super.equals(o);
}
LongArrayList other = (LongArrayList) o;
if (size != other.size) {
return false;
}
final long[] arr = other.array;
for (int i = 0; i < size; i++) {
if (array[i] != arr[i]) {
return false;
}
}
return true;
}
@ -117,7 +120,7 @@ final class LongArrayList extends AbstractProtobufList<Long> implements LongList
}
return new LongArrayList(Arrays.copyOf(array, capacity), size);
}
@Override
public Long get(int index) {
return getLong(index);
@ -169,7 +172,7 @@ final class LongArrayList extends AbstractProtobufList<Long> implements LongList
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index));
}
if (size < array.length) {
// Shift everything over to make room
System.arraycopy(array, index, array, index + 1, size - index);
@ -177,10 +180,10 @@ final class LongArrayList extends AbstractProtobufList<Long> implements LongList
// Resize to 1.5x the size
int length = ((size * 3) / 2) + 1;
long[] newArray = new long[length];
// Copy the first part directly
System.arraycopy(array, 0, newArray, 0, index);
// Copy the rest shifted over by one to make room
System.arraycopy(array, index, newArray, index + 1, size - index);
array = newArray;
@ -194,38 +197,38 @@ final class LongArrayList extends AbstractProtobufList<Long> implements LongList
@Override
public boolean addAll(Collection<? extends Long> collection) {
ensureIsMutable();
if (collection == null) {
throw new NullPointerException();
}
// We specialize when adding another LongArrayList to avoid boxing elements.
if (!(collection instanceof LongArrayList)) {
return super.addAll(collection);
}
LongArrayList list = (LongArrayList) collection;
if (list.size == 0) {
return false;
}
int overflow = Integer.MAX_VALUE - size;
if (overflow < list.size) {
// We can't actually represent a list this large.
throw new OutOfMemoryError();
}
int newSize = size + list.size;
if (newSize > array.length) {
array = Arrays.copyOf(array, newSize);
}
System.arraycopy(list.array, 0, array, size, list.size);
size = newSize;
modCount++;
return true;
}
@Override
public boolean remove(Object o) {
ensureIsMutable();
@ -254,7 +257,7 @@ final class LongArrayList extends AbstractProtobufList<Long> implements LongList
/**
* Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an
* {@link IndexOutOfBoundsException} if it is not.
*
*
* @param index the index to verify is in range
*/
private void ensureIndexInRange(int index) {

@ -41,63 +41,83 @@ import java.util.TreeMap;
/**
* Implements MapEntry messages.
*
*
* In reflection API, map fields will be treated as repeated message fields and
* each map entry is accessed as a message. This MapEntry class is used to
* represent these map entry messages in reflection API.
*
*
* Protobuf internal. Users shouldn't use this class.
*/
public final class MapEntry<K, V> extends AbstractMessage {
private static class Metadata<K, V> {
public final Descriptor descriptor;
public final MapEntry<K, V> defaultInstance;
public final AbstractParser<MapEntry<K, V>> parser;
private static final class Metadata<K, V> extends MapEntryLite.Metadata<K, V> {
public final Descriptor descriptor;
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 new MapEntry message. */
private MapEntry(Metadata<K, V> metadata, MapEntryLite<K, V> data) {
/** 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;
this.data = data;
}
/** Parsing constructor. */
private MapEntry(
Metadata<K, V> metadata,
CodedInputStream input,
ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
try {
this.metadata = metadata;
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);
}
}
/**
* Create a default MapEntry instance. A default MapEntry instance should be
* created only once for each map entry message type. Generated code should
* store the created default instance and use it later to create new MapEntry
* messages of the same type.
* messages of the same type.
*/
public static <K, V> MapEntry<K, V> newDefaultInstance(
Descriptor descriptor,
@ -106,30 +126,38 @@ public final class MapEntry<K, V> extends AbstractMessage {
return new MapEntry<K, V>(
descriptor, keyType, defaultKey, valueType, defaultValue);
}
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
public Parser<MapEntry<K, V>> getParserForType() {
return metadata.parser;
@ -139,15 +167,15 @@ public final class MapEntry<K, V> extends AbstractMessage {
public Builder<K, V> newBuilderForType() {
return new Builder<K, V>(metadata);
}
@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));
@ -166,12 +193,12 @@ public final class MapEntry<K, V> extends AbstractMessage {
}
return Collections.unmodifiableMap(result);
}
private void checkFieldDescriptor(FieldDescriptor field) {
if (field.getContainingType() != metadata.descriptor) {
throw new RuntimeException(
"Wrong FieldDescriptor \"" + field.getFullName()
+ "\" used in message \"" + metadata.descriptor.getFullName());
+ "\" used in message \"" + metadata.descriptor.getFullName());
}
}
@ -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,29 +296,24 @@ 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
public Descriptor getDescriptorForType() {
return metadata.descriptor;
}
private void checkFieldDescriptor(FieldDescriptor field) {
if (field.getContainingType() != metadata.descriptor) {
throw new RuntimeException(
"Wrong FieldDescriptor \"" + field.getFullName()
+ "\" used in message \"" + metadata.descriptor.getFullName());
+ "\" used in message \"" + metadata.descriptor.getFullName());
}
}
@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;
}
@ -409,13 +413,13 @@ public final class MapEntry<K, V> extends AbstractMessage {
throw new RuntimeException(
"There is no repeated field in a map entry message.");
}
@Override
public Object getRepeatedField(FieldDescriptor field, int index) {
throw new RuntimeException(
"There is no repeated field in a map entry message.");
}
@Override
public UnknownFieldSet getUnknownFields() {
return UnknownFieldSet.getDefaultInstance();
@ -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,80 +31,74 @@
package com.google.protobuf;
import java.io.IOException;
import java.util.AbstractMap;
import java.util.Map;
/**
* Implements the lite version of map entry messages.
*
*
* This class serves as an utility class to help do serialization/parsing of
* map entries. It's used in generated code and also in the full version
* MapEntry message.
*
*
* Protobuf internal. Users shouldn't use.
*/
public class MapEntryLite<K, V>
extends AbstractMessageLite<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;
}
}
private static final int KEY_FIELD_NUMBER = 1;
private static final int VALUE_FIELD_NUMBER = 2;
private final Metadata<K, V> metadata;
private final K key;
private final V value;
/** Creates a default MapEntryLite message instance. */
private MapEntryLite(
WireFormat.FieldType keyType, K defaultKey,
WireFormat.FieldType valueType, V defaultValue) {
this.metadata = new Metadata<K, V>(this, keyType, valueType);
this.metadata = new Metadata<K, V>(keyType, defaultKey, valueType, defaultValue);
this.key = defaultKey;
this.value = defaultValue;
}
/** Creates a new MapEntryLite message. */
private MapEntryLite(Metadata<K, V> metadata, K key, V value) {
this.metadata = metadata;
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
/**
* Creates a default MapEntryLite message instance.
*
*
* This method is used by generated code to create the default instance for
* a map entry message. The created default instance should be used to create
* new map entry messages of the same type. For each map entry message, only
* one default instance should be created.
* one default instance should be created.
*/
public static <K, V> MapEntryLite<K, V> newDefaultInstance(
WireFormat.FieldType keyType, K defaultKey,
@ -112,80 +106,20 @@ public class MapEntryLite<K, V>
return new MapEntryLite<K, V>(
keyType, defaultKey, valueType, defaultValue);
}
@Override
public void writeTo(CodedOutputStream output) throws IOException {
writeField(KEY_FIELD_NUMBER, metadata.keyType, key, output);
writeField(VALUE_FIELD_NUMBER, metadata.valueType, value, output);
}
private void writeField(
int number, WireFormat.FieldType type, Object value,
CodedOutputStream output) throws IOException {
output.writeTag(number, type.getWireType());
FieldSet.writeElementNoTag(output, type, value);
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);
}
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;
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);
}
private int getFieldSize(
int number, WireFormat.FieldType type, Object value) {
return CodedOutputStream.computeTagSize(number)
+ FieldSet.computeElementSizeNoTag(type, value);
}
/** Parsing constructor. */
private MapEntryLite(
Metadata<K, V> metadata,
CodedInputStream input,
ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
try {
K key = metadata.defaultInstance.key;
V value = metadata.defaultInstance.value;
while (true) {
int tag = input.readTag();
if (tag == 0) {
break;
}
if (tag == WireFormat.makeTag(
KEY_FIELD_NUMBER, metadata.keyType.getWireType())) {
key = mergeField(
input, extensionRegistry, metadata.keyType, key);
} else if (tag == WireFormat.makeTag(
VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) {
value = mergeField(
input, extensionRegistry, metadata.valueType, value);
} else {
if (!input.skipField(tag)) {
break;
}
}
}
this.metadata = metadata;
this.key = key;
this.value = value;
} catch (InvalidProtocolBufferException e) {
throw e.setUnfinishedMessage(this);
} catch (IOException e) {
throw new InvalidProtocolBufferException(e.getMessage())
.setUnfinishedMessage(this);
}
}
@SuppressWarnings("unchecked")
private <T> T mergeField(
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);
/**
* 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 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);
}
@Override
public MapEntryLite<K, V> getDefaultInstanceForType() {
return metadata.defaultInstance;
/**
* 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));
}
@Override
public boolean isInitialized() {
if (metadata.valueType.getJavaType() == WireFormat.JavaType.MESSAGE) {
return ((MessageLite) value).isInitialized();
/**
* 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);
}
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;
}
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;
}
}
}
return true;
return new AbstractMap.SimpleImmutableEntry<K, V>(key, value);
}
/**
* Builder used to create {@link MapEntryLite} messages.
* 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 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 Builder<K, V> setValue(V value) {
this.value = value;
return this;
}
public Builder<K, V> clearKey() {
this.key = metadata.defaultInstance.key;
return this;
}
public Builder<K, V> clearValue() {
this.value = metadata.defaultInstance.value;
return this;
}
@Override
public Builder<K, V> clear() {
this.key = metadata.defaultInstance.key;
this.value = metadata.defaultInstance.value;
return this;
}
@Override
public MapEntryLite<K, V> build() {
MapEntryLite<K, V> result = buildPartial();
if (!result.isInitialized()) {
throw newUninitializedMessageException(result);
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;
while (true) {
int tag = input.readTag();
if (tag == 0) {
break;
}
return result;
}
@Override
public MapEntryLite<K, V> buildPartial() {
return new MapEntryLite<K, V>(metadata, key, value);
}
@Override
public MessageLite getDefaultInstanceForType() {
return metadata.defaultInstance;
}
@Override
public boolean isInitialized() {
if (metadata.valueType.getJavaType() == WireFormat.JavaType.MESSAGE) {
return ((MessageLite) value).isInitialized();
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;
}
}
return true;
}
private Builder(Metadata<K, V> metadata, K key, V value) {
this.metadata = metadata;
this.key = key;
this.value = value;
}
@Override
public Builder<K, V> clone() {
return new Builder<K, V>(metadata, key, value);
}
@Override
public Builder<K, V> mergeFrom(
CodedInputStream input, ExtensionRegistryLite extensionRegistry)
throws IOException {
MapEntryLite<K, V> entry =
new MapEntryLite<K, V>(metadata, input, extensionRegistry);
this.key = entry.key;
this.value = entry.value;
return this;
}
@Override
protected Builder<K, V> internalMergeFrom(MapEntryLite<K, V> message) {
throw new UnsupportedOperationException();
}
input.checkLastTagWas(0);
input.popLimit(oldLimit);
map.put(key, value);
}
}

@ -30,25 +30,26 @@
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.
*
*
* This class supports accessing the map field as a {@link Map} to be used in
* generated API and also supports accessing the field as a {@link List} to be
* used in reflection API. It keeps track of where the data is currently stored
* and do necessary conversions between map and list.
*
* and do necessary conversions between map and list.
*
* This class is a protobuf implementation detail. Users shouldn't use this
* class directly.
*
*
* THREAD-SAFETY NOTE: Read-only access is thread-safe. Users can call getMap()
* and getList() concurrently in multiple threads. If write-access is needed,
* all access must be synchronized.
@ -56,21 +57,21 @@ import java.util.Map;
public class MapField<K, V> implements MutabilityOracle {
/**
* Indicates where the data of this map field is currently stored.
*
*
* MAP: Data is stored in mapData.
* LIST: Data is stored in listData.
* BOTH: mapData and listData have the same data.
*
* When the map field is accessed (through generated API or reflection API),
* it will shift between these 3 modes:
*
*
* getMap() getList() getMutableMap() getMutableList()
* MAP MAP BOTH MAP LIST
* LIST BOTH LIST MAP LIST
* BOTH BOTH BOTH MAP LIST
*
*
* As the map field changes its mode, the list/map reference returned in a
* previous method call may be invalidated.
* previous method call may be invalidated.
*/
private enum StorageMode {MAP, LIST, BOTH}
@ -78,26 +79,26 @@ public class MapField<K, V> implements MutabilityOracle {
private volatile StorageMode mode;
private MutatabilityAwareMap<K, V> mapData;
private List<Message> listData;
// Convert between a map entry Message and a key-value pair.
private static interface Converter<K, V> {
Message convertKeyAndValueToMessage(K key, V value);
void convertMessageToKeyAndValue(Message message, Map<K, V> map);
Message getMessageDefaultInstance();
}
private static class ImmutableMessageConverter<K, V> implements Converter<K, V> {
private final MapEntry<K, V> defaultEntry;
public ImmutableMessageConverter(MapEntry<K, V> defaultEntry) {
this.defaultEntry = defaultEntry;
}
@Override
public Message convertKeyAndValueToMessage(K key, V value) {
return defaultEntry.newBuilderForType().setKey(key).setValue(value).buildPartial();
}
@Override
public void convertMessageToKeyAndValue(Message message, Map<K, V> map) {
MapEntry<K, V> entry = (MapEntry<K, V>) message;
@ -109,10 +110,10 @@ public class MapField<K, V> implements MutabilityOracle {
return defaultEntry;
}
}
private final Converter<K, V> converter;
private MapField(
Converter<K, V> converter,
StorageMode mode,
@ -123,34 +124,34 @@ public class MapField<K, V> implements MutabilityOracle {
this.mapData = new MutatabilityAwareMap<K, V>(this, mapData);
this.listData = null;
}
private MapField(
MapEntry<K, V> defaultEntry,
StorageMode mode,
Map<K, V> mapData) {
this(new ImmutableMessageConverter<K, V>(defaultEntry), mode, mapData);
}
/** Returns an immutable empty MapField. */
public static <K, V> MapField<K, V> emptyMapField(
MapEntry<K, V> defaultEntry) {
return new MapField<K, V>(
defaultEntry, StorageMode.MAP, Collections.<K, V>emptyMap());
}
/** Creates a new mutable empty MapField. */
public static <K, V> MapField<K, V> newMapField(MapEntry<K, V> defaultEntry) {
return new MapField<K, V>(
defaultEntry, StorageMode.MAP, new LinkedHashMap<K, V>());
}
private Message convertKeyAndValueToMessage(K key, V value) {
return converter.convertKeyAndValueToMessage(key, value);
}
@SuppressWarnings("unchecked")
private void convertMessageToKeyAndValue(Message message, Map<K, V> map) {
converter.convertMessageToKeyAndValue(message, map);
@ -173,7 +174,7 @@ public class MapField<K, V> implements MutabilityOracle {
}
return new MutatabilityAwareMap<K, V>(this, mapData);
}
/** Returns the content of this MapField as a read-only Map. */
public Map<K, V> getMap() {
if (mode == StorageMode.LIST) {
@ -186,7 +187,7 @@ public class MapField<K, V> implements MutabilityOracle {
}
return Collections.unmodifiableMap(mapData);
}
/** Gets a mutable Map view of this MapField. */
public Map<K, V> getMutableMap() {
if (mode != StorageMode.MAP) {
@ -194,20 +195,20 @@ public class MapField<K, V> implements MutabilityOracle {
mapData = convertListToMap(listData);
}
listData = null;
mode = StorageMode.MAP;
mode = StorageMode.MAP;
}
return mapData;
}
public void mergeFrom(MapField<K, V> other) {
getMutableMap().putAll(MapFieldLite.copy(other.getMap()));
}
public void clear() {
mapData = new MutatabilityAwareMap<K, V>(this, new LinkedHashMap<K, V>());
mode = StorageMode.MAP;
}
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object object) {
@ -217,18 +218,18 @@ public class MapField<K, V> implements MutabilityOracle {
MapField<K, V> other = (MapField<K, V>) object;
return MapFieldLite.<K, V>equals(getMap(), other.getMap());
}
@Override
public int hashCode() {
return MapFieldLite.<K, V>calculateHashCodeForMap(getMap());
}
/** Returns a deep copy of this MapField. */
public MapField<K, V> copy() {
return new MapField<K, V>(
converter, StorageMode.MAP, MapFieldLite.copy(getMap()));
}
/** Gets the content of this MapField as a read-only List. */
List<Message> getList() {
if (mode == StorageMode.MAP) {
@ -241,7 +242,7 @@ public class MapField<K, V> implements MutabilityOracle {
}
return Collections.unmodifiableList(listData);
}
/** Gets a mutable List view of this MapField. */
List<Message> getMutableList() {
if (mode != StorageMode.LIST) {
@ -253,7 +254,7 @@ public class MapField<K, V> implements MutabilityOracle {
}
return listData;
}
/**
* Gets the default instance of the message stored in the list view of this
* map field.
@ -261,7 +262,7 @@ public class MapField<K, V> implements MutabilityOracle {
Message getMapEntryMessageDefaultInstance() {
return converter.getMessageDefaultInstance();
}
/**
* Makes this list immutable. All subsequent modifications will throw an
* {@link UnsupportedOperationException}.
@ -269,14 +270,14 @@ public class MapField<K, V> implements MutabilityOracle {
public void makeImmutable() {
isMutable = false;
}
/**
* Returns whether this field can be modified.
*/
public boolean isMutable() {
return isMutable;
}
/* (non-Javadoc)
* @see com.google.protobuf.MutabilityOracle#ensureMutable()
*/
@ -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,71 +33,85 @@ 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;
/**
* Internal representation of map fields in generated lite-runtime messages.
*
*
* This class is a protobuf implementation detail. Users shouldn't use this
* class directly.
*/
public 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();
}
/** Returns an singleton immutable empty MapFieldLite instance. */
@SuppressWarnings({"unchecked", "cast"})
public static <K, V> MapFieldLite<K, V> emptyMapField() {
return (MapFieldLite<K, V>) EMPTY_MAP_FIELD;
}
/** Creates a new MapFieldLite instance. */
public static <K, V> MapFieldLite<K, V> newMapField() {
return new MapFieldLite<K, V>(new LinkedHashMap<K, V>());
public void mergeFrom(MapFieldLite<K, V> other) {
ensureMutable();
if (!other.isEmpty()) {
putAll(other);
}
}
@SuppressWarnings({"unchecked", "cast"})
@Override public Set<Map.Entry<K, V>> entrySet() {
return isEmpty() ? Collections.<Map.Entry<K, V>>emptySet() : super.entrySet();
}
@Override public void clear() {
ensureMutable();
clear();
}
/** Gets the content of this MapField as a read-only Map. */
public Map<K, V> getMap() {
return Collections.unmodifiableMap(mapData);
@Override public V put(K key, V value) {
ensureMutable();
return super.put(key, value);
}
/** Gets a mutable Map view of this MapField. */
public Map<K, V> getMutableMap() {
return mapData;
public V put(Map.Entry<K, V> entry) {
return put(entry.getKey(), entry.getValue());
}
public void mergeFrom(MapFieldLite<K, V> other) {
mapData.putAll(copy(other.mapData));
@Override public void putAll(Map<? extends K, ? extends V> m) {
ensureMutable();
super.putAll(m);
}
public void clear() {
mapData.clear();
@Override public V remove(Object key) {
ensureMutable();
return super.remove(key);
}
private static boolean equals(Object a, Object b) {
if (a instanceof byte[] && b instanceof byte[]) {
return Arrays.equals((byte[]) a, (byte[]) b);
}
return a.equals(b);
}
/**
* Checks whether two {@link Map}s are equal. We don't use the default equals
* method of {@link Map} because it compares by identity not by content for
@ -120,20 +134,16 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
}
return true;
}
/**
* Checks whether two map fields are equal.
*/
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object object) {
if (!(object instanceof MapFieldLite)) {
return false;
}
MapFieldLite<K, V> other = (MapFieldLite<K, V>) object;
return equals(mapData, other.mapData);
return (object instanceof Map) && equals(this, (Map<K, V>) object);
}
private static int calculateHashCodeForObject(Object a) {
if (a instanceof byte[]) {
return Internal.hashCode((byte[]) a);
@ -156,14 +166,14 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
result += calculateHashCodeForObject(entry.getKey())
^ calculateHashCodeForObject(entry.getValue());
}
return result;
return result;
}
@Override
public int hashCode() {
return calculateHashCodeForMap(mapData);
return calculateHashCodeForMap(this);
}
private static Object copy(Object object) {
if (object instanceof byte[]) {
byte[] data = (byte[]) object;
@ -171,7 +181,7 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
}
return object;
}
/**
* Makes a deep copy of a {@link Map}. Immutable objects in the map will be
* shared (e.g., integers, strings, immutable messages) and mutable ones will
@ -185,12 +195,12 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
}
return result;
}
/** 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);
}
/**
* Makes this field immutable. All subsequent modifications will throw an
* {@link UnsupportedOperationException}.
@ -198,352 +208,17 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
public void makeImmutable() {
isMutable = false;
}
/**
* Returns whether this field can be modified.
*/
public boolean isMutable() {
return isMutable;
}
@Override
public 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.
private void ensureMutable() {
if (!isMutable()) {
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() {}

@ -40,30 +40,31 @@ import java.util.Iterator;
/**
* Tests for {@link BooleanArrayList}.
*
*
* @author dweis@google.com (Daniel Weis)
*/
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;
@Override
protected void setUp() throws Exception {
list = new BooleanArrayList();
}
public void testEmptyListReturnsSameInstance() {
assertSame(BooleanArrayList.emptyList(), BooleanArrayList.emptyList());
}
public void testEmptyListIsImmutable() {
assertImmutable(BooleanArrayList.emptyList());
}
public void testMakeImmutable() {
list.addBoolean(true);
list.addBoolean(false);
@ -72,16 +73,16 @@ public class BooleanArrayListTest extends TestCase {
list.makeImmutable();
assertImmutable(list);
}
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));
assertEquals(true, (boolean) iterator.next());
list.set(0, true);
assertEquals(false, (boolean) iterator.next());
list.remove(0);
try {
iterator.next();
@ -89,7 +90,7 @@ public class BooleanArrayListTest extends TestCase {
} catch (ConcurrentModificationException e) {
// expected
}
iterator = list.iterator();
list.add(0, false);
try {
@ -99,19 +100,19 @@ public class BooleanArrayListTest extends TestCase {
// expected
}
}
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);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
try {
TERTIARY_LIST.get(3);
fail();
@ -119,19 +120,19 @@ public class BooleanArrayListTest extends TestCase {
// expected
}
}
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);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
try {
TERTIARY_LIST.get(3);
fail();
@ -139,7 +140,7 @@ public class BooleanArrayListTest extends TestCase {
// expected
}
}
public void testSize() {
assertEquals(0, BooleanArrayList.emptyList().size());
assertEquals(1, UNARY_LIST.size());
@ -150,26 +151,26 @@ public class BooleanArrayListTest extends TestCase {
list.addBoolean(false);
list.addBoolean(false);
assertEquals(4, list.size());
list.remove(0);
assertEquals(3, list.size());
list.add(true);
assertEquals(4, list.size());
}
public void testSet() {
list.addBoolean(false);
list.addBoolean(false);
assertEquals(false, (boolean) list.set(0, true));
assertEquals(true, list.getBoolean(0));
assertEquals(false, (boolean) list.set(1, false));
assertEquals(false, list.getBoolean(1));
try {
list.set(-1, true);
list.set(-1, false);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
@ -182,17 +183,17 @@ public class BooleanArrayListTest extends TestCase {
// expected
}
}
public void testSetInt() {
public void testSetBoolean() {
list.addBoolean(true);
list.addBoolean(true);
assertEquals(true, list.setBoolean(0, false));
assertEquals(false, list.getBoolean(0));
assertEquals(true, list.setBoolean(1, false));
assertEquals(false, list.getBoolean(1));
try {
list.setBoolean(-1, false);
fail();
@ -201,76 +202,78 @@ public class BooleanArrayListTest extends TestCase {
}
try {
list.setBoolean(2, true);
list.setBoolean(2, false);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
}
public void testAdd() {
assertEquals(0, list.size());
assertTrue(list.add(true));
assertEquals(asList(true), list);
assertTrue(list.add(false));
assertEquals(asList(false), list);
assertTrue(list.add(true));
list.add(0, false);
assertEquals(asList(false, true, false), list);
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
}
try {
list.add(4, true);
} catch (IndexOutOfBoundsException e) {
// expected
}
}
public void testAddInt() {
assertEquals(0, list.size());
list.addBoolean(true);
assertEquals(asList(true), list);
public void testAddBoolean() {
assertEquals(0, list.size());
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));
assertTrue(list.addAll(asList(true, false, false, false, true)));
assertEquals(asList(false, true, false, false, false, true), list);
assertEquals(true, (boolean) list.get(0));
assertEquals(true, list.getBoolean(0));
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()));
}
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);
@ -280,92 +283,93 @@ public class BooleanArrayListTest extends TestCase {
assertEquals(false, (boolean) list.remove(0));
assertEquals(asList(), list);
try {
list.remove(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
try {
list.remove(0);
} catch (IndexOutOfBoundsException e) {
// expected
}
}
private void assertImmutable(BooleanArrayList list) {
try {
list.add(false);
list.add(true);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.add(0, true);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(Collections.<Boolean>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(Collections.singletonList(false));
list.addAll(Collections.singletonList(true));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(new BooleanArrayList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(0, Collections.singleton(true));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(0, UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(0, Collections.<Boolean>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addBoolean(true);
list.addBoolean(false);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.clear();
fail();
@ -379,63 +383,63 @@ public class BooleanArrayListTest extends TestCase {
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.remove(new Object());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.removeAll(Collections.<Boolean>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.removeAll(Collections.singleton(Boolean.TRUE));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.removeAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.retainAll(Collections.<Boolean>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.retainAll(Collections.singleton(Boolean.TRUE));
list.removeAll(Collections.singleton(Boolean.TRUE));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.retainAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.set(0, true);
list.set(0, false);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.setBoolean(0, false);
fail();
@ -443,7 +447,7 @@ public class BooleanArrayListTest extends TestCase {
// expected
}
}
private static BooleanArrayList newImmutableBooleanArrayList(boolean... elements) {
BooleanArrayList list = new BooleanArrayList();
for (boolean element : elements) {

@ -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();

@ -40,39 +40,40 @@ import java.util.Iterator;
/**
* Tests for {@link DoubleArrayList}.
*
*
* @author dweis@google.com (Daniel Weis)
*/
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);
private DoubleArrayList list;
@Override
protected void setUp() throws Exception {
list = new DoubleArrayList();
}
public void testEmptyListReturnsSameInstance() {
assertSame(DoubleArrayList.emptyList(), DoubleArrayList.emptyList());
}
public void testEmptyListIsImmutable() {
assertImmutable(DoubleArrayList.emptyList());
}
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);
}
public void testModificationWithIteration() {
list.addAll(asList(1D, 2D, 3D, 4D));
Iterator<Double> iterator = list.iterator();
@ -81,7 +82,7 @@ public class DoubleArrayListTest extends TestCase {
assertEquals(1D, (double) iterator.next());
list.set(0, 1D);
assertEquals(2D, (double) iterator.next());
list.remove(0);
try {
iterator.next();
@ -89,7 +90,7 @@ public class DoubleArrayListTest extends TestCase {
} catch (ConcurrentModificationException e) {
// expected
}
iterator = list.iterator();
list.add(0, 0D);
try {
@ -99,19 +100,19 @@ public class DoubleArrayListTest extends TestCase {
// expected
}
}
public void testGet() {
assertEquals(1D, (double) TERTIARY_LIST.get(0));
assertEquals(2D, (double) TERTIARY_LIST.get(1));
assertEquals(3D, (double) TERTIARY_LIST.get(2));
try {
TERTIARY_LIST.get(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
try {
TERTIARY_LIST.get(3);
fail();
@ -119,19 +120,19 @@ public class DoubleArrayListTest extends TestCase {
// expected
}
}
public void testGetInt() {
public void testGetDouble() {
assertEquals(1D, TERTIARY_LIST.getDouble(0));
assertEquals(2D, TERTIARY_LIST.getDouble(1));
assertEquals(3D, TERTIARY_LIST.getDouble(2));
try {
TERTIARY_LIST.get(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
try {
TERTIARY_LIST.get(3);
fail();
@ -139,35 +140,35 @@ public class DoubleArrayListTest extends TestCase {
// expected
}
}
public void testSize() {
assertEquals(0, DoubleArrayList.emptyList().size());
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);
assertEquals(4, list.size());
list.remove(0);
assertEquals(3, list.size());
list.add(16D);
list.add(17D);
assertEquals(4, list.size());
}
public void testSet() {
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));
try {
list.set(-1, 0D);
fail();
@ -182,17 +183,17 @@ public class DoubleArrayListTest extends TestCase {
// expected
}
}
public void testSetInt() {
list.addDouble(2);
list.addDouble(4);
assertEquals(2D, list.setDouble(0, 0));
public void testSetDouble() {
list.addDouble(1);
list.addDouble(3);
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 {
list.setDouble(-1, 0);
fail();
@ -207,7 +208,7 @@ public class DoubleArrayListTest extends TestCase {
// expected
}
}
public void testAdd() {
assertEquals(0, list.size());
@ -217,29 +218,31 @@ public class DoubleArrayListTest extends TestCase {
assertTrue(list.add(3D));
list.add(0, 4D);
assertEquals(asList(4D, 2D, 3D), list);
list.add(0, 1D);
list.add(0, 0D);
// Force a resize by getting up to 11 elements.
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);
} catch (IndexOutOfBoundsException e) {
// expected
}
try {
list.add(4, 5D);
} catch (IndexOutOfBoundsException e) {
// expected
}
}
public void testAddInt() {
public void testAddDouble() {
assertEquals(0, list.size());
list.addDouble(2);
@ -248,7 +251,7 @@ public class DoubleArrayListTest extends TestCase {
list.addDouble(3);
assertEquals(asList(2D, 3D), list);
}
public void testAddAll() {
assertEquals(0, list.size());
@ -256,17 +259,17 @@ public class DoubleArrayListTest extends TestCase {
assertEquals(1, list.size());
assertEquals(1D, (double) list.get(0));
assertEquals(1D, list.getDouble(0));
assertTrue(list.addAll(asList(2D, 3D, 4D, 5D, 6D)));
assertEquals(asList(1D, 2D, 3D, 4D, 5D, 6D), list);
assertTrue(list.addAll(TERTIARY_LIST));
assertEquals(asList(1D, 2D, 3D, 4D, 5D, 6D, 1D, 2D, 3D), list);
assertFalse(list.addAll(Collections.<Double>emptyList()));
assertFalse(list.addAll(DoubleArrayList.emptyList()));
}
public void testRemove() {
list.addAll(TERTIARY_LIST);
assertEquals(1D, (double) list.remove(0));
@ -280,96 +283,96 @@ public class DoubleArrayListTest extends TestCase {
assertEquals(2D, (double) list.remove(0));
assertEquals(asList(), list);
try {
list.remove(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
try {
list.remove(0);
} catch (IndexOutOfBoundsException e) {
// expected
}
}
private void assertImmutable(DoubleArrayList list) {
if (list.contains(1D)) {
throw new RuntimeException("Cannot test the immutability of lists that contain 1.");
}
try {
list.add(1D);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.add(0, 1D);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(Collections.<Double>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(Collections.singletonList(1D));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(new DoubleArrayList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(0, Collections.singleton(1D));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(0, UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(0, Collections.<Double>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addDouble(0);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.clear();
fail();
@ -383,28 +386,28 @@ public class DoubleArrayListTest extends TestCase {
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.remove(new Object());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.removeAll(Collections.<Double>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.removeAll(Collections.singleton(1D));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.removeAll(UNARY_LIST);
fail();
@ -418,28 +421,28 @@ public class DoubleArrayListTest extends TestCase {
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.retainAll(Collections.singleton(1D));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.retainAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.set(0, 0D);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.setDouble(0, 0);
fail();
@ -447,7 +450,7 @@ public class DoubleArrayListTest extends TestCase {
// expected
}
}
private static DoubleArrayList newImmutableDoubleArrayList(double... elements) {
DoubleArrayList list = new DoubleArrayList();
for (double element : elements) {

@ -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.

@ -40,39 +40,40 @@ import java.util.Iterator;
/**
* Tests for {@link FloatArrayList}.
*
*
* @author dweis@google.com (Daniel Weis)
*/
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);
private FloatArrayList list;
@Override
protected void setUp() throws Exception {
list = new FloatArrayList();
}
public void testEmptyListReturnsSameInstance() {
assertSame(FloatArrayList.emptyList(), FloatArrayList.emptyList());
}
public void testEmptyListIsImmutable() {
assertImmutable(FloatArrayList.emptyList());
}
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);
}
public void testModificationWithIteration() {
list.addAll(asList(1F, 2F, 3F, 4F));
Iterator<Float> iterator = list.iterator();
@ -81,7 +82,7 @@ public class FloatArrayListTest extends TestCase {
assertEquals(1F, (float) iterator.next());
list.set(0, 1F);
assertEquals(2F, (float) iterator.next());
list.remove(0);
try {
iterator.next();
@ -89,7 +90,7 @@ public class FloatArrayListTest extends TestCase {
} catch (ConcurrentModificationException e) {
// expected
}
iterator = list.iterator();
list.add(0, 0F);
try {
@ -99,19 +100,19 @@ public class FloatArrayListTest extends TestCase {
// expected
}
}
public void testGet() {
assertEquals(1F, (float) TERTIARY_LIST.get(0));
assertEquals(2F, (float) TERTIARY_LIST.get(1));
assertEquals(3F, (float) TERTIARY_LIST.get(2));
try {
TERTIARY_LIST.get(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
try {
TERTIARY_LIST.get(3);
fail();
@ -119,19 +120,19 @@ public class FloatArrayListTest extends TestCase {
// expected
}
}
public void testGetFloat() {
assertEquals(1F, TERTIARY_LIST.getFloat(0));
assertEquals(2F, TERTIARY_LIST.getFloat(1));
assertEquals(3F, TERTIARY_LIST.getFloat(2));
try {
TERTIARY_LIST.get(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
try {
TERTIARY_LIST.get(3);
fail();
@ -139,35 +140,35 @@ public class FloatArrayListTest extends TestCase {
// expected
}
}
public void testSize() {
assertEquals(0, FloatArrayList.emptyList().size());
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);
assertEquals(4, list.size());
list.remove(0);
assertEquals(3, list.size());
list.add(16F);
list.add(17F);
assertEquals(4, list.size());
}
public void testSet() {
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));
try {
list.set(-1, 0F);
fail();
@ -182,17 +183,17 @@ public class FloatArrayListTest extends TestCase {
// expected
}
}
public void testSetFloat() {
list.addFloat(2);
list.addFloat(4);
assertEquals(2F, list.setFloat(0, 0));
list.addFloat(1);
list.addFloat(3);
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 {
list.setFloat(-1, 0);
fail();
@ -207,7 +208,7 @@ public class FloatArrayListTest extends TestCase {
// expected
}
}
public void testAdd() {
assertEquals(0, list.size());
@ -217,28 +218,30 @@ public class FloatArrayListTest extends TestCase {
assertTrue(list.add(3F));
list.add(0, 4F);
assertEquals(asList(4F, 2F, 3F), list);
list.add(0, 1F);
list.add(0, 0F);
// Force a resize by getting up to 11 elements.
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);
} catch (IndexOutOfBoundsException e) {
// expected
}
try {
list.add(4, 5F);
} catch (IndexOutOfBoundsException e) {
// expected
}
}
public void testAddFloat() {
assertEquals(0, list.size());
@ -248,7 +251,7 @@ public class FloatArrayListTest extends TestCase {
list.addFloat(3);
assertEquals(asList(2F, 3F), list);
}
public void testAddAll() {
assertEquals(0, list.size());
@ -256,17 +259,17 @@ public class FloatArrayListTest extends TestCase {
assertEquals(1, list.size());
assertEquals(1F, (float) list.get(0));
assertEquals(1F, list.getFloat(0));
assertTrue(list.addAll(asList(2F, 3F, 4F, 5F, 6F)));
assertEquals(asList(1F, 2F, 3F, 4F, 5F, 6F), list);
assertTrue(list.addAll(TERTIARY_LIST));
assertEquals(asList(1F, 2F, 3F, 4F, 5F, 6F, 1F, 2F, 3F), list);
assertFalse(list.addAll(Collections.<Float>emptyList()));
assertFalse(list.addAll(FloatArrayList.emptyList()));
}
public void testRemove() {
list.addAll(TERTIARY_LIST);
assertEquals(1F, (float) list.remove(0));
@ -280,96 +283,96 @@ public class FloatArrayListTest extends TestCase {
assertEquals(2F, (float) list.remove(0));
assertEquals(asList(), list);
try {
list.remove(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
try {
list.remove(0);
} catch (IndexOutOfBoundsException e) {
// expected
}
}
private void assertImmutable(FloatArrayList list) {
if (list.contains(1F)) {
throw new RuntimeException("Cannot test the immutability of lists that contain 1.");
}
try {
list.add(1F);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.add(0, 1F);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(Collections.<Float>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(Collections.singletonList(1F));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(new FloatArrayList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(0, Collections.singleton(1F));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(0, UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(0, Collections.<Float>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addFloat(0);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.clear();
fail();
@ -383,63 +386,63 @@ public class FloatArrayListTest extends TestCase {
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.remove(new Object());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.removeAll(Collections.<Float>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.removeAll(Collections.singleton(1F));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.removeAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.retainAll(Collections.<Float>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.retainAll(Collections.singleton(1F));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.retainAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.set(0, 0F);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.setFloat(0, 0);
fail();
@ -447,10 +450,10 @@ public class FloatArrayListTest extends TestCase {
// expected
}
}
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();

@ -40,35 +40,36 @@ import java.util.Iterator;
/**
* Tests for {@link IntArrayList}.
*
*
* @author dweis@google.com (Daniel Weis)
*/
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);
private IntArrayList list;
@Override
protected void setUp() throws Exception {
list = new IntArrayList();
}
public void testEmptyListReturnsSameInstance() {
assertSame(IntArrayList.emptyList(), IntArrayList.emptyList());
}
public void testEmptyListIsImmutable() {
assertImmutable(IntArrayList.emptyList());
}
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);
}
@ -81,7 +82,7 @@ public class IntArrayListTest extends TestCase {
assertEquals(1, (int) iterator.next());
list.set(0, 1);
assertEquals(2, (int) iterator.next());
list.remove(0);
try {
iterator.next();
@ -99,19 +100,19 @@ public class IntArrayListTest extends TestCase {
// expected
}
}
public void testGet() {
assertEquals(1, (int) TERTIARY_LIST.get(0));
assertEquals(2, (int) TERTIARY_LIST.get(1));
assertEquals(3, (int) TERTIARY_LIST.get(2));
try {
TERTIARY_LIST.get(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
try {
TERTIARY_LIST.get(3);
fail();
@ -119,19 +120,19 @@ public class IntArrayListTest extends TestCase {
// expected
}
}
public void testGetInt() {
assertEquals(1, TERTIARY_LIST.getInt(0));
assertEquals(2, TERTIARY_LIST.getInt(1));
assertEquals(3, TERTIARY_LIST.getInt(2));
try {
TERTIARY_LIST.get(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
try {
TERTIARY_LIST.get(3);
fail();
@ -139,35 +140,35 @@ public class IntArrayListTest extends TestCase {
// expected
}
}
public void testSize() {
assertEquals(0, IntArrayList.emptyList().size());
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);
assertEquals(4, list.size());
list.remove(0);
assertEquals(3, list.size());
list.add(16);
list.add(17);
assertEquals(4, list.size());
}
public void testSet() {
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));
try {
list.set(-1, 0);
fail();
@ -182,17 +183,17 @@ public class IntArrayListTest extends TestCase {
// expected
}
}
public void testSetInt() {
list.addInt(2);
list.addInt(4);
assertEquals(2, list.setInt(0, 0));
list.addInt(1);
list.addInt(3);
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 {
list.setInt(-1, 0);
fail();
@ -207,7 +208,7 @@ public class IntArrayListTest extends TestCase {
// expected
}
}
public void testAdd() {
assertEquals(0, list.size());
@ -217,28 +218,30 @@ public class IntArrayListTest extends TestCase {
assertTrue(list.add(3));
list.add(0, 4);
assertEquals(asList(4, 2, 3), list);
list.add(0, 1);
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);
} catch (IndexOutOfBoundsException e) {
// expected
}
try {
list.add(4, 5);
} catch (IndexOutOfBoundsException e) {
// expected
}
}
public void testAddInt() {
assertEquals(0, list.size());
@ -248,7 +251,7 @@ public class IntArrayListTest extends TestCase {
list.addInt(3);
assertEquals(asList(2, 3), list);
}
public void testAddAll() {
assertEquals(0, list.size());
@ -256,17 +259,17 @@ public class IntArrayListTest extends TestCase {
assertEquals(1, list.size());
assertEquals(1, (int) list.get(0));
assertEquals(1, list.getInt(0));
assertTrue(list.addAll(asList(2, 3, 4, 5, 6)));
assertEquals(asList(1, 2, 3, 4, 5, 6), list);
assertTrue(list.addAll(TERTIARY_LIST));
assertEquals(asList(1, 2, 3, 4, 5, 6, 1, 2, 3), list);
assertFalse(list.addAll(Collections.<Integer>emptyList()));
assertFalse(list.addAll(IntArrayList.emptyList()));
}
public void testRemove() {
list.addAll(TERTIARY_LIST);
assertEquals(1, (int) list.remove(0));
@ -280,96 +283,96 @@ public class IntArrayListTest extends TestCase {
assertEquals(2, (int) list.remove(0));
assertEquals(asList(), list);
try {
list.remove(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
try {
list.remove(0);
} catch (IndexOutOfBoundsException e) {
// expected
}
}
private void assertImmutable(IntArrayList list) {
if (list.contains(1)) {
throw new RuntimeException("Cannot test the immutability of lists that contain 1.");
}
try {
list.add(1);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.add(0, 1);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(Collections.<Integer>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(Collections.singletonList(1));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(new IntArrayList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(0, Collections.singleton(1));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(0, UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(0, Collections.<Integer>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addInt(0);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.clear();
fail();
@ -383,63 +386,63 @@ public class IntArrayListTest extends TestCase {
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.remove(new Object());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.removeAll(Collections.<Integer>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.removeAll(Collections.singleton(1));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.removeAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.retainAll(Collections.<Integer>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.retainAll(Collections.singleton(1));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.retainAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.set(0, 0);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.setInt(0, 0);
fail();
@ -447,7 +450,7 @@ public class IntArrayListTest extends TestCase {
// expected
}
}
private static IntArrayList newImmutableIntArrayList(int... elements) {
IntArrayList list = new IntArrayList();
for (int element : elements) {

@ -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)

@ -1630,7 +1630,7 @@ public class LiteTest extends TestCase {
fail();
} catch (InvalidProtocolBufferException expected) {}
}
public void testMergeFrom_sanity() throws Exception {
TestAllTypesLite one = TestUtilLite.getAllLiteSetBuilder().build();
byte[] bytes = one.toByteArray();
@ -1642,7 +1642,19 @@ public class LiteTest extends TestCase {
assertEquals(two, one);
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();
@ -2202,6 +2214,21 @@ public class LiteTest extends TestCase {
assertEqualsAndHashCodeAreFalse(fooWithOnlyValue, fooWithValueAndUnknownFields);
assertEqualsAndHashCodeAreFalse(fooWithValueAndExtension, fooWithValueAndUnknownFields);
}
public void testEqualsAndHashCodeWithExtensions() throws InvalidProtocolBufferException {
Foo fooWithOnlyValue = Foo.newBuilder()
.setValue(1)
.build();
Foo fooWithValueAndExtension = fooWithOnlyValue.toBuilder()
.setValue(1)
.setExtension(Bar.fooExt, Bar.newBuilder()
.setName("name")
.build())
.build();
assertEqualsAndHashCodeAreFalse(fooWithOnlyValue, fooWithValueAndExtension);
}
// Test to ensure we avoid a class cast exception with oneofs.
public void testEquals_oneOfMessages() {

@ -40,48 +40,49 @@ import java.util.Iterator;
/**
* Tests for {@link LongArrayList}.
*
*
* @author dweis@google.com (Daniel Weis)
*/
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);
private LongArrayList list;
@Override
protected void setUp() throws Exception {
list = new LongArrayList();
}
public void testEmptyListReturnsSameInstance() {
assertSame(LongArrayList.emptyList(), LongArrayList.emptyList());
}
public void testEmptyListIsImmutable() {
assertImmutable(LongArrayList.emptyList());
}
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);
}
public void testModificationWithIteration() {
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 {
iterator.next();
@ -89,7 +90,7 @@ public class LongArrayListTest extends TestCase {
} catch (ConcurrentModificationException e) {
// expected
}
iterator = list.iterator();
list.add(0, 0L);
try {
@ -99,19 +100,19 @@ public class LongArrayListTest extends TestCase {
// expected
}
}
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);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
try {
TERTIARY_LIST.get(3);
fail();
@ -119,19 +120,19 @@ public class LongArrayListTest extends TestCase {
// expected
}
}
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);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
try {
TERTIARY_LIST.get(3);
fail();
@ -139,35 +140,35 @@ public class LongArrayListTest extends TestCase {
// expected
}
}
public void testSize() {
assertEquals(0, LongArrayList.emptyList().size());
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);
assertEquals(4, list.size());
list.remove(0);
assertEquals(3, list.size());
list.add(16L);
list.add(17L);
assertEquals(4, list.size());
}
public void testSet() {
list.addLong(2);
list.addLong(4);
assertEquals(2, (long) list.set(0, 0L));
assertEquals(0, list.getLong(0));
assertEquals(4, (long) list.set(1, 0L));
assertEquals(0, list.getLong(1));
assertEquals(2L, (long) list.set(0, 3L));
assertEquals(3L, list.getLong(0));
assertEquals(4L, (long) list.set(1, 0L));
assertEquals(0L, list.getLong(1));
try {
list.set(-1, 0L);
fail();
@ -182,17 +183,17 @@ public class LongArrayListTest extends TestCase {
// expected
}
}
public void testSetLong() {
list.addLong(2);
list.addLong(4);
assertEquals(2, list.setLong(0, 0));
assertEquals(0, list.getLong(0));
list.addLong(1);
list.addLong(3);
assertEquals(1L, list.setLong(0, 0));
assertEquals(0L, list.getLong(0));
assertEquals(3L, list.setLong(1, 0));
assertEquals(0L, list.getLong(1));
assertEquals(4, list.setLong(1, 0));
assertEquals(0, list.getLong(1));
try {
list.setLong(-1, 0);
fail();
@ -207,7 +208,7 @@ public class LongArrayListTest extends TestCase {
// expected
}
}
public void testAdd() {
assertEquals(0, list.size());
@ -217,28 +218,30 @@ public class LongArrayListTest extends TestCase {
assertTrue(list.add(3L));
list.add(0, 4L);
assertEquals(asList(4L, 2L, 3L), list);
list.add(0, 1L);
list.add(0, 0L);
// Force a resize by getting up to 11 elements.
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);
} catch (IndexOutOfBoundsException e) {
// expected
}
try {
list.add(4, 5L);
} catch (IndexOutOfBoundsException e) {
// expected
}
}
public void testAddLong() {
assertEquals(0, list.size());
@ -248,128 +251,128 @@ public class LongArrayListTest extends TestCase {
list.addLong(3);
assertEquals(asList(2L, 3L), list);
}
public void testAddAll() {
assertEquals(0, list.size());
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);
assertTrue(list.addAll(TERTIARY_LIST));
assertEquals(asList(1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L), list);
assertFalse(list.addAll(Collections.<Long>emptyList()));
assertFalse(list.addAll(LongArrayList.emptyList()));
}
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 {
list.remove(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
try {
list.remove(0);
} catch (IndexOutOfBoundsException e) {
// expected
}
}
private void assertImmutable(LongArrayList list) {
if (list.contains(1L)) {
throw new RuntimeException("Cannot test the immutability of lists that contain 1.");
}
try {
list.add(1L);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.add(0, 1L);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(Collections.<Long>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(Collections.singletonList(1L));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(new LongArrayList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(0, Collections.singleton(1L));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(0, UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addAll(0, Collections.<Long>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.addLong(0);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.clear();
fail();
@ -383,63 +386,63 @@ public class LongArrayListTest extends TestCase {
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.remove(new Object());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.removeAll(Collections.<Long>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.removeAll(Collections.singleton(1L));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.removeAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.retainAll(Collections.<Long>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.retainAll(Collections.singleton(1L));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.retainAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.set(0, 0L);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
try {
list.setLong(0, 0);
fail();
@ -447,7 +450,7 @@ public class LongArrayListTest extends TestCase {
// expected
}
}
private static LongArrayList newImmutableLongArrayList(long... elements) {
LongArrayList list = new LongArrayList();
for (long element : elements) {

@ -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 {
public final class MapForProto2LiteTest extends TestCase {
private void setMapValues(TestMap.Builder builder) {
builder.getMutableInt32ToInt32Field().put(1, 11);
builder.getMutableInt32ToInt32Field().put(2, 22);
builder.getMutableInt32ToInt32Field().put(3, 33);
builder.getMutableInt32ToStringField().put(1, "11");
builder.getMutableInt32ToStringField().put(2, "22");
builder.getMutableInt32ToStringField().put(3, "33");
builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("11"));
builder.getMutableInt32ToBytesField().put(2, TestUtil.toBytes("22"));
builder.getMutableInt32ToBytesField().put(3, TestUtil.toBytes("33"));
builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.FOO);
builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.BAR);
builder.getMutableInt32ToEnumField().put(3, TestMap.EnumValue.BAZ);
builder.getMutableInt32ToMessageField().put(
1, MessageValue.newBuilder().setValue(11).build());
builder.getMutableInt32ToMessageField().put(
2, MessageValue.newBuilder().setValue(22).build());
builder.getMutableInt32ToMessageField().put(
3, MessageValue.newBuilder().setValue(33).build());
builder.getMutableStringToInt32Field().put("1", 11);
builder.getMutableStringToInt32Field().put("2", 22);
builder.getMutableStringToInt32Field().put("3", 33);
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 mapBuilder = TestMap.newBuilder();
setMapValues(mapBuilder);
TestMap map = mapBuilder.build();
assertMapValuesSet(map);
}
private void copyMapValues(TestMap source, TestMap.Builder destination) {
@ -94,22 +104,22 @@ public class MapForProto2LiteTest extends TestCase {
assertEquals("11", message.getInt32ToStringField().get(1));
assertEquals("22", message.getInt32ToStringField().get(2));
assertEquals("33", message.getInt32ToStringField().get(3));
assertEquals(3, message.getInt32ToBytesField().size());
assertEquals(TestUtil.toBytes("11"), message.getInt32ToBytesField().get(1));
assertEquals(TestUtil.toBytes("22"), message.getInt32ToBytesField().get(2));
assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3));
assertEquals(3, message.getInt32ToEnumField().size());
assertEquals(TestMap.EnumValue.FOO, message.getInt32ToEnumField().get(1));
assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(2));
assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3));
assertEquals(3, message.getInt32ToMessageField().size());
assertEquals(11, message.getInt32ToMessageField().get(1).getValue());
assertEquals(22, message.getInt32ToMessageField().get(2).getValue());
assertEquals(33, message.getInt32ToMessageField().get(3).getValue());
assertEquals(3, message.getStringToInt32Field().size());
assertEquals(11, message.getStringToInt32Field().get("1").intValue());
assertEquals(22, message.getStringToInt32Field().get("2").intValue());
@ -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.getMutableInt32ToMessageField().put(
1, MessageValue.newBuilder().setValue(111).build());
builder.getMutableInt32ToMessageField().remove(2);
builder.getMutableInt32ToMessageField().put(
4, MessageValue.newBuilder().setValue(44).build());
builder.getMutableStringToInt32Field().put("1", 111);
builder.getMutableStringToInt32Field().remove("2");
builder.getMutableStringToInt32Field().put("4", 44);
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 mapBuilder = TestMap.newBuilder();
setMapValues(mapBuilder);
TestMap map = mapBuilder.build();
assertMapValuesSet(map);
mapBuilder = map.toBuilder();
updateMapValues(mapBuilder);
map = mapBuilder.build();
assertMapValuesUpdated(map);
}
private void assertMapValuesUpdated(TestMap message) {
@ -154,188 +175,149 @@ public class MapForProto2LiteTest extends TestCase {
assertEquals("111", message.getInt32ToStringField().get(1));
assertEquals("33", message.getInt32ToStringField().get(3));
assertEquals("44", message.getInt32ToStringField().get(4));
assertEquals(3, message.getInt32ToBytesField().size());
assertEquals(TestUtil.toBytes("111"), message.getInt32ToBytesField().get(1));
assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3));
assertEquals(TestUtil.toBytes("44"), message.getInt32ToBytesField().get(4));
assertEquals(3, message.getInt32ToEnumField().size());
assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(1));
assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3));
assertEquals(TestMap.EnumValue.QUX, message.getInt32ToEnumField().get(4));
assertEquals(3, message.getInt32ToMessageField().size());
assertEquals(111, message.getInt32ToMessageField().get(1).getValue());
assertEquals(33, message.getInt32ToMessageField().get(3).getValue());
assertEquals(44, message.getInt32ToMessageField().get(4).getValue());
assertEquals(3, message.getStringToInt32Field().size());
assertEquals(111, message.getStringToInt32Field().get("1").intValue());
assertEquals(33, message.getStringToInt32Field().get("3").intValue());
assertEquals(44, message.getStringToInt32Field().get("4").intValue());
}
private void assertMapValuesCleared(TestMap message) {
assertEquals(0, message.getInt32ToInt32Field().size());
assertEquals(0, message.getInt32ToStringField().size());
assertEquals(0, message.getInt32ToBytesField().size());
assertEquals(0, message.getInt32ToEnumField().size());
assertEquals(0, message.getInt32ToMessageField().size());
assertEquals(0, message.getStringToInt32Field().size());
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 {
// Since builders are implemented as a thin wrapper around a message
// instance, we attempt to verify that we can't cause the builder to modify
// a produced message.
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
}
if (!map.isEmpty()) {
try {
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.getMutableInt32ToInt32Field().put(2, 3);
builder.putInt32ToInt32Field(2, 3);
assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field());
Map<Integer, TestMap.EnumValue> enumMap = builder.getMutableInt32ToEnumField();
enumMap.put(1, TestMap.EnumValue.BAR);
builder.putInt32ToEnumField(1, TestMap.EnumValue.BAR);
assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.build().getInt32ToEnumField());
try {
enumMap.put(2, TestMap.EnumValue.FOO);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
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());
Map<Integer, TestMap.MessageValue> messageMap = builder.getMutableInt32ToMessageField();
messageMap.put(1, TestMap.MessageValue.getDefaultInstance());
builder.putInt32ToStringField(2, "2");
assertEquals(newMap(1, "1", 2, "2"), builder.getInt32ToStringField());
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();
assertMapValuesCleared(message);
builder = message.toBuilder();
setMapValues(builder);
message = builder.build();
assertMapValuesSet(message);
builder = message.toBuilder();
updateMapValues(builder);
message = builder.build();
assertMapValuesUpdated(message);
builder = message.toBuilder();
builder.clear();
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);
@ -357,14 +379,14 @@ public class MapForProto2LiteTest extends TestCase {
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesSet(message);
builder = message.toBuilder();
updateMapValues(builder);
message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesUpdated(message);
builder = message.toBuilder();
builder.clear();
message = builder.build();
@ -372,12 +394,61 @@ public class MapForProto2LiteTest extends TestCase {
message = TestMap.parser().parseFrom(message.toByteString());
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);
TestMap message = builder.build();
TestMap.Builder other = TestMap.newBuilder();
other.mergeFrom(message);
assertMapValuesSet(other.build());
@ -386,26 +457,26 @@ public class MapForProto2LiteTest extends TestCase {
public void testEqualsAndHashCode() throws Exception {
// Test that generated equals() and hashCode() will disregard the order
// of map entries when comparing/hashing map fields.
// We can't control the order of elements in a HashMap. The best we can do
// here is to add elements in different order.
TestMap.Builder b1 = TestMap.newBuilder();
b1.getMutableInt32ToInt32Field().put(1, 2);
b1.getMutableInt32ToInt32Field().put(3, 4);
b1.getMutableInt32ToInt32Field().put(5, 6);
TestMap.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);
@ -442,17 +512,288 @@ public class MapForProto2LiteTest extends TestCase {
assertEquals(Arrays.asList("1", "2", "3"),
new ArrayList<String>(message.getStringToInt32Field().keySet()));
}
private static <K, V> Map<K, V> newMap(K key1, V value1) {
Map<K, V> map = new HashMap<K, V>();
map.put(key1, value1);
return map;
}
private static <K, V> Map<K, V> newMap(K key1, V value1, K key2, V value2) {
Map<K, V> map = new HashMap<K, V>();
map.put(key1, value1);
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);
@ -56,27 +61,67 @@ public class MapForProto2Test extends TestCase {
builder.getMutableInt32ToStringField().put(1, "11");
builder.getMutableInt32ToStringField().put(2, "22");
builder.getMutableInt32ToStringField().put(3, "33");
builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("11"));
builder.getMutableInt32ToBytesField().put(2, TestUtil.toBytes("22"));
builder.getMutableInt32ToBytesField().put(3, TestUtil.toBytes("33"));
builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.FOO);
builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.BAR);
builder.getMutableInt32ToEnumField().put(3, TestMap.EnumValue.BAZ);
builder.getMutableInt32ToMessageField().put(
1, MessageValue.newBuilder().setValue(11).build());
builder.getMutableInt32ToMessageField().put(
2, MessageValue.newBuilder().setValue(22).build());
builder.getMutableInt32ToMessageField().put(
3, MessageValue.newBuilder().setValue(33).build());
builder.getMutableStringToInt32Field().put("1", 11);
builder.getMutableStringToInt32Field().put("2", 22);
builder.getMutableStringToInt32Field().put("3", 33);
}
private void 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());
@ -97,29 +142,29 @@ public class MapForProto2Test extends TestCase {
assertEquals("11", message.getInt32ToStringField().get(1));
assertEquals("22", message.getInt32ToStringField().get(2));
assertEquals("33", message.getInt32ToStringField().get(3));
assertEquals(3, message.getInt32ToBytesField().size());
assertEquals(TestUtil.toBytes("11"), message.getInt32ToBytesField().get(1));
assertEquals(TestUtil.toBytes("22"), message.getInt32ToBytesField().get(2));
assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3));
assertEquals(3, message.getInt32ToEnumField().size());
assertEquals(TestMap.EnumValue.FOO, message.getInt32ToEnumField().get(1));
assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(2));
assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3));
assertEquals(3, message.getInt32ToMessageField().size());
assertEquals(11, message.getInt32ToMessageField().get(1).getValue());
assertEquals(22, message.getInt32ToMessageField().get(2).getValue());
assertEquals(33, message.getInt32ToMessageField().get(3).getValue());
assertEquals(3, message.getStringToInt32Field().size());
assertEquals(11, message.getStringToInt32Field().get("1").intValue());
assertEquals(22, message.getStringToInt32Field().get("2").intValue());
assertEquals(33, message.getStringToInt32Field().get("3").intValue());
}
private void updateMapValues(TestMap.Builder builder) {
private void updateMapValuesUsingMutableMap(TestMap.Builder builder) {
builder.getMutableInt32ToInt32Field().put(1, 111);
builder.getMutableInt32ToInt32Field().remove(2);
builder.getMutableInt32ToInt32Field().put(4, 44);
@ -127,26 +172,78 @@ public class MapForProto2Test extends TestCase {
builder.getMutableInt32ToStringField().put(1, "111");
builder.getMutableInt32ToStringField().remove(2);
builder.getMutableInt32ToStringField().put(4, "44");
builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("111"));
builder.getMutableInt32ToBytesField().remove(2);
builder.getMutableInt32ToBytesField().put(4, TestUtil.toBytes("44"));
builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.BAR);
builder.getMutableInt32ToEnumField().remove(2);
builder.getMutableInt32ToEnumField().put(4, TestMap.EnumValue.QUX);
builder.getMutableInt32ToMessageField().put(
1, MessageValue.newBuilder().setValue(111).build());
builder.getMutableInt32ToMessageField().remove(2);
builder.getMutableInt32ToMessageField().put(
4, MessageValue.newBuilder().setValue(44).build());
builder.getMutableStringToInt32Field().put("1", 111);
builder.getMutableStringToInt32Field().remove("2");
builder.getMutableStringToInt32Field().put("4", 44);
}
private void 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());
@ -157,37 +254,72 @@ public class MapForProto2Test extends TestCase {
assertEquals("111", message.getInt32ToStringField().get(1));
assertEquals("33", message.getInt32ToStringField().get(3));
assertEquals("44", message.getInt32ToStringField().get(4));
assertEquals(3, message.getInt32ToBytesField().size());
assertEquals(TestUtil.toBytes("111"), message.getInt32ToBytesField().get(1));
assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3));
assertEquals(TestUtil.toBytes("44"), message.getInt32ToBytesField().get(4));
assertEquals(3, message.getInt32ToEnumField().size());
assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(1));
assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3));
assertEquals(TestMap.EnumValue.QUX, message.getInt32ToEnumField().get(4));
assertEquals(3, message.getInt32ToMessageField().size());
assertEquals(111, message.getInt32ToMessageField().get(1).getValue());
assertEquals(33, message.getInt32ToMessageField().get(3).getValue());
assertEquals(44, message.getInt32ToMessageField().get(4).getValue());
assertEquals(3, message.getStringToInt32Field().size());
assertEquals(111, message.getStringToInt32Field().get("1").intValue());
assertEquals(33, message.getStringToInt32Field().get("3").intValue());
assertEquals(44, message.getStringToInt32Field().get("4").intValue());
}
private void assertMapValuesCleared(TestMap message) {
assertEquals(0, message.getInt32ToInt32Field().size());
assertEquals(0, message.getInt32ToStringField().size());
assertEquals(0, message.getInt32ToBytesField().size());
assertEquals(0, message.getInt32ToEnumField().size());
assertEquals(0, message.getInt32ToMessageField().size());
assertEquals(0, message.getStringToInt32Field().size());
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() {
TestMap.Builder builder = TestMap.newBuilder();
Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field();
@ -217,7 +349,7 @@ public class MapForProto2Test extends TestCase {
assertEquals(
newMap(1, TestMap.EnumValue.BAR, 2, TestMap.EnumValue.FOO),
builder.getInt32ToEnumField());
Map<Integer, String> stringMap = builder.getMutableInt32ToStringField();
stringMap.put(1, "1");
assertEquals(newMap(1, "1"), builder.build().getInt32ToStringField());
@ -232,7 +364,7 @@ public class MapForProto2Test extends TestCase {
assertEquals(
newMap(1, "1", 2, "2"),
builder.getInt32ToStringField());
Map<Integer, TestMap.MessageValue> messageMap = builder.getMutableInt32ToMessageField();
messageMap.put(1, TestMap.MessageValue.getDefaultInstance());
assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()),
@ -302,48 +434,91 @@ public class MapForProto2Test extends TestCase {
TestMap.Builder builder = TestMap.newBuilder();
TestMap message = builder.build();
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());
assertMapValuesUpdated(message);
builder = message.toBuilder();
builder.clear();
message = builder.build();
@ -351,12 +526,61 @@ public class MapForProto2Test extends TestCase {
message = TestMap.parser().parseFrom(message.toByteString());
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();
other.mergeFrom(message);
assertMapValuesSet(other.build());
@ -365,7 +589,7 @@ public class MapForProto2Test extends TestCase {
public void testEqualsAndHashCode() throws Exception {
// Test that generated equals() and hashCode() will disregard the order
// of map entries when comparing/hashing map fields.
// We can't control the order of elements in a HashMap. The best we can do
// here is to add elements in different order.
TestMap.Builder b1 = TestMap.newBuilder();
@ -373,16 +597,16 @@ public class MapForProto2Test extends TestCase {
b1.getMutableInt32ToInt32Field().put(3, 4);
b1.getMutableInt32ToInt32Field().put(5, 6);
TestMap m1 = b1.build();
TestMap.Builder b2 = TestMap.newBuilder();
b2.getMutableInt32ToInt32Field().put(5, 6);
b2.getMutableInt32ToInt32Field().put(1, 2);
b2.getMutableInt32ToInt32Field().put(3, 4);
TestMap m2 = b2.build();
assertEquals(m1, m2);
assertEquals(m1.hashCode(), m2.hashCode());
// Make sure we did compare map fields.
b2.getMutableInt32ToInt32Field().put(1, 0);
m2 = b2.build();
@ -390,26 +614,26 @@ public class MapForProto2Test extends TestCase {
// Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed
// to be different.
}
// The following methods are used to test reflection API.
private static FieldDescriptor f(String name) {
return TestMap.getDescriptor().findFieldByName(name);
}
private static Object getFieldValue(Message mapEntry, String name) {
FieldDescriptor field = mapEntry.getDescriptorForType().findFieldByName(name);
return mapEntry.getField(field);
}
private static Message.Builder setFieldValue(
Message.Builder mapEntry, String name, Object value) {
FieldDescriptor field = mapEntry.getDescriptorForType().findFieldByName(name);
mapEntry.setField(field, value);
return mapEntry;
}
private static void assertHasMapValues(Message message, String name, Map<?, ?> values) {
FieldDescriptor field = f(name);
for (Object entry : (List<?>) message.getField(field)) {
@ -428,7 +652,7 @@ public class MapForProto2Test extends TestCase {
assertEquals(value, values.get(key));
}
}
private static <KeyType, ValueType>
Message newMapEntry(Message.Builder builder, String name, KeyType key, ValueType value) {
FieldDescriptor field = builder.getDescriptorForType().findFieldByName(name);
@ -439,7 +663,7 @@ public class MapForProto2Test extends TestCase {
entryBuilder.setField(valueField, value);
return entryBuilder.build();
}
private static void setMapValues(Message.Builder builder, String name, Map<?, ?> values) {
List<Message> entryList = new ArrayList<Message>();
for (Map.Entry<?, ?> entry : values.entrySet()) {
@ -448,9 +672,8 @@ public class MapForProto2Test extends TestCase {
FieldDescriptor field = builder.getDescriptorForType().findFieldByName(name);
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);
@ -476,14 +699,14 @@ public class MapForProto2Test extends TestCase {
mapForValues(
11, MessageValue.newBuilder().setValue(22).build(),
33, MessageValue.newBuilder().setValue(44).build()));
// Test clearField()
builder.clearField(f("int32_to_int32_field"));
builder.clearField(f("int32_to_message_field"));
message = builder.build();
assertEquals(0, message.getInt32ToInt32Field().size());
assertEquals(0, message.getInt32ToMessageField().size());
// Test setField()
setMapValues(builder, "int32_to_int32_field",
mapForValues(11, 22, 33, 44));
@ -496,7 +719,7 @@ public class MapForProto2Test extends TestCase {
assertEquals(44, message.getInt32ToInt32Field().get(33).intValue());
assertEquals(222, message.getInt32ToMessageField().get(111).getValue());
assertEquals(444, message.getInt32ToMessageField().get(333).getValue());
// Test addRepeatedField
builder.addRepeatedField(f("int32_to_int32_field"),
newMapEntry(builder, "int32_to_int32_field", 55, 66));
@ -516,7 +739,7 @@ public class MapForProto2Test extends TestCase {
message = builder.build();
assertEquals(55, message.getInt32ToInt32Field().get(55).intValue());
assertEquals(555, message.getInt32ToMessageField().get(555).getValue());
// Test setRepeatedField
for (int i = 0; i < builder.getRepeatedFieldCount(f("int32_to_int32_field")); i++) {
Message mapEntry = (Message) builder.getRepeatedField(f("int32_to_int32_field"), i);
@ -533,35 +756,35 @@ public class MapForProto2Test extends TestCase {
assertEquals(33, message.getInt32ToInt32Field().get(44).intValue());
assertEquals(55, message.getInt32ToInt32Field().get(55).intValue());
}
public void testTextFormat() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
setMapValuesUsingMutableMap(builder);
TestMap message = builder.build();
String textData = TextFormat.printToString(message);
builder = TestMap.newBuilder();
TextFormat.merge(textData, builder);
message = builder.build();
assertMapValuesSet(message);
}
public void testDynamicMessage() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
setMapValuesUsingMutableMap(builder);
TestMap message = builder.build();
Message dynamicDefaultInstance =
DynamicMessage.getDefaultInstance(TestMap.getDescriptor());
Message dynamicMessage = dynamicDefaultInstance
.newBuilderForType().mergeFrom(message.toByteString()).build();
assertEquals(message, dynamicMessage);
assertEquals(message.hashCode(), dynamicMessage.hashCode());
}
public void testReflectionEqualsAndHashCode() throws Exception {
// Test that generated equals() and hashCode() will disregard the order
// of map entries when comparing/hashing map fields.
@ -570,22 +793,22 @@ public class MapForProto2Test extends TestCase {
Message dynamicDefaultInstance =
DynamicMessage.getDefaultInstance(TestMap.getDescriptor());
FieldDescriptor field = f("int32_to_int32_field");
Message.Builder b1 = dynamicDefaultInstance.newBuilderForType();
b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 1, 2));
b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 3, 4));
b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 5, 6));
Message m1 = b1.build();
Message.Builder b2 = dynamicDefaultInstance.newBuilderForType();
b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 5, 6));
b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 1, 2));
b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 3, 4));
Message m2 = b2.build();
assertEquals(m1, m2);
assertEquals(m1.hashCode(), m2.hashCode());
// Make sure we did compare map fields.
b2.setRepeatedField(field, 0, newMapEntry(b1, "int32_to_int32_field", 0, 0));
m2 = b2.build();
@ -593,7 +816,7 @@ public class MapForProto2Test extends TestCase {
// Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed
// to be different.
}
public void testUnknownEnumValues() throws Exception {
TestUnknownEnumValue.Builder builder =
TestUnknownEnumValue.newBuilder();
@ -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
@ -666,18 +1142,36 @@ public class MapForProto2Test extends TestCase {
message.getDescriptorForType().findFieldByName("map_field"), 0);
assertEquals(2, mapEntry.getAllFields().size());
}
private static <K, V> Map<K, V> newMap(K key1, V value1) {
Map<K, V> map = new HashMap<K, V>();
map.put(key1, value1);
return map;
}
private static <K, V> Map<K, V> newMap(K key1, V value1, K key2, V value2) {
Map<K, V> map = new HashMap<K, V>();
map.put(key1, value1);
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,15 +278,50 @@ 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() {
TestMap.Builder builder = TestMap.newBuilder();
Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field();
@ -218,7 +351,7 @@ public class MapTest extends TestCase {
assertEquals(
newMap(1, TestMap.EnumValue.BAR, 2, TestMap.EnumValue.FOO),
builder.getInt32ToEnumField());
Map<Integer, String> stringMap = builder.getMutableInt32ToStringField();
stringMap.put(1, "1");
assertEquals(newMap(1, "1"), builder.build().getInt32ToStringField());
@ -233,7 +366,7 @@ public class MapTest extends TestCase {
assertEquals(
newMap(1, "1", 2, "2"),
builder.getInt32ToStringField());
Map<Integer, TestMap.MessageValue> messageMap = builder.getMutableInt32ToMessageField();
messageMap.put(1, TestMap.MessageValue.getDefaultInstance());
assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()),
@ -298,32 +431,34 @@ public class MapTest extends TestCase {
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();
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,19 +1002,317 @@ 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);
return map;
}
private static <K, V> Map<K, V> newMap(K key1, V value1, K key2, V value2) {
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}.
@ -48,7 +53,7 @@ public class FieldMaskUtil {
private static final String FIELD_PATH_SEPARATOR = ",";
private static final String FIELD_PATH_SEPARATOR_REGEX = ",";
private static final String FIELD_SEPARATOR_REGEX = "\\.";
private FieldMaskUtil() {}
/**
@ -78,19 +83,17 @@ 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)));
}
/**
* Parses from a string to a FieldMask and validates all field paths.
*
*
* @throws IllegalArgumentException if any of the field path is invalid.
*/
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,16 +146,46 @@ 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);
}
/**
* Checks whether paths in a given fields mask are valid.
*/
@ -171,9 +202,8 @@ 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;
@ -202,7 +231,7 @@ public class FieldMaskUtil {
}
return true;
}
/**
* Converts a FieldMask to its canonical form. In the canonical form of a
* FieldMask, all field paths are sorted alphabetically and redundant field
@ -251,7 +280,7 @@ public class FieldMaskUtil {
* destination message fields) when merging.
* Default behavior is to merge the source message field into the
* destination message field.
*/
*/
public boolean replaceMessageFields() {
return replaceMessageFields;
}
@ -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());
}
}

@ -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.");
}
@ -428,50 +334,49 @@ public class TimeUtil {
int nanos = (int) ((result - seconds) * 1000000000);
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 =
new BigInteger(String.valueOf(NANOS_PER_SECOND));
private static BigInteger toBigInteger(Duration duration) {
return toBigInteger(duration.getSeconds())
.multiply(NANOS_PER_SECOND_BIG_INTEGER)
.add(toBigInteger(duration.getNanos()));
.multiply(NANOS_PER_SECOND_BIG_INTEGER)
.add(toBigInteger(duration.getNanos()));
}
private static BigInteger toBigInteger(long value) {
return new BigInteger(String.valueOf(value));
}
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,52 +41,55 @@ 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(
NestedTestAllTypes.class, FieldMaskUtil.fromString("payload,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(
NestedTestAllTypes.class, FieldMaskUtil.fromString("payload,nonexist")));
assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.getDescriptor(), "payload"));
assertFalse(FieldMaskUtil.isValid(NestedTestAllTypes.getDescriptor(), "nonexist"));
assertTrue(FieldMaskUtil.isValid(
NestedTestAllTypes.getDescriptor(), FieldMaskUtil.fromString("payload")));
assertFalse(FieldMaskUtil.isValid(
NestedTestAllTypes.getDescriptor(), FieldMaskUtil.fromString("nonexist")));
assertTrue(FieldMaskUtil.isValid(
NestedTestAllTypes.class, "payload.optional_nested_message.bb"));
assertTrue(
FieldMaskUtil.isValid(
NestedTestAllTypes.getDescriptor(), FieldMaskUtil.fromString("payload")));
assertFalse(
FieldMaskUtil.isValid(
NestedTestAllTypes.getDescriptor(), FieldMaskUtil.fromString("nonexist")));
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 {
assertEquals("", FieldMaskUtil.toString(FieldMask.getDefaultInstance()));
FieldMask mask = FieldMask.newBuilder().addPaths("foo").build();
assertEquals("foo", FieldMaskUtil.toString(mask));
mask = FieldMask.newBuilder().addPaths("foo").addPaths("bar").build();
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.
@ -143,7 +145,33 @@ public class FieldMaskUtilTest extends TestCase {
} catch (IllegalArgumentException expected) {
}
}
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.
@ -161,7 +189,7 @@ public class FieldMaskUtilTest extends TestCase {
FieldMask result = FieldMaskUtil.union(mask1, mask2, mask3, mask4);
assertEquals("bar,foo", FieldMaskUtil.toString(result));
}
public void testIntersection() throws Exception {
// Only test a simple case here and expect
// {@link FieldMaskTreeTest#testIntersectFieldPath} to cover all scenarios.
@ -170,13 +198,14 @@ public class FieldMaskUtilTest extends TestCase {
FieldMask result = FieldMaskUtil.intersection(mask1, mask2);
assertEquals("bar.baz,bar.quz,foo.bar", FieldMaskUtil.toString(result));
}
public void testMerge() throws Exception {
// Only test a simple case here and expect
// {@link FieldMaskTreeTest#testMerge} to cover all scenarios.
NestedTestAllTypes source = NestedTestAllTypes.newBuilder()
.setPayload(TestAllTypes.newBuilder().setOptionalInt32(1234))
.build();
NestedTestAllTypes source =
NestedTestAllTypes.newBuilder()
.setPayload(TestAllTypes.newBuilder().setOptionalInt32(1234))
.build();
NestedTestAllTypes.Builder builder = NestedTestAllTypes.newBuilder();
FieldMaskUtil.merge(FieldMaskUtil.fromString("payload"), source, builder);
assertEquals(1234, builder.getPayload().getOptionalInt32());

@ -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,26 +113,26 @@ public class TimeUtilTest extends TestCase {
}
public void testTimestampConcurrentParsing() throws Exception {
String[] timestampStrings = new String[]{
"0001-01-01T00:00:00Z",
"9999-12-31T23:59:59.999999999Z",
"1970-01-01T00:00:00Z",
"1969-12-31T23:59:59.999Z",
};
String[] timestampStrings =
new String[] {
"0001-01-01T00:00:00Z",
"9999-12-31T23:59:59.999999999Z",
"1970-01-01T00:00:00Z",
"1969-12-31T23:59:59.999Z",
};
Timestamp[] timestampValues = new Timestamp[timestampStrings.length];
for (int i = 0; i < timestampStrings.length; i++) {
timestampValues[i] = TimeUtil.parseTimestamp(timestampStrings[i]);
}
final int THREAD_COUNT = 16;
final int RUNNING_TIME = 5000; // in milliseconds.
final int RUNNING_TIME = 5000; // in milliseconds.
final List<Thread> threads = new ArrayList<Thread>();
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,14 +157,14 @@ 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) {
// Expected.
}
try {
// Invalid nanos value.
Timestamp value = Timestamp.newBuilder().setNanos(-1).build();
@ -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,18 +289,16 @@ 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) {
// Expected.
}
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));
@ -378,7 +374,7 @@ public class TimeUtilTest extends TestCase {
assertEquals("1970-01-01T00:00:01.111111Z", TimeUtil.toString(timestamp));
timestamp = TimeUtil.createTimestampFromMillis(1111);
assertEquals("1970-01-01T00:00:01.111Z", TimeUtil.toString(timestamp));
timestamp = TimeUtil.parseTimestamp("1969-12-31T23:59:59.111111111Z");
assertEquals(-888888889, TimeUtil.toNanos(timestamp));
assertEquals(-888889, TimeUtil.toMicros(timestamp));
@ -402,7 +398,7 @@ public class TimeUtilTest extends TestCase {
assertEquals("1.111111s", TimeUtil.toString(duration));
duration = TimeUtil.createDurationFromMillis(1111);
assertEquals("1.111s", TimeUtil.toString(duration));
duration = TimeUtil.parseDuration("-1.111111111s");
assertEquals(-1111111111, TimeUtil.toNanos(duration));
assertEquals(-1111111, TimeUtil.toMicros(duration));
@ -459,29 +455,28 @@ public class TimeUtilTest extends TestCase {
duration = TimeUtil.add(duration, duration);
assertEquals("-2.250s", TimeUtil.toString(duration));
duration = TimeUtil.subtract(duration, TimeUtil.parseDuration("-1s"));
assertEquals("-1.250s", TimeUtil.toString(duration));
// 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");
Duration d2 = TimeUtil.subtract(d1, TimeUtil.createDurationFromNanos(1));
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.
//
// (-5) / 2 = -2, remainder = -1

@ -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,21 +189,23 @@ 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)
self.assertEqual(message, parsed_message,
'\n%s != %s' % (message, parsed_message))
'\n%s != %s' % (message, parsed_message))
def testPrintRawUtf8String(self, message_module):
message = message_module.TestAllTypes()
@ -211,7 +215,7 @@ class TextFormatTest(TextFormatBase):
parsed_message = message_module.TestAllTypes()
text_format.Parse(text, parsed_message)
self.assertEqual(message, parsed_message,
'\n%s != %s' % (message, parsed_message))
'\n%s != %s' % (message, parsed_message))
def testPrintFloatFormat(self, message_module):
# Check that float_format argument is passed to sub-message formatting.
@ -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,45 +375,38 @@ 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,
(r'1:23 : Enum type "\w+.TestAllTypes.NestedEnum" '
r'has no value named BARR.'),
text_format.Parse, text, message)
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)
message = message_module.TestAllTypes()
text = 'optional_nested_enum: 100'
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)
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)
def testParseBadIntValue(self, message_module):
message = message_module.TestAllTypes()
text = 'optional_int32: bork'
six.assertRaisesRegex(self,
text_format.ParseError,
('1:17 : Couldn\'t parse integer: bork'),
text_format.Parse, text, message)
six.assertRaisesRegex(self, text_format.ParseError,
('1:17 : Couldn\'t parse integer: bork'),
text_format.Parse, text, message)
def testParseStringFieldUnescape(self, message_module):
message = message_module.TestAllTypes()
@ -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,16 +635,14 @@ 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'
' 1545008 {\n'
' 15: 23\n'
' }\n'
' 1547769 {\n'
' 25: \"foo\"\n'
' }\n'
'}\n')
self.CompareToGoldenText(out.getvalue(), '1 {\n'
' 1545008 {\n'
' 15: 23\n'
' }\n'
' 1547769 {\n'
' 25: \"foo\"\n'
' }\n'
'}\n')
out.close()
def testPrintMessageSetAsOneLine(self):
@ -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,67 +883,57 @@ class Proto2Tests(TextFormatBase):
def testParseBadExtension(self):
message = unittest_pb2.TestAllExtensions()
text = '[unknown_extension]: 8\n'
six.assertRaisesRegex(self,
text_format.ParseError,
'1:2 : Extension "unknown_extension" not registered.',
text_format.Parse, text, message)
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,
message.Extensions[unittest_pb2.optional_int32_extension])
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" '
'should not have multiple '
'"protobuf_unittest.optional_int32_extension" extensions.'),
text_format.Parse, text, message)
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)
def testParseDuplicateNestedMessageScalars(self):
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 ">".',
text_format.Parse, text, message)
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 "}".',
text_format.Parse, text, message)
six.assertRaisesRegex(self, text_format.ParseError, '1:16 : Expected "}".',
text_format.Parse, text, message)
def testParseEmptyGroup(self):
message = unittest_pb2.TestAllTypes()
@ -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.assertRaises(text_format.ParseError, tokenizer.ConsumeUint32)
self.assertRaises(text_format.ParseError, tokenizer.ConsumeInt32)
self.assertEqual(uint32_max + 1, tokenizer.ConsumeInt64())
self.assertEqual(uint32_max + 1, tokenizer.ConsumeInteger())
self.assertRaises(text_format.ParseError, tokenizer.ConsumeInt64)
self.assertEqual(int64_max + 1, tokenizer.ConsumeUint64())
self.assertEqual(int64_max + 1, tokenizer.ConsumeInteger())
self.assertTrue(tokenizer.AtEnd())
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,22 +99,8 @@ 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)
def _MessageToJsonObject(message, including_default_value_fields):
"""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)
if full_name in _WKTJSONMETHODS:
return _WKTJSONMETHODS[full_name][0](
message, including_default_value_fields)
js = {}
return _RegularMessageToJsonObject(
message, js, including_default_value_fields)
printer = _Printer(including_default_value_fields)
return printer.ToJsonString(message)
def _IsMapEntry(field):
@ -122,179 +109,179 @@ def _IsMapEntry(field):
field.message_type.GetOptions().map_entry)
def _RegularMessageToJsonObject(message, js, including_default_value_fields):
"""Converts normal message according to Proto3 JSON Specification."""
fields = message.ListFields()
include_default = including_default_value_fields
class _Printer(object):
"""JSON format printer for protocol message."""
try:
for field, value in fields:
name = field.camelcase_name
if _IsMapEntry(field):
# Convert a map field.
v_field = field.message_type.fields_by_name['value']
js_map = {}
for key in value:
if isinstance(key, bool):
if key:
recorded_key = 'true'
else:
recorded_key = 'false'
else:
recorded_key = key
js_map[recorded_key] = _FieldToJsonObject(
v_field, value[key], including_default_value_fields)
js[name] = js_map
elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
# Convert a repeated field.
js[name] = [_FieldToJsonObject(field, k, include_default)
for k in value]
else:
js[name] = _FieldToJsonObject(field, value, include_default)
# Serialize default value if including_default_value_fields is True.
if including_default_value_fields:
message_descriptor = message.DESCRIPTOR
for field in message_descriptor.fields:
# Singular message fields and oneof fields will not be affected.
if ((field.label != descriptor.FieldDescriptor.LABEL_REPEATED and
field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE) or
field.containing_oneof):
continue
name = field.camelcase_name
if name in js:
# Skip the field which has been serailized already.
continue
if _IsMapEntry(field):
js[name] = {}
elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
js[name] = []
else:
js[name] = _FieldToJsonObject(field, field.default_value)
def __init__(self,
including_default_value_fields=False):
self.including_default_value_fields = including_default_value_fields
except ValueError as e:
raise SerializeToJsonError(
'Failed to serialize {0} field: {1}.'.format(field.name, e))
def ToJsonString(self, message):
js = self._MessageToJsonObject(message)
return json.dumps(js, indent=2)
return js
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 self._WrapperMessageToJsonObject(message)
if full_name in _WKTJSONMETHODS:
return methodcaller(_WKTJSONMETHODS[full_name][0], message)(self)
js = {}
return self._RegularMessageToJsonObject(message, js)
def _RegularMessageToJsonObject(self, message, js):
"""Converts normal message according to Proto3 JSON Specification."""
fields = message.ListFields()
def _FieldToJsonObject(
field, value, including_default_value_fields=False):
"""Converts field value according to Proto3 JSON Specification."""
if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
return _MessageToJsonObject(value, including_default_value_fields)
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:
return enum_value.name
else:
raise SerializeToJsonError('Enum field contains an integer value '
'which can not mapped to an enum value.')
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING:
if field.type == descriptor.FieldDescriptor.TYPE_BYTES:
# Use base64 Data encoding for bytes
return base64.b64encode(value).decode('utf-8')
else:
return value
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL:
return bool(value)
elif field.cpp_type in _INT64_TYPES:
return str(value)
elif field.cpp_type in _FLOAT_TYPES:
if math.isinf(value):
if value < 0.0:
return _NEG_INFINITY
else:
return _INFINITY
if math.isnan(value):
return _NAN
return value
try:
for field, value in fields:
name = field.camelcase_name
if _IsMapEntry(field):
# Convert a map field.
v_field = field.message_type.fields_by_name['value']
js_map = {}
for key in value:
if isinstance(key, bool):
if key:
recorded_key = 'true'
else:
recorded_key = 'false'
else:
recorded_key = key
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] = [self._FieldToJsonObject(field, k)
for k in value]
else:
js[name] = self._FieldToJsonObject(field, value)
# Serialize default value if including_default_value_fields is True.
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.
if ((field.label != descriptor.FieldDescriptor.LABEL_REPEATED and
field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE) or
field.containing_oneof):
continue
name = field.camelcase_name
if name in js:
# Skip the field which has been serailized already.
continue
if _IsMapEntry(field):
js[name] = {}
elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
js[name] = []
else:
js[name] = self._FieldToJsonObject(field, field.default_value)
except ValueError as e:
raise SerializeToJsonError(
'Failed to serialize {0} field: {1}.'.format(field.name, e))
def _AnyMessageToJsonObject(message, including_default):
"""Converts Any message according to Proto3 JSON Specification."""
if not message.ListFields():
return {}
# Must print @type first, use OrderedDict instead of {}
js = OrderedDict()
type_url = message.type_url
js['@type'] = type_url
sub_message = _CreateMessageFromTypeUrl(type_url)
sub_message.ParseFromString(message.value)
message_descriptor = sub_message.DESCRIPTOR
full_name = message_descriptor.full_name
if _IsWrapperMessage(message_descriptor):
js['value'] = _WrapperMessageToJsonObject(sub_message)
return js
if full_name in _WKTJSONMETHODS:
js['value'] = _WKTJSONMETHODS[full_name][0](sub_message, including_default)
return js
return _RegularMessageToJsonObject(sub_message, js, including_default)
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."""
# 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):
"""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
# to JSON. The parse back result will be different from original message.
if which is None or which == 'null_value':
return None
if which == 'list_value':
return _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)
def _FieldToJsonObject(self, field, value):
"""Converts field value according to Proto3 JSON Specification."""
if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
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:
return enum_value.name
else:
raise SerializeToJsonError('Enum field contains an integer value '
'which can not mapped to an enum value.')
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING:
if field.type == descriptor.FieldDescriptor.TYPE_BYTES:
# Use base64 Data encoding for bytes
return base64.b64encode(value).decode('utf-8')
else:
return value
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL:
return bool(value)
elif field.cpp_type in _INT64_TYPES:
return str(value)
elif field.cpp_type in _FLOAT_TYPES:
if math.isinf(value):
if value < 0.0:
return _NEG_INFINITY
else:
return _INFINITY
if math.isnan(value):
return _NAN
return value
def _AnyMessageToJsonObject(self, message):
"""Converts Any message according to Proto3 JSON Specification."""
if not message.ListFields():
return {}
# Must print @type first, use OrderedDict instead of {}
js = OrderedDict()
type_url = message.type_url
js['@type'] = type_url
sub_message = _CreateMessageFromTypeUrl(type_url)
sub_message.ParseFromString(message.value)
message_descriptor = sub_message.DESCRIPTOR
full_name = message_descriptor.full_name
if _IsWrapperMessage(message_descriptor):
js['value'] = self._WrapperMessageToJsonObject(sub_message)
return js
if full_name in _WKTJSONMETHODS:
js['value'] = methodcaller(_WKTJSONMETHODS[full_name][0],
sub_message)(self)
return js
return self._RegularMessageToJsonObject(sub_message, js)
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(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
# to JSON. The parse back result will be different from original message.
if which is None or which == 'null_value':
return None
if which == '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 self._FieldToJsonObject(oneof_descriptor, value)
def _ListValueMessageToJsonObject(message, unused_including_default=False):
"""Converts ListValue message according to Proto3 JSON Specification."""
return [_ValueMessageToJsonObject(value)
for value in message.values]
def _ListValueMessageToJsonObject(self, message):
"""Converts ListValue message according to Proto3 JSON Specification."""
return [self._ValueMessageToJsonObject(value)
for value in message.values]
def _StructMessageToJsonObject(self, message):
"""Converts Struct message according to Proto3 JSON Specification."""
fields = message.fields
ret = {}
for key in fields:
ret[key] = self._ValueMessageToJsonObject(fields[key])
return ret
def _StructMessageToJsonObject(message, unused_including_default=False):
"""Converts Struct message according to Proto3 JSON Specification."""
fields = message.fields
ret = {}
for key in fields:
ret[key] = _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,213 +328,217 @@ 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):
"""Convert field value pairs into regular message.
_INT_OR_FLOAT = six.integer_types + (float,)
Args:
js: A JSON object to convert the field value pairs.
message: A regular protocol message to record the data.
Raises:
ParseError: In case of problems converting.
"""
names = []
message_descriptor = message.DESCRIPTOR
for name in js:
try:
field = message_descriptor.fields_by_camelcase_name.get(name, None)
if not field:
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(
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(
message.DESCRIPTOR.full_name, oneof_name))
names.append(oneof_name)
value = js[name]
if value is None:
message.ClearField(field.name)
continue
# Parse field value.
if _IsMapEntry(field):
message.ClearField(field.name)
_ConvertMapFieldValue(value, message, field)
elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
message.ClearField(field.name)
if not isinstance(value, list):
raise ParseError('repeated field {0} must be in [] which is '
'{1}.'.format(name, value))
if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
# Repeated message field.
for item in value:
sub_message = getattr(message, field.name).add()
# None is a null_value in Value.
if (item is None and
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)
else:
# Repeated scalar field.
for item in value:
if item is None:
raise ParseError('null is not allowed to be used as an element'
' in a repeated field.')
getattr(message, field.name).append(
_ConvertScalarFieldValue(item, field))
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
sub_message = getattr(message, field.name)
_ConvertMessage(value, sub_message)
else:
setattr(message, field.name, _ConvertScalarFieldValue(value, field))
except ParseError as e:
if field and field.containing_oneof is None:
raise ParseError('Failed to parse {0} field: {1}'.format(name, e))
else:
raise ParseError(str(e))
except ValueError as e:
raise ParseError('Failed to parse {0} field: {1}.'.format(name, e))
except TypeError as e:
raise ParseError('Failed to parse {0} field: {1}.'.format(name, e))
class _Parser(object):
"""JSON format parser for protocol message."""
def __init__(self,
ignore_unknown_fields):
self.ignore_unknown_fields = ignore_unknown_fields
def _ConvertMessage(value, message):
"""Convert a JSON object into a message.
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.
Args:
value: A JSON object.
message: A WKT or regular protocol message to record the data.
Raises:
ParseError: In case of convert problems.
"""
message_descriptor = message.DESCRIPTOR
full_name = message_descriptor.full_name
if _IsWrapperMessage(message_descriptor):
_ConvertWrapperMessage(value, message)
elif full_name in _WKTJSONMETHODS:
_WKTJSONMETHODS[full_name][1](value, message)
else:
_ConvertFieldValuePair(value, message)
def _ConvertAnyMessage(value, message):
"""Convert a JSON representation into Any message."""
if isinstance(value, dict) and not value:
return
try:
type_url = value['@type']
except KeyError:
raise ParseError('@type is missing when parsing any message.')
sub_message = _CreateMessageFromTypeUrl(type_url)
message_descriptor = sub_message.DESCRIPTOR
full_name = message_descriptor.full_name
if _IsWrapperMessage(message_descriptor):
_ConvertWrapperMessage(value['value'], sub_message)
elif full_name in _WKTJSONMETHODS:
_WKTJSONMETHODS[full_name][1](value['value'], sub_message)
else:
del value['@type']
_ConvertFieldValuePair(value, sub_message)
# Sets Any message
message.value = sub_message.SerializeToString()
message.type_url = type_url
def _ConvertGenericMessage(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)
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:
js: A JSON object to convert the field value pairs.
message: A regular protocol message to record the data.
Raises:
ParseError: In case of problems converting.
"""
names = []
message_descriptor = message.DESCRIPTOR
for name in js:
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(
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(
message.DESCRIPTOR.full_name, oneof_name))
names.append(oneof_name)
value = js[name]
if value is None:
message.ClearField(field.name)
continue
# Parse field value.
if _IsMapEntry(field):
message.ClearField(field.name)
self._ConvertMapFieldValue(value, message, field)
elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
message.ClearField(field.name)
if not isinstance(value, list):
raise ParseError('repeated field {0} must be in [] which is '
'{1}.'.format(name, value))
if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
# Repeated message field.
for item in value:
sub_message = getattr(message, field.name).add()
# None is a null_value in Value.
if (item is None and
sub_message.DESCRIPTOR.full_name != 'google.protobuf.Value'):
raise ParseError('null is not allowed to be used as an element'
' in a repeated field.')
self.ConvertMessage(item, sub_message)
else:
# Repeated scalar field.
for item in value:
if item is None:
raise ParseError('null is not allowed to be used as an element'
' in a repeated field.')
getattr(message, field.name).append(
_ConvertScalarFieldValue(item, field))
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
sub_message = getattr(message, field.name)
self.ConvertMessage(value, sub_message)
else:
setattr(message, field.name, _ConvertScalarFieldValue(value, field))
except ParseError as e:
if field and field.containing_oneof is None:
raise ParseError('Failed to parse {0} field: {1}'.format(name, e))
else:
raise ParseError(str(e))
except ValueError as e:
raise ParseError('Failed to parse {0} field: {1}.'.format(name, e))
except TypeError as e:
raise ParseError('Failed to parse {0} field: {1}.'.format(name, e))
def _ConvertAnyMessage(self, value, message):
"""Convert a JSON representation into Any message."""
if isinstance(value, dict) and not value:
return
try:
type_url = value['@type']
except KeyError:
raise ParseError('@type is missing when parsing any message.')
sub_message = _CreateMessageFromTypeUrl(type_url)
message_descriptor = sub_message.DESCRIPTOR
full_name = message_descriptor.full_name
if _IsWrapperMessage(message_descriptor):
self._ConvertWrapperMessage(value['value'], sub_message)
elif full_name in _WKTJSONMETHODS:
methodcaller(
_WKTJSONMETHODS[full_name][1], value['value'], sub_message)(self)
else:
del value['@type']
self._ConvertFieldValuePair(value, sub_message)
# Sets Any message
message.value = sub_message.SerializeToString()
message.type_url = type_url
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)
def _ConvertValueMessage(self, value, message):
"""Convert a JSON representation into Value message."""
if isinstance(value, dict):
self._ConvertStructMessage(value, message.struct_value)
elif isinstance(value, list):
self. _ConvertListValueMessage(value, message.list_value)
elif value is None:
message.null_value = 0
elif isinstance(value, bool):
message.bool_value = value
elif isinstance(value, six.string_types):
message.string_value = value
elif isinstance(value, _INT_OR_FLOAT):
message.number_value = value
else:
raise ParseError('Unexpected type for Value message.')
_INT_OR_FLOAT = six.integer_types + (float,)
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:
self._ConvertValueMessage(item, message.values.add())
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:
self._ConvertValueMessage(value[key], message.fields[key])
return
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 _ConvertValueMessage(value, message):
"""Convert a JSON representation into Value message."""
if isinstance(value, dict):
_ConvertStructMessage(value, message.struct_value)
elif isinstance(value, list):
_ConvertListValueMessage(value, message.list_value)
elif value is None:
message.null_value = 0
elif isinstance(value, bool):
message.bool_value = value
elif isinstance(value, six.string_types):
message.string_value = value
elif isinstance(value, _INT_OR_FLOAT):
message.number_value = value
else:
raise ParseError('Unexpected type for Value message.')
def _ConvertListValueMessage(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())
def _ConvertStructMessage(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])
return
def _ConvertWrapperMessage(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):
"""Convert map field value for a message map field.
def _ConvertMapFieldValue(self, value, message, field):
"""Convert map field value for a message map field.
Args:
value: A JSON object to convert the map field value.
message: A protocol message to record the converted data.
field: The descriptor of the map field to be converted.
Args:
value: A JSON object to convert the map field value.
message: A protocol message to record the converted data.
field: The descriptor of the map field to be converted.
Raises:
ParseError: In case of convert problems.
"""
if not isinstance(value, dict):
raise ParseError(
'Map field {0} must be in a dict which is {1}.'.format(
field.name, value))
key_field = field.message_type.fields_by_name['key']
value_field = field.message_type.fields_by_name['value']
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])
else:
getattr(message, field.name)[key_value] = _ConvertScalarFieldValue(
value[key], value_field)
Raises:
ParseError: In case of convert problems.
"""
if not isinstance(value, dict):
raise ParseError(
'Map field {0} must be in a dict which is {1}.'.format(
field.name, value))
key_field = field.message_type.fields_by_name['key']
value_field = field.message_type.fields_by_name['value']
for key in value:
key_value = _ConvertScalarFieldValue(key, key_field, True)
if value_field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
self.ConvertMessage(value[key], getattr(
message, field.name)[key_value])
else:
getattr(message, field.name)[key_value] = _ConvertScalarFieldValue(
value[key], value_field)
def _ConvertScalarFieldValue(value, field, require_str=False):
@ -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) {
@ -703,14 +721,20 @@ static PyMethodDef SeqMethods[] = {
};
static PySequenceMethods SeqSequenceMethods = {
(lenfunc)Length, // sq_length
0, // sq_concat
0, // sq_repeat
(ssizeargfunc)GetItem, // sq_item
0, // sq_slice
0, // sq_ass_item
0, // sq_ass_slice
(objobjproc)SeqContains, // sq_contains
(lenfunc)Length, // sq_length
0, // sq_concat
0, // sq_repeat
(ssizeargfunc)GetItem, // sq_item
0, // sq_slice
0, // sq_ass_item
0, // sq_ass_slice
(objobjproc)SeqContains, // sq_contains
};
static PyMappingMethods SeqMappingMethods = {
(lenfunc)Length, // mp_length
(binaryfunc)SeqSubscript, // mp_subscript
0, // mp_ass_subscript
};
PyTypeObject DescriptorSequence_Type = {
@ -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
}
}

File diff suppressed because it is too large Load Diff

@ -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;
}
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);
}
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,18 +641,23 @@ CommandLineInterface::MemoryOutputStream::~MemoryOutputStream() {
return;
}
// 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.
pos = target->find_last_of('\n', pos);
if (pos == string::npos) {
// Insertion point is on the first line.
pos = 0;
if ((pos > 3) && (target->substr(pos - 3, 2) == "/*")) {
// Support for inline "/* @@protoc_insertion_point() */"
pos = pos - 3;
} else {
// Advance to character after '\n'.
++pos;
// 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.
pos = target->find_last_of('\n', pos);
if (pos == string::npos) {
// Insertion point is on the first line.
pos = 0;
} else {
// Advance to character after '\n'.
++pos;
}
}
// Extract indent.

@ -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);
key_field, options_, true, variables_,
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