Submit recent changes from internal branch. See CHANGES.txt for more details.
This commit is contained in:
parent
80aa23df6c
commit
33165fe0d5
63
CHANGES.txt
63
CHANGES.txt
@ -1,3 +1,66 @@
|
||||
2010-11-01 version 2.4.0:
|
||||
|
||||
General
|
||||
* The RPC (cc|java|py)_generic_services default value is now false instead of
|
||||
true.
|
||||
* Custom options can have aggregate types. For example,
|
||||
message MyOption {
|
||||
optional string comment = 1;
|
||||
optional string author = 2;
|
||||
}
|
||||
extend google.protobuf.FieldOptions {
|
||||
optional MyOption myoption = 12345;
|
||||
}
|
||||
This option can now be set as follows:
|
||||
message SomeType {
|
||||
optional int32 field = 1 [(myoption) = { comment:'x' author:'y' }];
|
||||
}
|
||||
|
||||
C++
|
||||
* Various speed and code size optimizations.
|
||||
* Added a release_foo() method on string and message fields.
|
||||
* Fixed gzip_output_stream sub-stream handling.
|
||||
|
||||
Java
|
||||
* Builders now maintain sub-builders for sub-messages. Use getFooBuilder() to
|
||||
get the builder for the sub-message "foo". This allows you to repeatedly
|
||||
modify deeply-nested sub-messages without rebuilding them.
|
||||
* Builder.build() no longer invalidates the Builder for generated messages
|
||||
(You may continue to modify it and then build another message).
|
||||
* Code generator will generate efficient equals() and hashCode()
|
||||
implementations if new option java_generate_equals_and_hash is enabled.
|
||||
(Otherwise, reflection-based implementations are used.)
|
||||
* Generated messages now implement Serializable.
|
||||
* Fields with [deprecated=true] will be marked with @Deprecated in Java.
|
||||
* Added lazy conversion of UTF-8 encoded strings to String objects to improve
|
||||
performance.
|
||||
* Various optimizations.
|
||||
* Enum value can be accessed directly, instead of calling getNumber() on the
|
||||
enum member.
|
||||
* For each enum value, an integer constant is also generated with the suffix
|
||||
_VALUE.
|
||||
|
||||
Python
|
||||
* Added an experimental C++ implementation for Python messages via a Python
|
||||
extension. Implementation type is controlled by an environment variable
|
||||
PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION (valid values: "cpp" and "python")
|
||||
The default value is currently "python" but will be changed to "cpp" in
|
||||
future release.
|
||||
* Improved performance on message instantiation significantly.
|
||||
Most of the work on message instantiation is done just once per message
|
||||
class, instead of once per message instance.
|
||||
* Improved performance on text message parsing.
|
||||
* Allow add() to forward keyword arguments to the concrete class.
|
||||
E.g. instead of
|
||||
item = repeated_field.add()
|
||||
item.foo = bar
|
||||
item.baz = quux
|
||||
You can do:
|
||||
repeated_field.add(foo=bar, baz=quux)
|
||||
* Added a sort() interface to the BaseContainer.
|
||||
* Added an extend() method to repeated composite fields.
|
||||
* Added UTF8 debug string support.
|
||||
|
||||
2010-01-08 version 2.3.0:
|
||||
|
||||
General
|
||||
|
32
Makefile.am
32
Makefile.am
@ -83,33 +83,57 @@ EXTRA_DIST = \
|
||||
java/src/main/java/com/google/protobuf/GeneratedMessageLite.java \
|
||||
java/src/main/java/com/google/protobuf/Internal.java \
|
||||
java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java \
|
||||
java/src/main/java/com/google/protobuf/LazyStringArrayList.java \
|
||||
java/src/main/java/com/google/protobuf/LazyStringList.java \
|
||||
java/src/main/java/com/google/protobuf/Message.java \
|
||||
java/src/main/java/com/google/protobuf/MessageLite.java \
|
||||
java/src/main/java/com/google/protobuf/MessageLiteOrBuilder.java \
|
||||
java/src/main/java/com/google/protobuf/MessageOrBuilder.java \
|
||||
java/src/main/java/com/google/protobuf/ProtocolMessageEnum.java \
|
||||
java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java \
|
||||
java/src/main/java/com/google/protobuf/RpcCallback.java \
|
||||
java/src/main/java/com/google/protobuf/RpcChannel.java \
|
||||
java/src/main/java/com/google/protobuf/RpcController.java \
|
||||
java/src/main/java/com/google/protobuf/RpcUtil.java \
|
||||
java/src/main/java/com/google/protobuf/Service.java \
|
||||
java/src/main/java/com/google/protobuf/ServiceException.java \
|
||||
java/src/main/java/com/google/protobuf/SingleFieldBuilder.java \
|
||||
java/src/main/java/com/google/protobuf/SmallSortedMap.java \
|
||||
java/src/main/java/com/google/protobuf/TextFormat.java \
|
||||
java/src/main/java/com/google/protobuf/UninitializedMessageException.java \
|
||||
java/src/main/java/com/google/protobuf/UnknownFieldSet.java \
|
||||
java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java \
|
||||
java/src/main/java/com/google/protobuf/WireFormat.java \
|
||||
java/src/test/java/com/google/protobuf/AbstractMessageTest.java \
|
||||
java/src/test/java/com/google/protobuf/CodedInputStreamTest.java \
|
||||
java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java \
|
||||
java/src/test/java/com/google/protobuf/DeprecatedFieldTest.java \
|
||||
java/src/test/java/com/google/protobuf/DescriptorsTest.java \
|
||||
java/src/test/java/com/google/protobuf/DynamicMessageTest.java \
|
||||
java/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java \
|
||||
java/src/test/java/com/google/protobuf/GeneratedMessageTest.java \
|
||||
java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java \
|
||||
java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java \
|
||||
java/src/test/java/com/google/protobuf/LiteTest.java \
|
||||
java/src/test/java/com/google/protobuf/MessageTest.java \
|
||||
java/src/test/java/com/google/protobuf/NestedBuildersTest.java \
|
||||
java/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java \
|
||||
java/src/test/java/com/google/protobuf/ServiceTest.java \
|
||||
java/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java \
|
||||
java/src/test/java/com/google/protobuf/SmallSortedMapTest.java \
|
||||
java/src/test/java/com/google/protobuf/TestBadIdentifiers.java \
|
||||
java/src/test/java/com/google/protobuf/TestUtil.java \
|
||||
java/src/test/java/com/google/protobuf/TextFormatTest.java \
|
||||
java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java \
|
||||
java/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java \
|
||||
java/src/test/java/com/google/protobuf/WireFormatTest.java \
|
||||
java/src/test/java/com/google/protobuf/multiple_files_test.proto \
|
||||
java/src/test/java/com/google/protobuf/nested_builders_test.proto \
|
||||
java/src/test/java/com/google/protobuf/nested_extension.proto \
|
||||
java/src/test/java/com/google/protobuf/nested_extension_lite.proto \
|
||||
java/src/test/java/com/google/protobuf/non_nested_extension.proto \
|
||||
java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto \
|
||||
java/src/test/java/com/google/protobuf/test_bad_identifiers.proto \
|
||||
java/pom.xml \
|
||||
java/README.txt \
|
||||
python/google/protobuf/internal/generator_test.py \
|
||||
@ -121,6 +145,9 @@ EXTRA_DIST = \
|
||||
python/google/protobuf/internal/message_test.py \
|
||||
python/google/protobuf/internal/more_extensions.proto \
|
||||
python/google/protobuf/internal/more_messages.proto \
|
||||
python/google/protobuf/internal/python_message.py \
|
||||
python/google/protobuf/internal/cpp_message.py \
|
||||
python/google/protobuf/internal/api_implementation.py \
|
||||
python/google/protobuf/internal/reflection_test.py \
|
||||
python/google/protobuf/internal/service_reflection_test.py \
|
||||
python/google/protobuf/internal/test_util.py \
|
||||
@ -129,6 +156,11 @@ EXTRA_DIST = \
|
||||
python/google/protobuf/internal/wire_format.py \
|
||||
python/google/protobuf/internal/wire_format_test.py \
|
||||
python/google/protobuf/internal/__init__.py \
|
||||
python/google/protobuf/pyext/python-proto2.cc \
|
||||
python/google/protobuf/pyext/python_descriptor.cc \
|
||||
python/google/protobuf/pyext/python_descriptor.h \
|
||||
python/google/protobuf/pyext/python_protobuf.cc \
|
||||
python/google/protobuf/pyext/python_protobuf.h \
|
||||
python/google/protobuf/descriptor.py \
|
||||
python/google/protobuf/message.py \
|
||||
python/google/protobuf/reflection.py \
|
||||
|
@ -105,6 +105,12 @@
|
||||
<arg value="../src/google/protobuf/unittest_mset.proto" />
|
||||
<arg
|
||||
value="src/test/java/com/google/protobuf/multiple_files_test.proto" />
|
||||
<arg value="src/test/java/com/google/protobuf/nested_builders_test.proto" />
|
||||
<arg value="src/test/java/com/google/protobuf/nested_extension.proto" />
|
||||
<arg value="src/test/java/com/google/protobuf/nested_extension_lite.proto" />
|
||||
<arg value="src/test/java/com/google/protobuf/non_nested_extension.proto" />
|
||||
<arg value="src/test/java/com/google/protobuf/non_nested_extension_lite.proto" />
|
||||
<arg value="src/test/java/com/google/protobuf/test_bad_identifiers.proto" />
|
||||
<arg
|
||||
value="../src/google/protobuf/unittest_optimize_for.proto" />
|
||||
<arg
|
||||
|
@ -32,9 +32,10 @@ package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.Descriptors.Descriptor;
|
||||
import com.google.protobuf.Descriptors.FieldDescriptor;
|
||||
import com.google.protobuf.Internal.EnumLite;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -167,11 +168,66 @@ public abstract class AbstractMessage extends AbstractMessageLite
|
||||
public int hashCode() {
|
||||
int hash = 41;
|
||||
hash = (19 * hash) + getDescriptorForType().hashCode();
|
||||
hash = (53 * hash) + getAllFields().hashCode();
|
||||
hash = hashFields(hash, getAllFields());
|
||||
hash = (29 * hash) + getUnknownFields().hashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
/** Get a hash code for given fields and values, using the given seed. */
|
||||
@SuppressWarnings("unchecked")
|
||||
protected int hashFields(int hash, Map<FieldDescriptor, Object> map) {
|
||||
for (Map.Entry<FieldDescriptor, Object> entry : map.entrySet()) {
|
||||
FieldDescriptor field = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
hash = (37 * hash) + field.getNumber();
|
||||
if (field.getType() != FieldDescriptor.Type.ENUM){
|
||||
hash = (53 * hash) + value.hashCode();
|
||||
} else if (field.isRepeated()) {
|
||||
List<? extends EnumLite> list = (List<? extends EnumLite>) value;
|
||||
hash = (53 * hash) + hashEnumList(list);
|
||||
} else {
|
||||
hash = (53 * hash) + hashEnum((EnumLite) value);
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for implementing {@link Message#hashCode()}.
|
||||
* @see Boolean#hashCode()
|
||||
*/
|
||||
protected static int hashLong(long n) {
|
||||
return (int) (n ^ (n >>> 32));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for implementing {@link Message#hashCode()}.
|
||||
* @see Boolean#hashCode()
|
||||
*/
|
||||
protected static int hashBoolean(boolean b) {
|
||||
return b ? 1231 : 1237;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for implementing {@link Message#hashCode()}.
|
||||
* <p>
|
||||
* This is needed because {@link java.lang.Enum#hashCode()} is final, but we
|
||||
* need to use the field number as the hash code to ensure compatibility
|
||||
* between statically and dynamically generated enum objects.
|
||||
*/
|
||||
protected static int hashEnum(EnumLite e) {
|
||||
return e.getNumber();
|
||||
}
|
||||
|
||||
/** Helper method for implementing {@link Message#hashCode()}. */
|
||||
protected static int hashEnumList(List<? extends EnumLite> list) {
|
||||
int hash = 1;
|
||||
for (EnumLite e : list) {
|
||||
hash = 31 * hash + hashEnum(e);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
/**
|
||||
|
@ -193,6 +193,18 @@ public final class ByteString {
|
||||
System.arraycopy(bytes, sourceOffset, target, targetOffset, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies bytes into a ByteBuffer.
|
||||
*
|
||||
* @param target ByteBuffer to copy into.
|
||||
* @throws ReadOnlyBufferException if the {@code target} is read-only
|
||||
* @throws BufferOverflowException if the {@code target}'s remaining()
|
||||
* space is not large enough to hold the data.
|
||||
*/
|
||||
public void copyTo(ByteBuffer target) {
|
||||
target.put(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies bytes to a {@code byte[]}.
|
||||
*/
|
||||
|
@ -67,7 +67,25 @@ public final class CodedInputStream {
|
||||
*/
|
||||
public static CodedInputStream newInstance(final byte[] buf, final int off,
|
||||
final int len) {
|
||||
return new CodedInputStream(buf, off, len);
|
||||
CodedInputStream result = new CodedInputStream(buf, off, len);
|
||||
try {
|
||||
// Some uses of CodedInputStream can be more efficient if they know
|
||||
// exactly how many bytes are available. By pushing the end point of the
|
||||
// buffer as a limit, we allow them to get this information via
|
||||
// getBytesUntilLimit(). Pushing a limit that we know is at the end of
|
||||
// the stream can never hurt, since we can never past that point anyway.
|
||||
result.pushLimit(len);
|
||||
} catch (InvalidProtocolBufferException ex) {
|
||||
// The only reason pushLimit() might throw an exception here is if len
|
||||
// is negative. Normally pushLimit()'s parameter comes directly off the
|
||||
// wire, so it's important to catch exceptions in case of corrupt or
|
||||
// malicious data. However, in this case, we expect that len is not a
|
||||
// user-supplied value, so we can assume that it being negative indicates
|
||||
// a programming error. Therefore, throwing an unchecked exception is
|
||||
// appropriate.
|
||||
throw new IllegalArgumentException(ex);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
@ -263,7 +281,9 @@ public final class CodedInputStream {
|
||||
/** Read a {@code bytes} field value from the stream. */
|
||||
public ByteString readBytes() throws IOException {
|
||||
final int size = readRawVarint32();
|
||||
if (size <= (bufferSize - bufferPos) && size > 0) {
|
||||
if (size == 0) {
|
||||
return ByteString.EMPTY;
|
||||
} else if (size <= (bufferSize - bufferPos) && size > 0) {
|
||||
// Fast path: We already have the bytes in a contiguous buffer, so
|
||||
// just copy directly from it.
|
||||
final ByteString result = ByteString.copyFrom(buffer, bufferPos, size);
|
||||
@ -368,8 +388,8 @@ public final class CodedInputStream {
|
||||
* has already read one byte. This allows the caller to determine if EOF
|
||||
* has been reached before attempting to read.
|
||||
*/
|
||||
static int readRawVarint32(final int firstByte,
|
||||
final InputStream input) throws IOException {
|
||||
public static int readRawVarint32(
|
||||
final int firstByte, final InputStream input) throws IOException {
|
||||
if ((firstByte & 0x80) == 0) {
|
||||
return firstByte;
|
||||
}
|
||||
@ -847,19 +867,19 @@ public final class CodedInputStream {
|
||||
} else {
|
||||
// Skipping more bytes than are in the buffer. First skip what we have.
|
||||
int pos = bufferSize - bufferPos;
|
||||
totalBytesRetired += pos;
|
||||
bufferPos = 0;
|
||||
bufferSize = 0;
|
||||
bufferPos = bufferSize;
|
||||
|
||||
// Then skip directly from the InputStream for the rest.
|
||||
while (pos < size) {
|
||||
final int n = (input == null) ? -1 : (int) input.skip(size - pos);
|
||||
if (n <= 0) {
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
}
|
||||
pos += n;
|
||||
totalBytesRetired += n;
|
||||
// Keep refilling the buffer until we get to the point we wanted to skip
|
||||
// to. This has the side effect of ensuring the limits are updated
|
||||
// correctly.
|
||||
refillBuffer(true);
|
||||
while (size - pos > bufferSize) {
|
||||
pos += bufferSize;
|
||||
bufferPos = bufferSize;
|
||||
refillBuffer(true);
|
||||
}
|
||||
|
||||
bufferPos = size - pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ package com.google.protobuf;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Encodes and writes protocol message fields.
|
||||
@ -381,9 +382,8 @@ public final class CodedOutputStream {
|
||||
|
||||
/** Write a {@code bytes} field to the stream. */
|
||||
public void writeBytesNoTag(final ByteString value) throws IOException {
|
||||
final byte[] bytes = value.toByteArray();
|
||||
writeRawVarint32(bytes.length);
|
||||
writeRawBytes(bytes);
|
||||
writeRawVarint32(value.size());
|
||||
writeRawBytes(value);
|
||||
}
|
||||
|
||||
/** Write a {@code uint32} field to the stream. */
|
||||
@ -870,6 +870,11 @@ public final class CodedOutputStream {
|
||||
writeRawByte((byte) value);
|
||||
}
|
||||
|
||||
/** Write a byte string. */
|
||||
public void writeRawBytes(final ByteString value) throws IOException {
|
||||
writeRawBytes(value, 0, value.size());
|
||||
}
|
||||
|
||||
/** Write an array of bytes. */
|
||||
public void writeRawBytes(final byte[] value) throws IOException {
|
||||
writeRawBytes(value, 0, value.length);
|
||||
@ -906,6 +911,53 @@ public final class CodedOutputStream {
|
||||
}
|
||||
}
|
||||
|
||||
/** Write part of a byte string. */
|
||||
public void writeRawBytes(final ByteString value, int offset, int length)
|
||||
throws IOException {
|
||||
if (limit - position >= length) {
|
||||
// We have room in the current buffer.
|
||||
value.copyTo(buffer, offset, position, length);
|
||||
position += length;
|
||||
} else {
|
||||
// Write extends past current buffer. Fill the rest of this buffer and
|
||||
// flush.
|
||||
final int bytesWritten = limit - position;
|
||||
value.copyTo(buffer, offset, position, bytesWritten);
|
||||
offset += bytesWritten;
|
||||
length -= bytesWritten;
|
||||
position = limit;
|
||||
refreshBuffer();
|
||||
|
||||
// Now deal with the rest.
|
||||
// Since we have an output stream, this is our buffer
|
||||
// and buffer offset == 0
|
||||
if (length <= limit) {
|
||||
// Fits in new buffer.
|
||||
value.copyTo(buffer, offset, 0, length);
|
||||
position = length;
|
||||
} else {
|
||||
// Write is very big, but we can't do it all at once without allocating
|
||||
// an a copy of the byte array since ByteString does not give us access
|
||||
// to the underlying bytes. Use the InputStream interface on the
|
||||
// ByteString and our buffer to copy between the two.
|
||||
InputStream inputStreamFrom = value.newInput();
|
||||
if (offset != inputStreamFrom.skip(offset)) {
|
||||
throw new IllegalStateException("Skip failed? Should never happen.");
|
||||
}
|
||||
// Use the buffer as the temporary buffer to avoid allocating memory.
|
||||
while (length > 0) {
|
||||
int bytesToRead = Math.min(length, limit);
|
||||
int bytesRead = inputStreamFrom.read(buffer, 0, bytesToRead);
|
||||
if (bytesRead != bytesToRead) {
|
||||
throw new IllegalStateException("Read failed? Should never happen");
|
||||
}
|
||||
output.write(buffer, 0, bytesRead);
|
||||
length -= bytesRead;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Encode and write a tag. */
|
||||
public void writeTag(final int fieldNumber, final int wireType)
|
||||
throws IOException {
|
||||
|
@ -46,6 +46,11 @@ import java.io.UnsupportedEncodingException;
|
||||
* its fields and other information about a type. You can get a message
|
||||
* type's descriptor by calling {@code MessageType.getDescriptor()}, or
|
||||
* (given a message object of the type) {@code message.getDescriptorForType()}.
|
||||
* Furthermore, each message is associated with a {@link FileDescriptor} for
|
||||
* a relevant {@code .proto} file. You can obtain it by calling
|
||||
* {@code Descriptor.getFile()}. A {@link FileDescriptor} contains descriptors
|
||||
* for all the messages defined in that file, and file descriptors for all the
|
||||
* imported {@code .proto} files.
|
||||
*
|
||||
* Descriptors are built from DescriptorProtos, as defined in
|
||||
* {@code google/protobuf/descriptor.proto}.
|
||||
@ -55,6 +60,9 @@ import java.io.UnsupportedEncodingException;
|
||||
public final class Descriptors {
|
||||
/**
|
||||
* Describes a {@code .proto} file, including everything defined within.
|
||||
* That includes, in particular, descriptors for all the messages and
|
||||
* file descriptors for all other imported {@code .proto} files
|
||||
* (dependencies).
|
||||
*/
|
||||
public static final class FileDescriptor {
|
||||
/** Convert the descriptor to its protocol message representation. */
|
||||
|
@ -33,7 +33,6 @@ package com.google.protobuf;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.TreeMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.io.IOException;
|
||||
@ -67,16 +66,12 @@ final class FieldSet<FieldDescriptorType extends
|
||||
MessageLite.Builder to, MessageLite from);
|
||||
}
|
||||
|
||||
private Map<FieldDescriptorType, Object> fields;
|
||||
private final SmallSortedMap<FieldDescriptorType, Object> fields;
|
||||
private boolean isImmutable;
|
||||
|
||||
/** Construct a new FieldSet. */
|
||||
private FieldSet() {
|
||||
// Use a TreeMap because fields need to be in canonical order when
|
||||
// serializing.
|
||||
// TODO(kenton): Maybe use some sort of sparse array instead? It would
|
||||
// even make sense to store the first 16 or so tags in a flat array
|
||||
// to make DynamicMessage faster.
|
||||
fields = new TreeMap<FieldDescriptorType, Object>();
|
||||
this.fields = SmallSortedMap.newFieldMap(16);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,7 +79,8 @@ final class FieldSet<FieldDescriptorType extends
|
||||
* DEFAULT_INSTANCE.
|
||||
*/
|
||||
private FieldSet(final boolean dummy) {
|
||||
this.fields = Collections.emptyMap();
|
||||
this.fields = SmallSortedMap.newFieldMap(0);
|
||||
makeImmutable();
|
||||
}
|
||||
|
||||
/** Construct a new FieldSet. */
|
||||
@ -105,14 +101,45 @@ final class FieldSet<FieldDescriptorType extends
|
||||
/** Make this FieldSet immutable from this point forward. */
|
||||
@SuppressWarnings("unchecked")
|
||||
public void makeImmutable() {
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry:
|
||||
fields.entrySet()) {
|
||||
if (entry.getKey().isRepeated()) {
|
||||
final List value = (List)entry.getValue();
|
||||
fields.put(entry.getKey(), Collections.unmodifiableList(value));
|
||||
}
|
||||
if (isImmutable) {
|
||||
return;
|
||||
}
|
||||
fields = Collections.unmodifiableMap(fields);
|
||||
fields.makeImmutable();
|
||||
isImmutable = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retuns whether the FieldSet is immutable. This is true if it is the
|
||||
* {@link #emptySet} or if {@link #makeImmutable} were called.
|
||||
*
|
||||
* @return whether the FieldSet is immutable.
|
||||
*/
|
||||
public boolean isImmutable() {
|
||||
return isImmutable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones the FieldSet. The returned FieldSet will be mutable even if the
|
||||
* original FieldSet was immutable.
|
||||
*
|
||||
* @return the newly cloned FieldSet
|
||||
*/
|
||||
@Override
|
||||
public FieldSet<FieldDescriptorType> clone() {
|
||||
// We can't just call fields.clone because List objects in the map
|
||||
// should not be shared.
|
||||
FieldSet<FieldDescriptorType> clone = FieldSet.newFieldSet();
|
||||
for (int i = 0; i < fields.getNumArrayEntries(); i++) {
|
||||
Map.Entry<FieldDescriptorType, Object> entry = fields.getArrayEntryAt(i);
|
||||
FieldDescriptorType descriptor = entry.getKey();
|
||||
clone.setField(descriptor, entry.getValue());
|
||||
}
|
||||
for (Map.Entry<FieldDescriptorType, Object> entry :
|
||||
fields.getOverflowEntries()) {
|
||||
FieldDescriptorType descriptor = entry.getKey();
|
||||
clone.setField(descriptor, entry.getValue());
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
@ -126,12 +153,13 @@ final class FieldSet<FieldDescriptorType extends
|
||||
* Get a simple map containing all the fields.
|
||||
*/
|
||||
public Map<FieldDescriptorType, Object> getAllFields() {
|
||||
return Collections.unmodifiableMap(fields);
|
||||
return fields.isImmutable() ? fields : Collections.unmodifiableMap(fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an iterator to the field map. This iterator should not be leaked
|
||||
* out of the protobuf library as it is not protected from mutation.
|
||||
* Get an iterator to the field map. This iterator should not be leaked out
|
||||
* of the protobuf library as it is not protected from mutation when
|
||||
* fields is not immutable.
|
||||
*/
|
||||
public Iterator<Map.Entry<FieldDescriptorType, Object>> iterator() {
|
||||
return fields.entrySet().iterator();
|
||||
@ -336,27 +364,39 @@ final class FieldSet<FieldDescriptorType extends
|
||||
* aren't actually present in the set, it is up to the caller to check
|
||||
* that all required fields are present.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean isInitialized() {
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry:
|
||||
fields.entrySet()) {
|
||||
final FieldDescriptorType descriptor = entry.getKey();
|
||||
if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
|
||||
if (descriptor.isRepeated()) {
|
||||
for (final MessageLite element:
|
||||
(List<MessageLite>) entry.getValue()) {
|
||||
if (!element.isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!((MessageLite) entry.getValue()).isInitialized()) {
|
||||
for (int i = 0; i < fields.getNumArrayEntries(); i++) {
|
||||
if (!isInitialized(fields.getArrayEntryAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry :
|
||||
fields.getOverflowEntries()) {
|
||||
if (!isInitialized(entry)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private boolean isInitialized(
|
||||
final Map.Entry<FieldDescriptorType, Object> entry) {
|
||||
final FieldDescriptorType descriptor = entry.getKey();
|
||||
if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
|
||||
if (descriptor.isRepeated()) {
|
||||
for (final MessageLite element:
|
||||
(List<MessageLite>) entry.getValue()) {
|
||||
if (!element.isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!((MessageLite) entry.getValue()).isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -378,39 +418,48 @@ final class FieldSet<FieldDescriptorType extends
|
||||
/**
|
||||
* Like {@link #mergeFrom(Message)}, but merges from another {@link FieldSet}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void mergeFrom(final FieldSet<FieldDescriptorType> other) {
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry:
|
||||
other.fields.entrySet()) {
|
||||
final FieldDescriptorType descriptor = entry.getKey();
|
||||
final Object otherValue = entry.getValue();
|
||||
for (int i = 0; i < other.fields.getNumArrayEntries(); i++) {
|
||||
mergeFromField(other.fields.getArrayEntryAt(i));
|
||||
}
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry :
|
||||
other.fields.getOverflowEntries()) {
|
||||
mergeFromField(entry);
|
||||
}
|
||||
}
|
||||
|
||||
if (descriptor.isRepeated()) {
|
||||
Object value = fields.get(descriptor);
|
||||
if (value == null) {
|
||||
// Our list is empty, but we still need to make a defensive copy of
|
||||
// the other list since we don't know if the other FieldSet is still
|
||||
// mutable.
|
||||
fields.put(descriptor, new ArrayList((List) otherValue));
|
||||
} else {
|
||||
// Concatenate the lists.
|
||||
((List) value).addAll((List) otherValue);
|
||||
}
|
||||
} else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
|
||||
Object value = fields.get(descriptor);
|
||||
if (value == null) {
|
||||
fields.put(descriptor, otherValue);
|
||||
} else {
|
||||
// Merge the messages.
|
||||
fields.put(descriptor,
|
||||
descriptor.internalMergeFrom(
|
||||
((MessageLite) value).toBuilder(), (MessageLite) otherValue)
|
||||
.build());
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
private void mergeFromField(
|
||||
final Map.Entry<FieldDescriptorType, Object> entry) {
|
||||
final FieldDescriptorType descriptor = entry.getKey();
|
||||
final Object otherValue = entry.getValue();
|
||||
|
||||
if (descriptor.isRepeated()) {
|
||||
Object value = fields.get(descriptor);
|
||||
if (value == null) {
|
||||
// Our list is empty, but we still need to make a defensive copy of
|
||||
// the other list since we don't know if the other FieldSet is still
|
||||
// mutable.
|
||||
fields.put(descriptor, new ArrayList((List) otherValue));
|
||||
} else {
|
||||
fields.put(descriptor, otherValue);
|
||||
// Concatenate the lists.
|
||||
((List) value).addAll((List) otherValue);
|
||||
}
|
||||
} else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
|
||||
Object value = fields.get(descriptor);
|
||||
if (value == null) {
|
||||
fields.put(descriptor, otherValue);
|
||||
} else {
|
||||
// Merge the messages.
|
||||
fields.put(
|
||||
descriptor,
|
||||
descriptor.internalMergeFrom(
|
||||
((MessageLite) value).toBuilder(), (MessageLite) otherValue)
|
||||
.build());
|
||||
}
|
||||
|
||||
} else {
|
||||
fields.put(descriptor, otherValue);
|
||||
}
|
||||
}
|
||||
|
||||
@ -468,8 +517,13 @@ final class FieldSet<FieldDescriptorType extends
|
||||
/** See {@link Message#writeTo(CodedOutputStream)}. */
|
||||
public void writeTo(final CodedOutputStream output)
|
||||
throws IOException {
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry:
|
||||
fields.entrySet()) {
|
||||
for (int i = 0; i < fields.getNumArrayEntries(); i++) {
|
||||
final Map.Entry<FieldDescriptorType, Object> entry =
|
||||
fields.getArrayEntryAt(i);
|
||||
writeField(entry.getKey(), entry.getValue(), output);
|
||||
}
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry :
|
||||
fields.getOverflowEntries()) {
|
||||
writeField(entry.getKey(), entry.getValue(), output);
|
||||
}
|
||||
}
|
||||
@ -479,16 +533,25 @@ final class FieldSet<FieldDescriptorType extends
|
||||
*/
|
||||
public void writeMessageSetTo(final CodedOutputStream output)
|
||||
throws IOException {
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry:
|
||||
fields.entrySet()) {
|
||||
final FieldDescriptorType descriptor = entry.getKey();
|
||||
if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE &&
|
||||
!descriptor.isRepeated() && !descriptor.isPacked()) {
|
||||
output.writeMessageSetExtension(entry.getKey().getNumber(),
|
||||
(MessageLite) entry.getValue());
|
||||
} else {
|
||||
writeField(descriptor, entry.getValue(), output);
|
||||
}
|
||||
for (int i = 0; i < fields.getNumArrayEntries(); i++) {
|
||||
writeMessageSetTo(fields.getArrayEntryAt(i), output);
|
||||
}
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry :
|
||||
fields.getOverflowEntries()) {
|
||||
writeMessageSetTo(entry, output);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeMessageSetTo(
|
||||
final Map.Entry<FieldDescriptorType, Object> entry,
|
||||
final CodedOutputStream output) throws IOException {
|
||||
final FieldDescriptorType descriptor = entry.getKey();
|
||||
if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE &&
|
||||
!descriptor.isRepeated() && !descriptor.isPacked()) {
|
||||
output.writeMessageSetExtension(entry.getKey().getNumber(),
|
||||
(MessageLite) entry.getValue());
|
||||
} else {
|
||||
writeField(descriptor, entry.getValue(), output);
|
||||
}
|
||||
}
|
||||
|
||||
@ -593,8 +656,13 @@ final class FieldSet<FieldDescriptorType extends
|
||||
*/
|
||||
public int getSerializedSize() {
|
||||
int size = 0;
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry:
|
||||
fields.entrySet()) {
|
||||
for (int i = 0; i < fields.getNumArrayEntries(); i++) {
|
||||
final Map.Entry<FieldDescriptorType, Object> entry =
|
||||
fields.getArrayEntryAt(i);
|
||||
size += computeFieldSize(entry.getKey(), entry.getValue());
|
||||
}
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry :
|
||||
fields.getOverflowEntries()) {
|
||||
size += computeFieldSize(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return size;
|
||||
@ -605,20 +673,28 @@ final class FieldSet<FieldDescriptorType extends
|
||||
*/
|
||||
public int getMessageSetSerializedSize() {
|
||||
int size = 0;
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry:
|
||||
fields.entrySet()) {
|
||||
final FieldDescriptorType descriptor = entry.getKey();
|
||||
if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE &&
|
||||
!descriptor.isRepeated() && !descriptor.isPacked()) {
|
||||
size += CodedOutputStream.computeMessageSetExtensionSize(
|
||||
entry.getKey().getNumber(), (MessageLite) entry.getValue());
|
||||
} else {
|
||||
size += computeFieldSize(descriptor, entry.getValue());
|
||||
}
|
||||
for (int i = 0; i < fields.getNumArrayEntries(); i++) {
|
||||
size += getMessageSetSerializedSize(fields.getArrayEntryAt(i));
|
||||
}
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry :
|
||||
fields.getOverflowEntries()) {
|
||||
size += getMessageSetSerializedSize(entry);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
private int getMessageSetSerializedSize(
|
||||
final Map.Entry<FieldDescriptorType, Object> entry) {
|
||||
final FieldDescriptorType descriptor = entry.getKey();
|
||||
if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE &&
|
||||
!descriptor.isRepeated() && !descriptor.isPacked()) {
|
||||
return CodedOutputStream.computeMessageSetExtensionSize(
|
||||
entry.getKey().getNumber(), (MessageLite) entry.getValue());
|
||||
} else {
|
||||
return computeFieldSize(descriptor, entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the number of bytes that would be needed to encode a
|
||||
* single tag/value pair of arbitrary type.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -31,6 +31,10 @@
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@ -41,8 +45,14 @@ import java.util.Map;
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public abstract class GeneratedMessageLite extends AbstractMessageLite {
|
||||
protected GeneratedMessageLite() {}
|
||||
public abstract class GeneratedMessageLite extends AbstractMessageLite
|
||||
implements Serializable {
|
||||
|
||||
protected GeneratedMessageLite() {
|
||||
}
|
||||
|
||||
protected GeneratedMessageLite(Builder builder) {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public abstract static class Builder<MessageType extends GeneratedMessageLite,
|
||||
@ -50,6 +60,11 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite {
|
||||
extends AbstractMessageLite.Builder<BuilderType> {
|
||||
protected Builder() {}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public BuilderType clear() {
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
// This is implemented here only to work around an apparent bug in the
|
||||
// Java compiler and/or build system. See bug #1898463. The mere presence
|
||||
// of this dummy clone() implementation makes it go away.
|
||||
@ -65,12 +80,6 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite {
|
||||
// Defined here for return type covariance.
|
||||
public abstract MessageType getDefaultInstanceForType();
|
||||
|
||||
/**
|
||||
* Get the message being built. We don't just pass this to the
|
||||
* constructor because it becomes null when build() is called.
|
||||
*/
|
||||
protected abstract MessageType internalGetResult();
|
||||
|
||||
/**
|
||||
* Called by subclasses to parse an unknown field.
|
||||
* @return {@code true} unless the tag is an end-group tag.
|
||||
@ -86,15 +95,46 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite {
|
||||
// =================================================================
|
||||
// Extensions-related stuff
|
||||
|
||||
/**
|
||||
* Lite equivalent of {@link com.google.protobuf.GeneratedMessage.ExtendableMessageOrBuilder}.
|
||||
*/
|
||||
public interface ExtendableMessageOrBuilder<
|
||||
MessageType extends ExtendableMessage> extends MessageLiteOrBuilder {
|
||||
|
||||
/** 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(
|
||||
GeneratedExtension<MessageType, List<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(
|
||||
GeneratedExtension<MessageType, List<Type>> extension,
|
||||
int index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lite equivalent of {@link GeneratedMessage.ExtendableMessage}.
|
||||
*/
|
||||
public abstract static class ExtendableMessage<
|
||||
MessageType extends ExtendableMessage<MessageType>>
|
||||
extends GeneratedMessageLite {
|
||||
protected ExtendableMessage() {}
|
||||
private final FieldSet<ExtensionDescriptor> extensions =
|
||||
FieldSet.newFieldSet();
|
||||
extends GeneratedMessageLite
|
||||
implements ExtendableMessageOrBuilder<MessageType> {
|
||||
|
||||
private final FieldSet<ExtensionDescriptor> extensions;
|
||||
|
||||
protected ExtendableMessage() {
|
||||
this.extensions = FieldSet.newFieldSet();
|
||||
}
|
||||
|
||||
protected ExtendableMessage(ExtendableBuilder<MessageType, ?> builder) {
|
||||
this.extensions = builder.buildExtensions();
|
||||
}
|
||||
|
||||
private void verifyExtensionContainingType(
|
||||
final GeneratedExtension<MessageType, ?> extension) {
|
||||
@ -108,13 +148,15 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite {
|
||||
}
|
||||
|
||||
/** Check if a singular extension is present. */
|
||||
public final boolean hasExtension(
|
||||
final GeneratedExtension<MessageType, ?> extension) {
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public final <Type> boolean hasExtension(
|
||||
final GeneratedExtension<MessageType, Type> extension) {
|
||||
verifyExtensionContainingType(extension);
|
||||
return extensions.hasField(extension.descriptor);
|
||||
}
|
||||
|
||||
/** Get the number of elements in a repeated extension. */
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public final <Type> int getExtensionCount(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension) {
|
||||
verifyExtensionContainingType(extension);
|
||||
@ -122,6 +164,7 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite {
|
||||
}
|
||||
|
||||
/** Get the value of an extension. */
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
@SuppressWarnings("unchecked")
|
||||
public final <Type> Type getExtension(
|
||||
final GeneratedExtension<MessageType, Type> extension) {
|
||||
@ -135,6 +178,7 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite {
|
||||
}
|
||||
|
||||
/** Get one element of a repeated extension. */
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
@SuppressWarnings("unchecked")
|
||||
public final <Type> Type getExtension(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension,
|
||||
@ -214,9 +258,88 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite {
|
||||
public abstract static class ExtendableBuilder<
|
||||
MessageType extends ExtendableMessage<MessageType>,
|
||||
BuilderType extends ExtendableBuilder<MessageType, BuilderType>>
|
||||
extends Builder<MessageType, BuilderType> {
|
||||
extends Builder<MessageType, BuilderType>
|
||||
implements ExtendableMessageOrBuilder<MessageType> {
|
||||
protected ExtendableBuilder() {}
|
||||
|
||||
private FieldSet<ExtensionDescriptor> extensions = FieldSet.emptySet();
|
||||
private boolean extensionsIsMutable;
|
||||
|
||||
@Override
|
||||
public BuilderType clear() {
|
||||
extensions.clear();
|
||||
extensionsIsMutable = false;
|
||||
return super.clear();
|
||||
}
|
||||
|
||||
private void ensureExtensionsIsMutable() {
|
||||
if (!extensionsIsMutable) {
|
||||
extensions = extensions.clone();
|
||||
extensionsIsMutable = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the build code path to create a copy of the extensions for
|
||||
* building the message.
|
||||
*/
|
||||
private FieldSet<ExtensionDescriptor> buildExtensions() {
|
||||
extensions.makeImmutable();
|
||||
extensionsIsMutable = false;
|
||||
return extensions;
|
||||
}
|
||||
|
||||
private void verifyExtensionContainingType(
|
||||
final GeneratedExtension<MessageType, ?> extension) {
|
||||
if (extension.getContainingTypeDefaultInstance() !=
|
||||
getDefaultInstanceForType()) {
|
||||
// This can only happen if someone uses unchecked operations.
|
||||
throw new IllegalArgumentException(
|
||||
"This extension is for a different message type. Please make " +
|
||||
"sure that you are not suppressing any generics type warnings.");
|
||||
}
|
||||
}
|
||||
|
||||
/** Check if a singular extension is present. */
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public final <Type> boolean hasExtension(
|
||||
final GeneratedExtension<MessageType, Type> extension) {
|
||||
verifyExtensionContainingType(extension);
|
||||
return extensions.hasField(extension.descriptor);
|
||||
}
|
||||
|
||||
/** Get the number of elements in a repeated extension. */
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public final <Type> int getExtensionCount(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension) {
|
||||
verifyExtensionContainingType(extension);
|
||||
return extensions.getRepeatedFieldCount(extension.descriptor);
|
||||
}
|
||||
|
||||
/** Get the value of an extension. */
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
@SuppressWarnings("unchecked")
|
||||
public final <Type> Type getExtension(
|
||||
final GeneratedExtension<MessageType, Type> extension) {
|
||||
verifyExtensionContainingType(extension);
|
||||
final Object value = extensions.getField(extension.descriptor);
|
||||
if (value == null) {
|
||||
return extension.defaultValue;
|
||||
} else {
|
||||
return (Type) value;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get one element of a repeated extension. */
|
||||
@SuppressWarnings("unchecked")
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public final <Type> Type getExtension(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension,
|
||||
final int index) {
|
||||
verifyExtensionContainingType(extension);
|
||||
return (Type) extensions.getRepeatedField(extension.descriptor, index);
|
||||
}
|
||||
|
||||
// This is implemented here only to work around an apparent bug in the
|
||||
// Java compiler and/or build system. See bug #1898463. The mere presence
|
||||
// of this dummy clone() implementation makes it go away.
|
||||
@ -226,41 +349,13 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite {
|
||||
"This is supposed to be overridden by subclasses.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected abstract MessageType internalGetResult();
|
||||
|
||||
/** Check if a singular extension is present. */
|
||||
public final boolean hasExtension(
|
||||
final GeneratedExtension<MessageType, ?> extension) {
|
||||
return internalGetResult().hasExtension(extension);
|
||||
}
|
||||
|
||||
/** Get the number of elements in a repeated extension. */
|
||||
public final <Type> int getExtensionCount(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension) {
|
||||
return internalGetResult().getExtensionCount(extension);
|
||||
}
|
||||
|
||||
/** Get the value of an extension. */
|
||||
public final <Type> Type getExtension(
|
||||
final GeneratedExtension<MessageType, Type> extension) {
|
||||
return internalGetResult().getExtension(extension);
|
||||
}
|
||||
|
||||
/** Get one element of a repeated extension. */
|
||||
public final <Type> Type getExtension(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension,
|
||||
final int index) {
|
||||
return internalGetResult().getExtension(extension, index);
|
||||
}
|
||||
|
||||
/** Set the value of an extension. */
|
||||
public final <Type> BuilderType setExtension(
|
||||
final GeneratedExtension<MessageType, Type> extension,
|
||||
final Type value) {
|
||||
final ExtendableMessage<MessageType> message = internalGetResult();
|
||||
message.verifyExtensionContainingType(extension);
|
||||
message.extensions.setField(extension.descriptor, value);
|
||||
verifyExtensionContainingType(extension);
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.setField(extension.descriptor, value);
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
@ -268,9 +363,9 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite {
|
||||
public final <Type> BuilderType setExtension(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension,
|
||||
final int index, final Type value) {
|
||||
final ExtendableMessage<MessageType> message = internalGetResult();
|
||||
message.verifyExtensionContainingType(extension);
|
||||
message.extensions.setRepeatedField(extension.descriptor, index, value);
|
||||
verifyExtensionContainingType(extension);
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.setRepeatedField(extension.descriptor, index, value);
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
@ -278,21 +373,26 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite {
|
||||
public final <Type> BuilderType addExtension(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension,
|
||||
final Type value) {
|
||||
final ExtendableMessage<MessageType> message = internalGetResult();
|
||||
message.verifyExtensionContainingType(extension);
|
||||
message.extensions.addRepeatedField(extension.descriptor, value);
|
||||
verifyExtensionContainingType(extension);
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.addRepeatedField(extension.descriptor, value);
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
/** Clear an extension. */
|
||||
public final <Type> BuilderType clearExtension(
|
||||
final GeneratedExtension<MessageType, ?> extension) {
|
||||
final ExtendableMessage<MessageType> message = internalGetResult();
|
||||
message.verifyExtensionContainingType(extension);
|
||||
message.extensions.clearField(extension.descriptor);
|
||||
verifyExtensionContainingType(extension);
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.clearField(extension.descriptor);
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
/** Called by subclasses to check if all extensions are initialized. */
|
||||
protected boolean extensionsAreInitialized() {
|
||||
return extensions.isInitialized();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by subclasses to parse an unknown field or an extension.
|
||||
* @return {@code true} unless the tag is an end-group tag.
|
||||
@ -302,9 +402,6 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite {
|
||||
final CodedInputStream input,
|
||||
final ExtensionRegistryLite extensionRegistry,
|
||||
final int tag) throws IOException {
|
||||
final FieldSet<ExtensionDescriptor> extensions =
|
||||
((ExtendableMessage) internalGetResult()).extensions;
|
||||
|
||||
final int wireType = WireFormat.getTagWireType(tag);
|
||||
final int fieldNumber = WireFormat.getTagFieldNumber(tag);
|
||||
|
||||
@ -347,6 +444,7 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite {
|
||||
// enum, drop it (don't even add it to unknownFields).
|
||||
return true;
|
||||
}
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.addRepeatedField(extension.descriptor, value);
|
||||
}
|
||||
} else {
|
||||
@ -354,6 +452,7 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite {
|
||||
final Object value =
|
||||
FieldSet.readPrimitiveField(input,
|
||||
extension.descriptor.getLiteType());
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.addRepeatedField(extension.descriptor, value);
|
||||
}
|
||||
}
|
||||
@ -400,8 +499,10 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite {
|
||||
}
|
||||
|
||||
if (extension.descriptor.isRepeated()) {
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.addRepeatedField(extension.descriptor, value);
|
||||
} else {
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.setField(extension.descriptor, value);
|
||||
}
|
||||
}
|
||||
@ -410,8 +511,8 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite {
|
||||
}
|
||||
|
||||
protected final void mergeExtensionFields(final MessageType other) {
|
||||
((ExtendableMessage) internalGetResult()).extensions.mergeFrom(
|
||||
((ExtendableMessage) other).extensions);
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.mergeFrom(((ExtendableMessage) other).extensions);
|
||||
}
|
||||
}
|
||||
|
||||
@ -420,8 +521,40 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite {
|
||||
/** For use by generated code only. */
|
||||
public static <ContainingType extends MessageLite, Type>
|
||||
GeneratedExtension<ContainingType, Type>
|
||||
newGeneratedExtension() {
|
||||
return new GeneratedExtension<ContainingType, Type>();
|
||||
newSingularGeneratedExtension(
|
||||
final ContainingType containingTypeDefaultInstance,
|
||||
final Type defaultValue,
|
||||
final MessageLite messageDefaultInstance,
|
||||
final Internal.EnumLiteMap<?> enumTypeMap,
|
||||
final int number,
|
||||
final WireFormat.FieldType type) {
|
||||
return new GeneratedExtension<ContainingType, Type>(
|
||||
containingTypeDefaultInstance,
|
||||
defaultValue,
|
||||
messageDefaultInstance,
|
||||
new ExtensionDescriptor(enumTypeMap, number, type,
|
||||
false /* isRepeated */,
|
||||
false /* isPacked */));
|
||||
}
|
||||
|
||||
/** For use by generated code only. */
|
||||
public static <ContainingType extends MessageLite, Type>
|
||||
GeneratedExtension<ContainingType, Type>
|
||||
newRepeatedGeneratedExtension(
|
||||
final ContainingType containingTypeDefaultInstance,
|
||||
final MessageLite messageDefaultInstance,
|
||||
final Internal.EnumLiteMap<?> enumTypeMap,
|
||||
final int number,
|
||||
final WireFormat.FieldType type,
|
||||
final boolean isPacked) {
|
||||
@SuppressWarnings("unchecked") // Subclasses ensure Type is a List
|
||||
Type emptyList = (Type) Collections.emptyList();
|
||||
return new GeneratedExtension<ContainingType, Type>(
|
||||
containingTypeDefaultInstance,
|
||||
emptyList,
|
||||
messageDefaultInstance,
|
||||
new ExtensionDescriptor(
|
||||
enumTypeMap, number, type, true /* isRepeated */, isPacked));
|
||||
}
|
||||
|
||||
private static final class ExtensionDescriptor
|
||||
@ -489,60 +622,33 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite {
|
||||
*/
|
||||
public static final class GeneratedExtension<
|
||||
ContainingType extends MessageLite, Type> {
|
||||
// We can't always initialize a GeneratedExtension when we first construct
|
||||
// it due to initialization order difficulties (namely, the default
|
||||
// instances may not have been constructed yet). So, we construct an
|
||||
// uninitialized GeneratedExtension once, then call internalInit() on it
|
||||
// later. Generated code will always call internalInit() on all extensions
|
||||
// as part of the static initialization code, and internalInit() throws an
|
||||
// exception if called more than once, so this method is useless to users.
|
||||
private GeneratedExtension() {}
|
||||
|
||||
private void internalInit(
|
||||
private GeneratedExtension(
|
||||
final ContainingType containingTypeDefaultInstance,
|
||||
final Type defaultValue,
|
||||
final MessageLite messageDefaultInstance,
|
||||
final ExtensionDescriptor descriptor) {
|
||||
// Defensive checks to verify the correct initialization order of
|
||||
// GeneratedExtensions and their related GeneratedMessages.
|
||||
if (containingTypeDefaultInstance == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Null containingTypeDefaultInstance");
|
||||
}
|
||||
if (descriptor.getLiteType() == WireFormat.FieldType.MESSAGE &&
|
||||
messageDefaultInstance == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Null messageDefaultInstance");
|
||||
}
|
||||
this.containingTypeDefaultInstance = containingTypeDefaultInstance;
|
||||
this.defaultValue = defaultValue;
|
||||
this.messageDefaultInstance = messageDefaultInstance;
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
|
||||
/** For use by generated code only. */
|
||||
public void internalInitSingular(
|
||||
final ContainingType containingTypeDefaultInstance,
|
||||
final Type defaultValue,
|
||||
final MessageLite messageDefaultInstance,
|
||||
final Internal.EnumLiteMap<?> enumTypeMap,
|
||||
final int number,
|
||||
final WireFormat.FieldType type) {
|
||||
internalInit(
|
||||
containingTypeDefaultInstance, defaultValue, messageDefaultInstance,
|
||||
new ExtensionDescriptor(enumTypeMap, number, type,
|
||||
false /* isRepeated */, false /* isPacked */));
|
||||
}
|
||||
|
||||
/** For use by generated code only. */
|
||||
@SuppressWarnings("unchecked")
|
||||
public void internalInitRepeated(
|
||||
final ContainingType containingTypeDefaultInstance,
|
||||
final MessageLite messageDefaultInstance,
|
||||
final Internal.EnumLiteMap<?> enumTypeMap,
|
||||
final int number,
|
||||
final WireFormat.FieldType type,
|
||||
final boolean isPacked) {
|
||||
internalInit(
|
||||
containingTypeDefaultInstance, (Type) Collections.emptyList(),
|
||||
messageDefaultInstance,
|
||||
new ExtensionDescriptor(
|
||||
enumTypeMap, number, type, true /* isRepeated */, isPacked));
|
||||
}
|
||||
|
||||
private ContainingType containingTypeDefaultInstance;
|
||||
private Type defaultValue;
|
||||
private MessageLite messageDefaultInstance;
|
||||
private ExtensionDescriptor descriptor;
|
||||
private final ContainingType containingTypeDefaultInstance;
|
||||
private final Type defaultValue;
|
||||
private final MessageLite messageDefaultInstance;
|
||||
private final ExtensionDescriptor descriptor;
|
||||
|
||||
/**
|
||||
* Default instance of the type being extended, used to identify that type.
|
||||
@ -564,4 +670,61 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite {
|
||||
return messageDefaultInstance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A serialized (serializable) form of the generated message. Stores the
|
||||
* message as a class name and a byte array.
|
||||
*/
|
||||
static final class SerializedForm implements Serializable {
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
private String messageClassName;
|
||||
private byte[] asBytes;
|
||||
|
||||
/**
|
||||
* Creates the serialized form by calling {@link com.google.protobuf.MessageLite#toByteArray}.
|
||||
* @param regularForm the message to serialize
|
||||
*/
|
||||
SerializedForm(MessageLite regularForm) {
|
||||
messageClassName = regularForm.getClass().getName();
|
||||
asBytes = regularForm.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* When read from an ObjectInputStream, this method converts this object
|
||||
* back to the regular form. Part of Java's serialization magic.
|
||||
* @return a GeneratedMessage of the type that was serialized
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Object readResolve() throws ObjectStreamException {
|
||||
try {
|
||||
Class messageClass = Class.forName(messageClassName);
|
||||
Method newBuilder = messageClass.getMethod("newBuilder");
|
||||
MessageLite.Builder builder =
|
||||
(MessageLite.Builder) newBuilder.invoke(null);
|
||||
builder.mergeFrom(asBytes);
|
||||
return builder.buildPartial();
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("Unable to find proto buffer class", e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException("Unable to find newBuilder method", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Unable to call newBuilder method", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException("Error calling newBuilder", e.getCause());
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw new RuntimeException("Unable to understand proto buffer", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces this object in the output stream with a serialized form.
|
||||
* Part of Java's serialization magic. Generated sub-classes must override
|
||||
* this method by calling <code>return super.writeReplace();</code>
|
||||
* @return a SerializedForm of this message
|
||||
*/
|
||||
protected Object writeReplace() throws ObjectStreamException {
|
||||
return new SerializedForm(this);
|
||||
}
|
||||
}
|
||||
|
@ -99,6 +99,91 @@ public class Internal {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper called by generated code to determine if a byte array is a valid
|
||||
* UTF-8 encoded string such that the original bytes can be converted to
|
||||
* a String object and then back to a byte array round tripping the bytes
|
||||
* without loss.
|
||||
* <p>
|
||||
* This is inspired by UTF_8.java in sun.nio.cs.
|
||||
*
|
||||
* @param byteString the string to check
|
||||
* @return whether the byte array is round trippable
|
||||
*/
|
||||
public static boolean isValidUtf8(ByteString byteString) {
|
||||
int index = 0;
|
||||
int size = byteString.size();
|
||||
// To avoid the masking, we could change this to use bytes;
|
||||
// Then X > 0xC2 gets turned into X < -0xC2; X < 0x80
|
||||
// gets turned into X >= 0, etc.
|
||||
|
||||
while (index < size) {
|
||||
int byte1 = byteString.byteAt(index++) & 0xFF;
|
||||
if (byte1 < 0x80) {
|
||||
// fast loop for single bytes
|
||||
continue;
|
||||
|
||||
// we know from this point on that we have 2-4 byte forms
|
||||
} else if (byte1 < 0xC2 || byte1 > 0xF4) {
|
||||
// catch illegal first bytes: < C2 or > F4
|
||||
return false;
|
||||
}
|
||||
if (index >= size) {
|
||||
// fail if we run out of bytes
|
||||
return false;
|
||||
}
|
||||
int byte2 = byteString.byteAt(index++) & 0xFF;
|
||||
if (byte2 < 0x80 || byte2 > 0xBF) {
|
||||
// general trail-byte test
|
||||
return false;
|
||||
}
|
||||
if (byte1 <= 0xDF) {
|
||||
// two-byte form; general trail-byte test is sufficient
|
||||
continue;
|
||||
}
|
||||
|
||||
// we know from this point on that we have 3 or 4 byte forms
|
||||
if (index >= size) {
|
||||
// fail if we run out of bytes
|
||||
return false;
|
||||
}
|
||||
int byte3 = byteString.byteAt(index++) & 0xFF;
|
||||
if (byte3 < 0x80 || byte3 > 0xBF) {
|
||||
// general trail-byte test
|
||||
return false;
|
||||
}
|
||||
if (byte1 <= 0xEF) {
|
||||
// three-byte form. Vastly more frequent than four-byte forms
|
||||
// The following has an extra test, but not worth restructuring
|
||||
if (byte1 == 0xE0 && byte2 < 0xA0 ||
|
||||
byte1 == 0xED && byte2 > 0x9F) {
|
||||
// check special cases of byte2
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
// four-byte form
|
||||
|
||||
if (index >= size) {
|
||||
// fail if we run out of bytes
|
||||
return false;
|
||||
}
|
||||
int byte4 = byteString.byteAt(index++) & 0xFF;
|
||||
if (byte4 < 0x80 || byte4 > 0xBF) {
|
||||
// general trail-byte test
|
||||
return false;
|
||||
}
|
||||
// The following has an extra test, but not worth restructuring
|
||||
if (byte1 == 0xF0 && byte2 < 0x90 ||
|
||||
byte1 == 0xF4 && byte2 > 0x8F) {
|
||||
// check special cases of byte2
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for an enum value or value descriptor, to be used in FieldSet.
|
||||
* The lite library stores enum values directly in FieldSets but the full
|
||||
|
155
java/src/main/java/com/google/protobuf/LazyStringArrayList.java
Normal file
155
java/src/main/java/com/google/protobuf/LazyStringArrayList.java
Normal file
@ -0,0 +1,155 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.RandomAccess;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* An implementation of {@link LazyStringList} that wraps an ArrayList. Each
|
||||
* element is either a ByteString or a String. It caches the last one requested
|
||||
* which is most likely the one needed next. This minimizes memory usage while
|
||||
* satisfying the most common use cases.
|
||||
* <p>
|
||||
* <strong>Note that this implementation is not synchronized.</strong>
|
||||
* If multiple threads access an <tt>ArrayList</tt> instance concurrently,
|
||||
* and at least one of the threads modifies the list structurally, it
|
||||
* <i>must</i> be synchronized externally. (A structural modification is
|
||||
* any operation that adds or deletes one or more elements, or explicitly
|
||||
* resizes the backing array; merely setting the value of an element is not
|
||||
* a structural modification.) This is typically accomplished by
|
||||
* synchronizing on some object that naturally encapsulates the list.
|
||||
* <p>
|
||||
* If the implementation is accessed via concurrent reads, this is thread safe.
|
||||
* Conversions are done in a thread safe manner. It's possible that the
|
||||
* conversion may happen more than once if two threads attempt to access the
|
||||
* same element and the modifications were not visible to each other, but this
|
||||
* will not result in any corruption of the list or change in behavior other
|
||||
* than performance.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class LazyStringArrayList extends AbstractList<String>
|
||||
implements LazyStringList, RandomAccess {
|
||||
|
||||
public final static LazyStringList EMPTY = new UnmodifiableLazyStringList(
|
||||
new LazyStringArrayList());
|
||||
|
||||
private final List<Object> list;
|
||||
|
||||
public LazyStringArrayList() {
|
||||
list = new ArrayList<Object>();
|
||||
}
|
||||
|
||||
public LazyStringArrayList(List<String> from) {
|
||||
list = new ArrayList<Object>(from);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(int index) {
|
||||
Object o = list.get(index);
|
||||
if (o instanceof String) {
|
||||
return (String) o;
|
||||
} else {
|
||||
ByteString bs = (ByteString) o;
|
||||
String s = bs.toStringUtf8();
|
||||
if (Internal.isValidUtf8(bs)) {
|
||||
list.set(index, s);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String set(int index, String s) {
|
||||
Object o = list.set(index, s);
|
||||
return asString(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, String element) {
|
||||
list.add(index, element);
|
||||
modCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends String> c) {
|
||||
boolean ret = list.addAll(index, c);
|
||||
modCount++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String remove(int index) {
|
||||
Object o = list.remove(index);
|
||||
modCount++;
|
||||
return asString(o);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
list.clear();
|
||||
modCount++;
|
||||
}
|
||||
|
||||
// @Override
|
||||
public void add(ByteString element) {
|
||||
list.add(element);
|
||||
modCount++;
|
||||
}
|
||||
|
||||
// @Override
|
||||
public ByteString getByteString(int index) {
|
||||
Object o = list.get(index);
|
||||
if (o instanceof String) {
|
||||
ByteString b = ByteString.copyFromUtf8((String) o);
|
||||
list.set(index, b);
|
||||
return b;
|
||||
} else {
|
||||
return (ByteString) o;
|
||||
}
|
||||
}
|
||||
|
||||
private String asString(Object o) {
|
||||
if (o instanceof String) {
|
||||
return (String) o;
|
||||
} else {
|
||||
return ((ByteString) o).toStringUtf8();
|
||||
}
|
||||
}
|
||||
}
|
72
java/src/main/java/com/google/protobuf/LazyStringList.java
Normal file
72
java/src/main/java/com/google/protobuf/LazyStringList.java
Normal file
@ -0,0 +1,72 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An interface extending List<String> that also provides access to the
|
||||
* items of the list as UTF8-encoded ByteString objects. This is used by the
|
||||
* protocol buffer implementation to support lazily converting bytes parsed
|
||||
* over the wire to String objects until needed and also increases the
|
||||
* efficiency of serialization if the String was never requested as the
|
||||
* ByteString is already cached.
|
||||
* <p>
|
||||
* This only adds additional methods that are required for the use in the
|
||||
* protocol buffer code in order to be able successfuly round trip byte arrays
|
||||
* through parsing and serialization without conversion to strings. It's not
|
||||
* attempting to support the functionality of say List<ByteString>, hence
|
||||
* why only these two very specific methods are added.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public interface LazyStringList extends List<String> {
|
||||
|
||||
/**
|
||||
* Returns the element at the specified position in this list as a ByteString.
|
||||
*
|
||||
* @param index index of the element to return
|
||||
* @return the element at the specified position in this list
|
||||
* @throws IndexOutOfBoundsException if the index is out of range
|
||||
* (<tt>index < 0 || index >= size()</tt>)
|
||||
*/
|
||||
ByteString getByteString(int index);
|
||||
|
||||
/**
|
||||
* Appends the specified element to the end of this list (optional
|
||||
* operation).
|
||||
*
|
||||
* @param element element to be appended to this list
|
||||
* @throws UnsupportedOperationException if the <tt>add</tt> operation
|
||||
* is not supported by this list
|
||||
*/
|
||||
void add(ByteString element);
|
||||
}
|
@ -48,69 +48,7 @@ import java.util.Map;
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public interface Message extends MessageLite {
|
||||
/**
|
||||
* Get the message's type's descriptor. This differs from the
|
||||
* {@code getDescriptor()} method of generated message classes in that
|
||||
* this method is an abstract method of the {@code Message} interface
|
||||
* whereas {@code getDescriptor()} is a static method of a specific class.
|
||||
* They return the same thing.
|
||||
*/
|
||||
Descriptors.Descriptor getDescriptorForType();
|
||||
|
||||
// (From MessageLite, re-declared here only for return type covariance.)
|
||||
Message getDefaultInstanceForType();
|
||||
|
||||
/**
|
||||
* Returns a collection of all the fields in this message which are set
|
||||
* and their corresponding values. A singular ("required" or "optional")
|
||||
* field is set iff hasField() returns true for that field. A "repeated"
|
||||
* field is set iff getRepeatedFieldSize() is greater than zero. The
|
||||
* values are exactly what would be returned by calling
|
||||
* {@link #getField(Descriptors.FieldDescriptor)} for each field. The map
|
||||
* is guaranteed to be a sorted map, so iterating over it will return fields
|
||||
* in order by field number.
|
||||
*/
|
||||
Map<Descriptors.FieldDescriptor, Object> getAllFields();
|
||||
|
||||
/**
|
||||
* Returns true if the given field is set. This is exactly equivalent to
|
||||
* calling the generated "has" accessor method corresponding to the field.
|
||||
* @throws IllegalArgumentException The field is a repeated field, or
|
||||
* {@code field.getContainingType() != getDescriptorForType()}.
|
||||
*/
|
||||
boolean hasField(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Obtains the value of the given field, or the default value if it is
|
||||
* not set. For primitive fields, the boxed primitive value is returned.
|
||||
* For enum fields, the EnumValueDescriptor for the value is returend. For
|
||||
* embedded message fields, the sub-message is returned. For repeated
|
||||
* fields, a java.util.List is returned.
|
||||
*/
|
||||
Object getField(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Gets the number of elements of a repeated field. This is exactly
|
||||
* equivalent to calling the generated "Count" accessor method corresponding
|
||||
* to the field.
|
||||
* @throws IllegalArgumentException The field is not a repeated field, or
|
||||
* {@code field.getContainingType() != getDescriptorForType()}.
|
||||
*/
|
||||
int getRepeatedFieldCount(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Gets an element of a repeated field. For primitive fields, the boxed
|
||||
* primitive value is returned. For enum fields, the EnumValueDescriptor
|
||||
* for the value is returend. For embedded message fields, the sub-message
|
||||
* is returned.
|
||||
* @throws IllegalArgumentException The field is not a repeated field, or
|
||||
* {@code field.getContainingType() != getDescriptorForType()}.
|
||||
*/
|
||||
Object getRepeatedField(Descriptors.FieldDescriptor field, int index);
|
||||
|
||||
/** Get the {@link UnknownFieldSet} for this message. */
|
||||
UnknownFieldSet getUnknownFields();
|
||||
public interface Message extends MessageLite, MessageOrBuilder {
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Comparison and hashing
|
||||
@ -119,7 +57,8 @@ public interface Message extends MessageLite {
|
||||
* Compares the specified object with this message for equality. Returns
|
||||
* <tt>true</tt> if the given object is a message of the same type (as
|
||||
* defined by {@code getDescriptorForType()}) and has identical values for
|
||||
* all of its fields.
|
||||
* all of its fields. Subclasses must implement this; inheriting
|
||||
* {@code Object.equals()} is incorrect.
|
||||
*
|
||||
* @param other object to be compared for equality with this message
|
||||
* @return <tt>true</tt> if the specified object is equal to this message
|
||||
@ -129,8 +68,9 @@ public interface Message extends MessageLite {
|
||||
|
||||
/**
|
||||
* Returns the hash code value for this message. The hash code of a message
|
||||
* is defined to be <tt>getDescriptor().hashCode() ^ map.hashCode()</tt>,
|
||||
* where <tt>map</tt> is a map of field numbers to field values.
|
||||
* should mix the message's type (object identity of the decsriptor) with its
|
||||
* contents (known and unknown field values). Subclasses must implement this;
|
||||
* inheriting {@code Object.hashCode()} is incorrect.
|
||||
*
|
||||
* @return the hash code value for this message
|
||||
* @see Map#hashCode()
|
||||
@ -158,7 +98,7 @@ public interface Message extends MessageLite {
|
||||
/**
|
||||
* Abstract interface implemented by Protocol Message builders.
|
||||
*/
|
||||
interface Builder extends MessageLite.Builder {
|
||||
interface Builder extends MessageLite.Builder, MessageOrBuilder {
|
||||
// (From MessageLite.Builder, re-declared here only for return type
|
||||
// covariance.)
|
||||
Builder clear();
|
||||
@ -197,17 +137,6 @@ public interface Message extends MessageLite {
|
||||
*/
|
||||
Descriptors.Descriptor getDescriptorForType();
|
||||
|
||||
// (From MessageLite.Builder, re-declared here only for return type
|
||||
// covariance.)
|
||||
Message getDefaultInstanceForType();
|
||||
|
||||
/**
|
||||
* Like {@link Message#getAllFields()}. The returned map may or may not
|
||||
* reflect future changes to the builder. Either way, the returned map is
|
||||
* itself unmodifiable.
|
||||
*/
|
||||
Map<Descriptors.FieldDescriptor, Object> getAllFields();
|
||||
|
||||
/**
|
||||
* Create a Builder for messages of the appropriate type for the given
|
||||
* field. Messages built with this can then be passed to setField(),
|
||||
@ -215,12 +144,6 @@ public interface Message extends MessageLite {
|
||||
*/
|
||||
Builder newBuilderForField(Descriptors.FieldDescriptor field);
|
||||
|
||||
/** Like {@link Message#hasField(Descriptors.FieldDescriptor)} */
|
||||
boolean hasField(Descriptors.FieldDescriptor field);
|
||||
|
||||
/** Like {@link Message#getField(Descriptors.FieldDescriptor)} */
|
||||
Object getField(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Sets a field to the given value. The value must be of the correct type
|
||||
* for this field, i.e. the same type that
|
||||
@ -234,16 +157,6 @@ public interface Message extends MessageLite {
|
||||
*/
|
||||
Builder clearField(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Like {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}
|
||||
*/
|
||||
int getRepeatedFieldCount(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Like {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)}
|
||||
*/
|
||||
Object getRepeatedField(Descriptors.FieldDescriptor field, int index);
|
||||
|
||||
/**
|
||||
* Sets an element of a repeated field to the given value. The value must
|
||||
* be of the correct type for this field, i.e. the same type that
|
||||
@ -262,9 +175,6 @@ public interface Message extends MessageLite {
|
||||
*/
|
||||
Builder addRepeatedField(Descriptors.FieldDescriptor field, Object value);
|
||||
|
||||
/** Get the {@link UnknownFieldSet} for this message. */
|
||||
UnknownFieldSet getUnknownFields();
|
||||
|
||||
/** Set the {@link UnknownFieldSet} for this message. */
|
||||
Builder setUnknownFields(UnknownFieldSet unknownFields);
|
||||
|
||||
|
@ -64,22 +64,8 @@ import java.io.OutputStream;
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public interface MessageLite {
|
||||
/**
|
||||
* Get an instance of the type with all fields set to their default values.
|
||||
* This may or may not be a singleton. This differs from the
|
||||
* {@code getDefaultInstance()} method of generated message classes in that
|
||||
* this method is an abstract method of the {@code MessageLite} interface
|
||||
* whereas {@code getDefaultInstance()} is a static method of a specific
|
||||
* class. They return the same thing.
|
||||
*/
|
||||
MessageLite getDefaultInstanceForType();
|
||||
public interface MessageLite extends MessageLiteOrBuilder {
|
||||
|
||||
/**
|
||||
* Returns true if all required fields in the message and all embedded
|
||||
* messages are set, false otherwise.
|
||||
*/
|
||||
boolean isInitialized();
|
||||
|
||||
/**
|
||||
* Serializes the message and writes it to {@code output}. This does not
|
||||
@ -153,7 +139,7 @@ public interface MessageLite {
|
||||
/**
|
||||
* Abstract interface implemented by Protocol Message builders.
|
||||
*/
|
||||
interface Builder extends Cloneable {
|
||||
interface Builder extends MessageLiteOrBuilder, Cloneable {
|
||||
/** Resets all fields to their default values. */
|
||||
Builder clear();
|
||||
|
||||
@ -186,12 +172,6 @@ public interface MessageLite {
|
||||
*/
|
||||
Builder clone();
|
||||
|
||||
/**
|
||||
* Returns true if all required fields in the message and all embedded
|
||||
* messages are set, false otherwise.
|
||||
*/
|
||||
boolean isInitialized();
|
||||
|
||||
/**
|
||||
* Parses a message of this type from the input and merges it with this
|
||||
* message, as if using {@link Builder#mergeFrom(MessageLite)}.
|
||||
@ -230,12 +210,6 @@ public interface MessageLite {
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Get the message's type's default instance.
|
||||
* See {@link MessageLite#getDefaultInstanceForType()}.
|
||||
*/
|
||||
MessageLite getDefaultInstanceForType();
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Convenience methods.
|
||||
|
||||
@ -243,6 +217,8 @@ public interface MessageLite {
|
||||
* Parse {@code data} as a message of this type and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(ByteString data) throws InvalidProtocolBufferException;
|
||||
|
||||
@ -250,6 +226,8 @@ public interface MessageLite {
|
||||
* Parse {@code data} as a message of this type and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(ByteString data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
@ -259,6 +237,8 @@ public interface MessageLite {
|
||||
* Parse {@code data} as a message of this type and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(byte[] data) throws InvalidProtocolBufferException;
|
||||
|
||||
@ -266,6 +246,8 @@ public interface MessageLite {
|
||||
* Parse {@code data} as a message of this type and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(byte[] data, int off, int len)
|
||||
throws InvalidProtocolBufferException;
|
||||
@ -274,6 +256,8 @@ public interface MessageLite {
|
||||
* Parse {@code data} as a message of this type and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(byte[] data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
@ -283,6 +267,8 @@ public interface MessageLite {
|
||||
* Parse {@code data} as a message of this type and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(byte[] data, int off, int len,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
@ -299,6 +285,8 @@ public interface MessageLite {
|
||||
* and {@link #mergeDelimitedFrom(InputStream)} to read it.
|
||||
* <p>
|
||||
* Despite usually reading the entire input, this does not close the stream.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(InputStream input) throws IOException;
|
||||
|
||||
@ -306,6 +294,8 @@ public interface MessageLite {
|
||||
* Parse a message of this type from {@code input} and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
|
@ -0,0 +1,56 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
/**
|
||||
* Base interface for methods common to {@link MessageLite}
|
||||
* and {@link MessageLite.Builder} to provide type equivalency.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public interface MessageLiteOrBuilder {
|
||||
/**
|
||||
* Get an instance of the type with all fields set to their default values.
|
||||
* This may or may not be a singleton. This differs from the
|
||||
* {@code getDefaultInstance()} method of generated message classes in that
|
||||
* this method is an abstract method of the {@code MessageLite} interface
|
||||
* whereas {@code getDefaultInstance()} is a static method of a specific
|
||||
* class. They return the same thing.
|
||||
*/
|
||||
MessageLite getDefaultInstanceForType();
|
||||
|
||||
/**
|
||||
* Returns true if all required fields in the message and all embedded
|
||||
* messages are set, false otherwise.
|
||||
*/
|
||||
boolean isInitialized();
|
||||
|
||||
}
|
110
java/src/main/java/com/google/protobuf/MessageOrBuilder.java
Normal file
110
java/src/main/java/com/google/protobuf/MessageOrBuilder.java
Normal file
@ -0,0 +1,110 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Base interface for methods common to {@link Message} and
|
||||
* {@link Message.Builder} to provide type equivalency.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public interface MessageOrBuilder extends MessageLiteOrBuilder {
|
||||
|
||||
// (From MessageLite, re-declared here only for return type covariance.)
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
Message getDefaultInstanceForType();
|
||||
|
||||
/**
|
||||
* Get the message's type's descriptor. This differs from the
|
||||
* {@code getDescriptor()} method of generated message classes in that
|
||||
* this method is an abstract method of the {@code Message} interface
|
||||
* whereas {@code getDescriptor()} is a static method of a specific class.
|
||||
* They return the same thing.
|
||||
*/
|
||||
Descriptors.Descriptor getDescriptorForType();
|
||||
|
||||
/**
|
||||
* Returns a collection of all the fields in this message which are set
|
||||
* and their corresponding values. A singular ("required" or "optional")
|
||||
* field is set iff hasField() returns true for that field. A "repeated"
|
||||
* field is set iff getRepeatedFieldSize() is greater than zero. The
|
||||
* values are exactly what would be returned by calling
|
||||
* {@link #getField(Descriptors.FieldDescriptor)} for each field. The map
|
||||
* is guaranteed to be a sorted map, so iterating over it will return fields
|
||||
* in order by field number.
|
||||
* <br>
|
||||
* If this is for a builder, the returned map may or may not reflect future
|
||||
* changes to the builder. Either way, the returned map is itself
|
||||
* unmodifiable.
|
||||
*/
|
||||
Map<Descriptors.FieldDescriptor, Object> getAllFields();
|
||||
|
||||
/**
|
||||
* Returns true if the given field is set. This is exactly equivalent to
|
||||
* calling the generated "has" accessor method corresponding to the field.
|
||||
* @throws IllegalArgumentException The field is a repeated field, or
|
||||
* {@code field.getContainingType() != getDescriptorForType()}.
|
||||
*/
|
||||
boolean hasField(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Obtains the value of the given field, or the default value if it is
|
||||
* not set. For primitive fields, the boxed primitive value is returned.
|
||||
* For enum fields, the EnumValueDescriptor for the value is returend. For
|
||||
* embedded message fields, the sub-message is returned. For repeated
|
||||
* fields, a java.util.List is returned.
|
||||
*/
|
||||
Object getField(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Gets the number of elements of a repeated field. This is exactly
|
||||
* equivalent to calling the generated "Count" accessor method corresponding
|
||||
* to the field.
|
||||
* @throws IllegalArgumentException The field is not a repeated field, or
|
||||
* {@code field.getContainingType() != getDescriptorForType()}.
|
||||
*/
|
||||
int getRepeatedFieldCount(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Gets an element of a repeated field. For primitive fields, the boxed
|
||||
* primitive value is returned. For enum fields, the EnumValueDescriptor
|
||||
* for the value is returend. For embedded message fields, the sub-message
|
||||
* is returned.
|
||||
* @throws IllegalArgumentException The field is not a repeated field, or
|
||||
* {@code field.getContainingType() != getDescriptorForType()}.
|
||||
*/
|
||||
Object getRepeatedField(Descriptors.FieldDescriptor field, int index);
|
||||
|
||||
/** Get the {@link UnknownFieldSet} for this message. */
|
||||
UnknownFieldSet getUnknownFields();
|
||||
}
|
696
java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java
Normal file
696
java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java
Normal file
@ -0,0 +1,696 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <code>RepeatedFieldBuilder</code> 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</code>
|
||||
* to an immutable <code>Message</code>. In this way, it's possible to maintain
|
||||
* a tree of <code>Builder</code>'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>SingleFieldBuilder</code> and <code>RepeatedFieldBuilder</code>
|
||||
* classes cache messages that were created so that messages only need to be
|
||||
* created when some change occured 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 RepeatedFieldBuilder
|
||||
<MType extends GeneratedMessage,
|
||||
BType extends GeneratedMessage.Builder,
|
||||
IType extends MessageOrBuilder>
|
||||
implements GeneratedMessage.BuilderParent {
|
||||
|
||||
// Parent to send changes to.
|
||||
private GeneratedMessage.BuilderParent parent;
|
||||
|
||||
// List of messages. Never null. It may be immutable, in which case
|
||||
// isMessagesListImmutable will be true. 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<SingleFieldBuilder<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 GeneratedMessage.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 RepeatedFieldBuilder(
|
||||
List<MType> messages,
|
||||
boolean isMessagesListMutable,
|
||||
GeneratedMessage.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<SingleFieldBuilder<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</code>, it is converted to a <code>Message</code> 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</code>, it is converted to a <code>Message</code> 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 SingleFieldBuilder.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);
|
||||
}
|
||||
|
||||
SingleFieldBuilder<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();
|
||||
SingleFieldBuilder<MType, BType, IType> builder = builders.get(index);
|
||||
if (builder == null) {
|
||||
MType message = messages.get(index);
|
||||
builder = new SingleFieldBuilder<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);
|
||||
}
|
||||
|
||||
SingleFieldBuilder<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 RepeatedFieldBuilder<MType, BType, IType> setMessage(
|
||||
int index, MType message) {
|
||||
if (message == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
ensureMutableMessageList();
|
||||
messages.set(index, message);
|
||||
if (builders != null) {
|
||||
SingleFieldBuilder<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 RepeatedFieldBuilder<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 RepeatedFieldBuilder<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 RepeatedFieldBuilder<MType, BType, IType> addAllMessages(
|
||||
Iterable<? extends MType> values) {
|
||||
for (final MType value : values) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
}
|
||||
if (values instanceof Collection) {
|
||||
@SuppressWarnings("unchecked") final
|
||||
Collection<MType> collection = (Collection<MType>) values;
|
||||
if (collection.size() == 0) {
|
||||
return this;
|
||||
}
|
||||
ensureMutableMessageList();
|
||||
for (MType value : values) {
|
||||
addMessage(value);
|
||||
}
|
||||
} else {
|
||||
ensureMutableMessageList();
|
||||
for (MType value : values) {
|
||||
addMessage(value);
|
||||
}
|
||||
}
|
||||
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();
|
||||
SingleFieldBuilder<MType, BType, IType> builder =
|
||||
new SingleFieldBuilder<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();
|
||||
SingleFieldBuilder<MType, BType, IType> builder =
|
||||
new SingleFieldBuilder<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) {
|
||||
SingleFieldBuilder<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 (SingleFieldBuilder<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);
|
||||
SingleFieldBuilder<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 GeneratedMessage,
|
||||
BType extends GeneratedMessage.Builder,
|
||||
IType extends MessageOrBuilder>
|
||||
extends AbstractList<MType> implements List<MType> {
|
||||
|
||||
RepeatedFieldBuilder<MType, BType, IType> builder;
|
||||
|
||||
MessageExternalList(
|
||||
RepeatedFieldBuilder<MType, BType, IType> builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return this.builder.getCount();
|
||||
}
|
||||
|
||||
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 GeneratedMessage,
|
||||
BType extends GeneratedMessage.Builder,
|
||||
IType extends MessageOrBuilder>
|
||||
extends AbstractList<BType> implements List<BType> {
|
||||
|
||||
RepeatedFieldBuilder<MType, BType, IType> builder;
|
||||
|
||||
BuilderExternalList(
|
||||
RepeatedFieldBuilder<MType, BType, IType> builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return this.builder.getCount();
|
||||
}
|
||||
|
||||
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 GeneratedMessage,
|
||||
BType extends GeneratedMessage.Builder,
|
||||
IType extends MessageOrBuilder>
|
||||
extends AbstractList<IType> implements List<IType> {
|
||||
|
||||
RepeatedFieldBuilder<MType, BType, IType> builder;
|
||||
|
||||
MessageOrBuilderExternalList(
|
||||
RepeatedFieldBuilder<MType, BType, IType> builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return this.builder.getCount();
|
||||
}
|
||||
|
||||
public IType get(int index) {
|
||||
return builder.getMessageOrBuilder(index);
|
||||
}
|
||||
|
||||
void incrementModCount() {
|
||||
modCount++;
|
||||
}
|
||||
}
|
||||
}
|
@ -35,10 +35,18 @@ package com.google.protobuf;
|
||||
*
|
||||
* @author cpovirk@google.com (Chris Povirk)
|
||||
*/
|
||||
public final class ServiceException extends Exception {
|
||||
public class ServiceException extends Exception {
|
||||
private static final long serialVersionUID = -1219262335729891920L;
|
||||
|
||||
public ServiceException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ServiceException(final Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public ServiceException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
241
java/src/main/java/com/google/protobuf/SingleFieldBuilder.java
Normal file
241
java/src/main/java/com/google/protobuf/SingleFieldBuilder.java
Normal file
@ -0,0 +1,241 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
/**
|
||||
* <code>SingleFieldBuilder</code> 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</code>
|
||||
* to an immutable <code>Message</code>. In this way, it's possible to maintain
|
||||
* a tree of <code>Builder</code>'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>SingleFieldBuilder</code> and <code>RepeatedFieldBuilder</code>
|
||||
* classes cache messages that were created so that messages only need to be
|
||||
* created when some change occured 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 SingleFieldBuilder
|
||||
<MType extends GeneratedMessage,
|
||||
BType extends GeneratedMessage.Builder,
|
||||
IType extends MessageOrBuilder>
|
||||
implements GeneratedMessage.BuilderParent {
|
||||
|
||||
// Parent to send changes to.
|
||||
private GeneratedMessage.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 GeneratedMessage.BuilderListener.
|
||||
private boolean isClean;
|
||||
|
||||
public SingleFieldBuilder(
|
||||
MType message,
|
||||
GeneratedMessage.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</code>, it is converted to a <code>Message</code> 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 SingleFieldBuilder<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 SingleFieldBuilder<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 SingleFieldBuilder<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();
|
||||
}
|
||||
}
|
618
java/src/main/java/com/google/protobuf/SmallSortedMap.java
Normal file
618
java/src/main/java/com/google/protobuf/SmallSortedMap.java
Normal file
@ -0,0 +1,618 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.TreeMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
|
||||
/**
|
||||
* A custom map implementation from FieldDescriptor to Object optimized to
|
||||
* minimize the number of memory allocations for instances with a small number
|
||||
* of mappings. The implementation stores the first {@code k} mappings in an
|
||||
* array for a configurable value of {@code k}, allowing direct access to the
|
||||
* corresponding {@code Entry}s without the need to create an Iterator. The
|
||||
* remaining entries are stored in an overflow map. Iteration over the entries
|
||||
* in the map should be done as follows:
|
||||
*
|
||||
* <pre>
|
||||
* for (int i = 0; i < fieldMap.getNumArrayEntries(); i++) {
|
||||
* process(fieldMap.getArrayEntryAt(i));
|
||||
* }
|
||||
* for (Map.Entry<K, V> entry : fieldMap.getOverflowEntries()) {
|
||||
* process(entry);
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* The resulting iteration is in order of ascending field tag number. The
|
||||
* object returned by {@link #entrySet()} adheres to the same contract but is
|
||||
* less efficient as it necessarily involves creating an object for iteration.
|
||||
* <p>
|
||||
* The tradeoff for this memory efficiency is that the worst case running time
|
||||
* of the {@code put()} operation is {@code O(k + lg n)}, which happens when
|
||||
* entries are added in descending order. {@code k} should be chosen such that
|
||||
* it covers enough common cases without adversely affecting larger maps. In
|
||||
* practice, the worst case scenario does not happen for extensions because
|
||||
* extension fields are serialized and deserialized in order of ascending tag
|
||||
* number, but the worst case scenario can happen for DynamicMessages.
|
||||
* <p>
|
||||
* The running time for all other operations is similar to that of
|
||||
* {@code TreeMap}.
|
||||
* <p>
|
||||
* Instances are not thread-safe until {@link #makeImmutable()} is called,
|
||||
* after which any modifying operation will result in an
|
||||
* {@link UnsupportedOperationException}.
|
||||
*
|
||||
* @author darick@google.com Darick Tong
|
||||
*/
|
||||
// This class is final for all intents and purposes because the constructor is
|
||||
// private. However, the FieldDescriptor-specific logic is encapsulated in
|
||||
// a subclass to aid testability of the core logic.
|
||||
class SmallSortedMap<K extends Comparable<K>, V> extends AbstractMap<K, V> {
|
||||
|
||||
/**
|
||||
* Creates a new instance for mapping FieldDescriptors to their values.
|
||||
* The {@link #makeImmutable()} implementation will convert the List values
|
||||
* of any repeated fields to unmodifiable lists.
|
||||
*
|
||||
* @param arraySize The size of the entry array containing the
|
||||
* lexicographically smallest mappings.
|
||||
*/
|
||||
static <FieldDescriptorType extends
|
||||
FieldSet.FieldDescriptorLite<FieldDescriptorType>>
|
||||
SmallSortedMap<FieldDescriptorType, Object> newFieldMap(int arraySize) {
|
||||
return new SmallSortedMap<FieldDescriptorType, Object>(arraySize) {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void makeImmutable() {
|
||||
if (!isImmutable()) {
|
||||
for (int i = 0; i < getNumArrayEntries(); i++) {
|
||||
final Map.Entry<FieldDescriptorType, Object> entry =
|
||||
getArrayEntryAt(i);
|
||||
if (entry.getKey().isRepeated()) {
|
||||
final List value = (List) entry.getValue();
|
||||
entry.setValue(Collections.unmodifiableList(value));
|
||||
}
|
||||
}
|
||||
for (Map.Entry<FieldDescriptorType, Object> entry :
|
||||
getOverflowEntries()) {
|
||||
if (entry.getKey().isRepeated()) {
|
||||
final List value = (List) entry.getValue();
|
||||
entry.setValue(Collections.unmodifiableList(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
super.makeImmutable();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance for testing.
|
||||
*
|
||||
* @param arraySize The size of the entry array containing the
|
||||
* lexicographically smallest mappings.
|
||||
*/
|
||||
static <K extends Comparable<K>, V> SmallSortedMap<K, V> newInstanceForTest(
|
||||
int arraySize) {
|
||||
return new SmallSortedMap<K, V>(arraySize);
|
||||
}
|
||||
|
||||
private final int maxArraySize;
|
||||
// The "entry array" is actually a List because generic arrays are not
|
||||
// allowed. ArrayList also nicely handles the entry shifting on inserts and
|
||||
// removes.
|
||||
private List<Entry> entryList;
|
||||
private Map<K, V> overflowEntries;
|
||||
private boolean isImmutable;
|
||||
// The EntrySet is a stateless view of the Map. It's initialized the first
|
||||
// time it is requested and reused henceforth.
|
||||
private volatile EntrySet lazyEntrySet;
|
||||
|
||||
/**
|
||||
* @code arraySize Size of the array in which the lexicographically smallest
|
||||
* mappings are stored. (i.e. the {@code k} referred to in the class
|
||||
* documentation).
|
||||
*/
|
||||
private SmallSortedMap(int arraySize) {
|
||||
this.maxArraySize = arraySize;
|
||||
this.entryList = Collections.emptyList();
|
||||
this.overflowEntries = Collections.emptyMap();
|
||||
}
|
||||
|
||||
/** Make this map immutable from this point forward. */
|
||||
public void makeImmutable() {
|
||||
if (!isImmutable) {
|
||||
// Note: There's no need to wrap the entryList in an unmodifiableList
|
||||
// because none of the list's accessors are exposed. The iterator() of
|
||||
// overflowEntries, on the other hand, is exposed so it must be made
|
||||
// unmodifiable.
|
||||
overflowEntries = overflowEntries.isEmpty() ?
|
||||
Collections.<K, V>emptyMap() :
|
||||
Collections.unmodifiableMap(overflowEntries);
|
||||
isImmutable = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** @return Whether {@link #makeImmutable()} has been called. */
|
||||
public boolean isImmutable() {
|
||||
return isImmutable;
|
||||
}
|
||||
|
||||
/** @return The number of entries in the entry array. */
|
||||
public int getNumArrayEntries() {
|
||||
return entryList.size();
|
||||
}
|
||||
|
||||
/** @return The array entry at the given {@code index}. */
|
||||
public Map.Entry<K, V> getArrayEntryAt(int index) {
|
||||
return entryList.get(index);
|
||||
}
|
||||
|
||||
/** @return There number of overflow entries. */
|
||||
public int getNumOverflowEntries() {
|
||||
return overflowEntries.size();
|
||||
}
|
||||
|
||||
/** @return An iterable over the overflow entries. */
|
||||
public Iterable<Map.Entry<K, V>> getOverflowEntries() {
|
||||
return overflowEntries.isEmpty() ?
|
||||
EmptySet.<Map.Entry<K, V>>iterable() :
|
||||
overflowEntries.entrySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return entryList.size() + overflowEntries.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation throws a {@code ClassCastException} if o is not an
|
||||
* object of type {@code K}.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final K key = (K) o;
|
||||
return binarySearchInArray(key) >= 0 || overflowEntries.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation throws a {@code ClassCastException} if o is not an
|
||||
* object of type {@code K}.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public V get(Object o) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final K key = (K) o;
|
||||
final int index = binarySearchInArray(key);
|
||||
if (index >= 0) {
|
||||
return entryList.get(index).getValue();
|
||||
}
|
||||
return overflowEntries.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
checkMutable();
|
||||
final int index = binarySearchInArray(key);
|
||||
if (index >= 0) {
|
||||
// Replace existing array entry.
|
||||
return entryList.get(index).setValue(value);
|
||||
}
|
||||
ensureEntryArrayMutable();
|
||||
final int insertionPoint = -(index + 1);
|
||||
if (insertionPoint >= maxArraySize) {
|
||||
// Put directly in overflow.
|
||||
return getOverflowEntriesMutable().put(key, value);
|
||||
}
|
||||
// Insert new Entry in array.
|
||||
if (entryList.size() == maxArraySize) {
|
||||
// Shift the last array entry into overflow.
|
||||
final Entry lastEntryInArray = entryList.remove(maxArraySize - 1);
|
||||
getOverflowEntriesMutable().put(lastEntryInArray.getKey(),
|
||||
lastEntryInArray.getValue());
|
||||
}
|
||||
entryList.add(insertionPoint, new Entry(key, value));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
checkMutable();
|
||||
if (!entryList.isEmpty()) {
|
||||
entryList.clear();
|
||||
}
|
||||
if (!overflowEntries.isEmpty()) {
|
||||
overflowEntries.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation throws a {@code ClassCastException} if o is not an
|
||||
* object of type {@code K}.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public V remove(Object o) {
|
||||
checkMutable();
|
||||
@SuppressWarnings("unchecked")
|
||||
final K key = (K) o;
|
||||
final int index = binarySearchInArray(key);
|
||||
if (index >= 0) {
|
||||
return removeArrayEntryAt(index);
|
||||
}
|
||||
// overflowEntries might be Collections.unmodifiableMap(), so only
|
||||
// call remove() if it is non-empty.
|
||||
if (overflowEntries.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
return overflowEntries.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
private V removeArrayEntryAt(int index) {
|
||||
checkMutable();
|
||||
final V removed = entryList.remove(index).getValue();
|
||||
if (!overflowEntries.isEmpty()) {
|
||||
// Shift the first entry in the overflow to be the last entry in the
|
||||
// array.
|
||||
final Iterator<Map.Entry<K, V>> iterator =
|
||||
getOverflowEntriesMutable().entrySet().iterator();
|
||||
entryList.add(new Entry(iterator.next()));
|
||||
iterator.remove();
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key The key to find in the entry array.
|
||||
* @return The returned integer position follows the same semantics as the
|
||||
* value returned by {@link java.util.Arrays#binarySearch()}.
|
||||
*/
|
||||
private int binarySearchInArray(K key) {
|
||||
int left = 0;
|
||||
int right = entryList.size() - 1;
|
||||
|
||||
// Optimization: For the common case in which entries are added in
|
||||
// ascending tag order, check the largest element in the array before
|
||||
// doing a full binary search.
|
||||
if (right >= 0) {
|
||||
int cmp = key.compareTo(entryList.get(right).getKey());
|
||||
if (cmp > 0) {
|
||||
return -(right + 2); // Insert point is after "right".
|
||||
} else if (cmp == 0) {
|
||||
return right;
|
||||
}
|
||||
}
|
||||
|
||||
while (left <= right) {
|
||||
int mid = (left + right) / 2;
|
||||
int cmp = key.compareTo(entryList.get(mid).getKey());
|
||||
if (cmp < 0) {
|
||||
right = mid - 1;
|
||||
} else if (cmp > 0) {
|
||||
left = mid + 1;
|
||||
} else {
|
||||
return mid;
|
||||
}
|
||||
}
|
||||
return -(left + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to the AbstractMap implementation of {@code keySet()} and
|
||||
* {@code values()}, the entry set is created the first time this method is
|
||||
* called, and returned in response to all subsequent calls.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Set<Map.Entry<K, V>> entrySet() {
|
||||
if (lazyEntrySet == null) {
|
||||
lazyEntrySet = new EntrySet();
|
||||
}
|
||||
return lazyEntrySet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnsupportedOperationException if {@link #makeImmutable()} has
|
||||
* has been called.
|
||||
*/
|
||||
private void checkMutable() {
|
||||
if (isImmutable) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a {@link SortedMap} to which overflow entries mappings can be
|
||||
* added or removed.
|
||||
* @throws UnsupportedOperationException if {@link #makeImmutable()} has been
|
||||
* called.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private SortedMap<K, V> getOverflowEntriesMutable() {
|
||||
checkMutable();
|
||||
if (overflowEntries.isEmpty() && !(overflowEntries instanceof TreeMap)) {
|
||||
overflowEntries = new TreeMap<K, V>();
|
||||
}
|
||||
return (SortedMap<K, V>) overflowEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily creates the entry list. Any code that adds to the list must first
|
||||
* call this method.
|
||||
*/
|
||||
private void ensureEntryArrayMutable() {
|
||||
checkMutable();
|
||||
if (entryList.isEmpty() && !(entryList instanceof ArrayList)) {
|
||||
entryList = new ArrayList<Entry>(maxArraySize);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry implementation that implements Comparable in order to support
|
||||
* binary search witin the entry array. Also checks mutability in
|
||||
* {@link #setValue()}.
|
||||
*/
|
||||
private class Entry implements Map.Entry<K, V>, Comparable<Entry> {
|
||||
|
||||
private final K key;
|
||||
private V value;
|
||||
|
||||
Entry(Map.Entry<K, V> copy) {
|
||||
this(copy.getKey(), copy.getValue());
|
||||
}
|
||||
|
||||
Entry(K key, V value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Entry other) {
|
||||
return getKey().compareTo(other.getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public V setValue(V newValue) {
|
||||
checkMutable();
|
||||
final V oldValue = this.value;
|
||||
this.value = newValue;
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof Map.Entry)) {
|
||||
return false;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
Map.Entry<?, ?> other = (Map.Entry<?, ?>) o;
|
||||
return equals(key, other.getKey()) && equals(value, other.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (key == null ? 0 : key.hashCode()) ^
|
||||
(value == null ? 0 : value.hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return key + "=" + value;
|
||||
}
|
||||
|
||||
/** equals() that handles null values. */
|
||||
private boolean equals(Object o1, Object o2) {
|
||||
return o1 == null ? o2 == null : o1.equals(o2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stateless view of the entries in the field map.
|
||||
*/
|
||||
private class EntrySet extends AbstractSet<Map.Entry<K, V>> {
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<K, V>> iterator() {
|
||||
return new EntryIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return SmallSortedMap.this.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws a {@link ClassCastException} if o is not of the expected type.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map.Entry<K, V> entry = (Map.Entry<K, V>) o;
|
||||
final V existing = get(entry.getKey());
|
||||
final V value = entry.getValue();
|
||||
return existing == value ||
|
||||
(existing != null && existing.equals(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Map.Entry<K, V> entry) {
|
||||
if (!contains(entry)) {
|
||||
put(entry.getKey(), entry.getValue());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws a {@link ClassCastException} if o is not of the expected type.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map.Entry<K, V> entry = (Map.Entry<K, V>) o;
|
||||
if (contains(entry)) {
|
||||
SmallSortedMap.this.remove(entry.getKey());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
SmallSortedMap.this.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator implementation that switches from the entry array to the overflow
|
||||
* entries appropriately.
|
||||
*/
|
||||
private class EntryIterator implements Iterator<Map.Entry<K, V>> {
|
||||
|
||||
private int pos = -1;
|
||||
private boolean nextCalledBeforeRemove;
|
||||
private Iterator<Map.Entry<K, V>> lazyOverflowIterator;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return (pos + 1) < entryList.size() ||
|
||||
getOverflowIterator().hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map.Entry<K, V> next() {
|
||||
nextCalledBeforeRemove = true;
|
||||
// Always increment pos so that we know whether the last returned value
|
||||
// was from the array or from overflow.
|
||||
if (++pos < entryList.size()) {
|
||||
return entryList.get(pos);
|
||||
}
|
||||
return getOverflowIterator().next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
if (!nextCalledBeforeRemove) {
|
||||
throw new IllegalStateException("remove() was called before next()");
|
||||
}
|
||||
nextCalledBeforeRemove = false;
|
||||
checkMutable();
|
||||
|
||||
if (pos < entryList.size()) {
|
||||
removeArrayEntryAt(pos--);
|
||||
} else {
|
||||
getOverflowIterator().remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* It is important to create the overflow iterator only after the array
|
||||
* entries have been iterated over because the overflow entry set changes
|
||||
* when the client calls remove() on the array entries, which invalidates
|
||||
* any existing iterators.
|
||||
*/
|
||||
private Iterator<Map.Entry<K, V>> getOverflowIterator() {
|
||||
if (lazyOverflowIterator == null) {
|
||||
lazyOverflowIterator = overflowEntries.entrySet().iterator();
|
||||
}
|
||||
return lazyOverflowIterator;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class that holds immutable instances of an Iterable/Iterator that
|
||||
* we return when the overflow entries is empty. This eliminates the creation
|
||||
* of an Iterator object when there is nothing to iterate over.
|
||||
*/
|
||||
private static class EmptySet {
|
||||
|
||||
private static final Iterator<Object> ITERATOR = new Iterator<Object>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public Object next() {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
|
||||
private static final Iterable<Object> ITERABLE = new Iterable<Object>() {
|
||||
@Override
|
||||
public Iterator<Object> iterator() {
|
||||
return ITERATOR;
|
||||
}
|
||||
};
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> Iterable<T> iterable() {
|
||||
return (Iterable<T>) ITERABLE;
|
||||
}
|
||||
}
|
||||
}
|
@ -46,15 +46,17 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Provide ascii text parsing and formatting support for proto2 instances.
|
||||
* Provide text parsing and formatting support for proto2 instances.
|
||||
* The implementation largely follows google/protobuf/text_format.cc.
|
||||
*
|
||||
* @author wenboz@google.com Wenbo Zhu
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public final class TextFormat {
|
||||
private TextFormat() {
|
||||
}
|
||||
private TextFormat() {}
|
||||
|
||||
private static final Printer DEFAULT_PRINTER = new Printer(false);
|
||||
private static final Printer SINGLE_LINE_PRINTER = new Printer(true);
|
||||
|
||||
/**
|
||||
* Outputs a textual representation of the Protocol Message supplied into
|
||||
@ -63,16 +65,44 @@ public final class TextFormat {
|
||||
*/
|
||||
public static void print(final Message message, final Appendable output)
|
||||
throws IOException {
|
||||
final TextGenerator generator = new TextGenerator(output);
|
||||
print(message, generator);
|
||||
DEFAULT_PRINTER.print(message, new TextGenerator(output));
|
||||
}
|
||||
|
||||
/** Outputs a textual representation of {@code fields} to {@code output}. */
|
||||
public static void print(final UnknownFieldSet fields,
|
||||
final Appendable output)
|
||||
throws IOException {
|
||||
final TextGenerator generator = new TextGenerator(output);
|
||||
printUnknownFields(fields, generator);
|
||||
DEFAULT_PRINTER.printUnknownFields(fields, new TextGenerator(output));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a human readable form of this message, useful for debugging and
|
||||
* other purposes, with no newline characters.
|
||||
*/
|
||||
public static String shortDebugString(final Message message) {
|
||||
try {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
SINGLE_LINE_PRINTER.print(message, new TextGenerator(sb));
|
||||
// Single line mode currently might have an extra space at the end.
|
||||
return sb.toString().trim();
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a human readable form of the unknown fields, useful for debugging
|
||||
* and other purposes, with no newline characters.
|
||||
*/
|
||||
public static String shortDebugString(final UnknownFieldSet fields) {
|
||||
try {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
SINGLE_LINE_PRINTER.printUnknownFields(fields, new TextGenerator(sb));
|
||||
// Single line mode currently might have an extra space at the end.
|
||||
return sb.toString().trim();
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,9 +115,7 @@ public final class TextFormat {
|
||||
print(message, text);
|
||||
return text.toString();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Writing to a StringBuilder threw an IOException (should never " +
|
||||
"happen).", e);
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,28 +129,15 @@ public final class TextFormat {
|
||||
print(fields, text);
|
||||
return text.toString();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Writing to a StringBuilder threw an IOException (should never " +
|
||||
"happen).", e);
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void print(final Message message,
|
||||
final TextGenerator generator)
|
||||
throws IOException {
|
||||
for (final Map.Entry<FieldDescriptor, Object> field :
|
||||
message.getAllFields().entrySet()) {
|
||||
printField(field.getKey(), field.getValue(), generator);
|
||||
}
|
||||
printUnknownFields(message.getUnknownFields(), generator);
|
||||
}
|
||||
|
||||
public static void printField(final FieldDescriptor field,
|
||||
final Object value,
|
||||
final Appendable output)
|
||||
throws IOException {
|
||||
final TextGenerator generator = new TextGenerator(output);
|
||||
printField(field, value, generator);
|
||||
DEFAULT_PRINTER.printField(field, value, new TextGenerator(output));
|
||||
}
|
||||
|
||||
public static String printFieldToString(final FieldDescriptor field,
|
||||
@ -132,157 +147,263 @@ public final class TextFormat {
|
||||
printField(field, value, text);
|
||||
return text.toString();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Writing to a StringBuilder threw an IOException (should never " +
|
||||
"happen).", e);
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void printField(final FieldDescriptor field,
|
||||
final Object value,
|
||||
final TextGenerator generator)
|
||||
throws IOException {
|
||||
if (field.isRepeated()) {
|
||||
// Repeated field. Print each element.
|
||||
for (final Object element : (List<?>) value) {
|
||||
printSingleField(field, element, generator);
|
||||
}
|
||||
} else {
|
||||
printSingleField(field, value, generator);
|
||||
}
|
||||
/**
|
||||
* Outputs a textual representation of the value of given field value.
|
||||
*
|
||||
* @param field the descriptor of the field
|
||||
* @param value the value of the field
|
||||
* @param output the output to which to append the formatted value
|
||||
* @throws ClassCastException if the value is not appropriate for the
|
||||
* given field descriptor
|
||||
* @throws IOException if there is an exception writing to the output
|
||||
*/
|
||||
public static void printFieldValue(final FieldDescriptor field,
|
||||
final Object value,
|
||||
final Appendable output)
|
||||
throws IOException {
|
||||
DEFAULT_PRINTER.printFieldValue(field, value, new TextGenerator(output));
|
||||
}
|
||||
|
||||
private static void printSingleField(final FieldDescriptor field,
|
||||
final Object value,
|
||||
final TextGenerator generator)
|
||||
throws IOException {
|
||||
if (field.isExtension()) {
|
||||
generator.print("[");
|
||||
// We special-case MessageSet elements for compatibility with proto1.
|
||||
if (field.getContainingType().getOptions().getMessageSetWireFormat()
|
||||
&& (field.getType() == FieldDescriptor.Type.MESSAGE)
|
||||
&& (field.isOptional())
|
||||
// object equality
|
||||
&& (field.getExtensionScope() == field.getMessageType())) {
|
||||
generator.print(field.getMessageType().getFullName());
|
||||
} else {
|
||||
generator.print(field.getFullName());
|
||||
}
|
||||
generator.print("]");
|
||||
} else {
|
||||
if (field.getType() == FieldDescriptor.Type.GROUP) {
|
||||
// Groups must be serialized with their original capitalization.
|
||||
generator.print(field.getMessageType().getName());
|
||||
} else {
|
||||
generator.print(field.getName());
|
||||
}
|
||||
}
|
||||
|
||||
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
generator.print(" {\n");
|
||||
generator.indent();
|
||||
} else {
|
||||
generator.print(": ");
|
||||
}
|
||||
|
||||
printFieldValue(field, value, generator);
|
||||
|
||||
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
generator.outdent();
|
||||
generator.print("}");
|
||||
}
|
||||
generator.print("\n");
|
||||
/**
|
||||
* Outputs a textual representation of the value of an unknown field.
|
||||
*
|
||||
* @param tag the field's tag number
|
||||
* @param value the value of the field
|
||||
* @param output the output to which to append the formatted value
|
||||
* @throws ClassCastException if the value is not appropriate for the
|
||||
* given field descriptor
|
||||
* @throws IOException if there is an exception writing to the output
|
||||
*/
|
||||
public static void printUnknownFieldValue(final int tag,
|
||||
final Object value,
|
||||
final Appendable output)
|
||||
throws IOException {
|
||||
printUnknownFieldValue(tag, value, new TextGenerator(output));
|
||||
}
|
||||
|
||||
private static void printFieldValue(final FieldDescriptor field,
|
||||
final Object value,
|
||||
final TextGenerator generator)
|
||||
throws IOException {
|
||||
switch (field.getType()) {
|
||||
case INT32:
|
||||
case INT64:
|
||||
case SINT32:
|
||||
case SINT64:
|
||||
case SFIXED32:
|
||||
case SFIXED64:
|
||||
case FLOAT:
|
||||
case DOUBLE:
|
||||
case BOOL:
|
||||
// Good old toString() does what we want for these types.
|
||||
generator.print(value.toString());
|
||||
break;
|
||||
|
||||
case UINT32:
|
||||
case FIXED32:
|
||||
generator.print(unsignedToString((Integer) value));
|
||||
break;
|
||||
|
||||
case UINT64:
|
||||
case FIXED64:
|
||||
private static void printUnknownFieldValue(final int tag,
|
||||
final Object value,
|
||||
final TextGenerator generator)
|
||||
throws IOException {
|
||||
switch (WireFormat.getTagWireType(tag)) {
|
||||
case WireFormat.WIRETYPE_VARINT:
|
||||
generator.print(unsignedToString((Long) value));
|
||||
break;
|
||||
|
||||
case STRING:
|
||||
generator.print("\"");
|
||||
generator.print(escapeText((String) value));
|
||||
generator.print("\"");
|
||||
case WireFormat.WIRETYPE_FIXED32:
|
||||
generator.print(
|
||||
String.format((Locale) null, "0x%08x", (Integer) value));
|
||||
break;
|
||||
|
||||
case BYTES:
|
||||
case WireFormat.WIRETYPE_FIXED64:
|
||||
generator.print(String.format((Locale) null, "0x%016x", (Long) value));
|
||||
break;
|
||||
case WireFormat.WIRETYPE_LENGTH_DELIMITED:
|
||||
generator.print("\"");
|
||||
generator.print(escapeBytes((ByteString) value));
|
||||
generator.print("\"");
|
||||
break;
|
||||
|
||||
case ENUM:
|
||||
generator.print(((EnumValueDescriptor) value).getName());
|
||||
break;
|
||||
|
||||
case MESSAGE:
|
||||
case GROUP:
|
||||
print((Message) value, generator);
|
||||
case WireFormat.WIRETYPE_START_GROUP:
|
||||
DEFAULT_PRINTER.printUnknownFields((UnknownFieldSet) value, generator);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Bad tag: " + tag);
|
||||
}
|
||||
}
|
||||
|
||||
private static void printUnknownFields(final UnknownFieldSet unknownFields,
|
||||
final TextGenerator generator)
|
||||
throws IOException {
|
||||
for (final Map.Entry<Integer, UnknownFieldSet.Field> entry :
|
||||
unknownFields.asMap().entrySet()) {
|
||||
final UnknownFieldSet.Field field = entry.getValue();
|
||||
/** Helper class for converting protobufs to text. */
|
||||
private static final class Printer {
|
||||
/** Whether to omit newlines from the output. */
|
||||
final boolean singleLineMode;
|
||||
|
||||
for (final long value : field.getVarintList()) {
|
||||
generator.print(entry.getKey().toString());
|
||||
private Printer(final boolean singleLineMode) {
|
||||
this.singleLineMode = singleLineMode;
|
||||
}
|
||||
|
||||
private void print(final Message message, final TextGenerator generator)
|
||||
throws IOException {
|
||||
for (Map.Entry<FieldDescriptor, Object> field
|
||||
: message.getAllFields().entrySet()) {
|
||||
printField(field.getKey(), field.getValue(), generator);
|
||||
}
|
||||
printUnknownFields(message.getUnknownFields(), generator);
|
||||
}
|
||||
|
||||
private void printField(final FieldDescriptor field, final Object value,
|
||||
final TextGenerator generator) throws IOException {
|
||||
if (field.isRepeated()) {
|
||||
// Repeated field. Print each element.
|
||||
for (Object element : (List<?>) value) {
|
||||
printSingleField(field, element, generator);
|
||||
}
|
||||
} else {
|
||||
printSingleField(field, value, generator);
|
||||
}
|
||||
}
|
||||
|
||||
private void printSingleField(final FieldDescriptor field,
|
||||
final Object value,
|
||||
final TextGenerator generator)
|
||||
throws IOException {
|
||||
if (field.isExtension()) {
|
||||
generator.print("[");
|
||||
// We special-case MessageSet elements for compatibility with proto1.
|
||||
if (field.getContainingType().getOptions().getMessageSetWireFormat()
|
||||
&& (field.getType() == FieldDescriptor.Type.MESSAGE)
|
||||
&& (field.isOptional())
|
||||
// object equality
|
||||
&& (field.getExtensionScope() == field.getMessageType())) {
|
||||
generator.print(field.getMessageType().getFullName());
|
||||
} else {
|
||||
generator.print(field.getFullName());
|
||||
}
|
||||
generator.print("]");
|
||||
} else {
|
||||
if (field.getType() == FieldDescriptor.Type.GROUP) {
|
||||
// Groups must be serialized with their original capitalization.
|
||||
generator.print(field.getMessageType().getName());
|
||||
} else {
|
||||
generator.print(field.getName());
|
||||
}
|
||||
}
|
||||
|
||||
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
if (singleLineMode) {
|
||||
generator.print(" { ");
|
||||
} else {
|
||||
generator.print(" {\n");
|
||||
generator.indent();
|
||||
}
|
||||
} else {
|
||||
generator.print(": ");
|
||||
generator.print(unsignedToString(value));
|
||||
generator.print("\n");
|
||||
}
|
||||
for (final int value : field.getFixed32List()) {
|
||||
generator.print(entry.getKey().toString());
|
||||
|
||||
printFieldValue(field, value, generator);
|
||||
|
||||
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
if (singleLineMode) {
|
||||
generator.print("} ");
|
||||
} else {
|
||||
generator.outdent();
|
||||
generator.print("}\n");
|
||||
}
|
||||
} else {
|
||||
if (singleLineMode) {
|
||||
generator.print(" ");
|
||||
} else {
|
||||
generator.print("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void printFieldValue(final FieldDescriptor field,
|
||||
final Object value,
|
||||
final TextGenerator generator)
|
||||
throws IOException {
|
||||
switch (field.getType()) {
|
||||
case INT32:
|
||||
case SINT32:
|
||||
case SFIXED32:
|
||||
generator.print(((Integer) value).toString());
|
||||
break;
|
||||
|
||||
case INT64:
|
||||
case SINT64:
|
||||
case SFIXED64:
|
||||
generator.print(((Long) value).toString());
|
||||
break;
|
||||
|
||||
case BOOL:
|
||||
generator.print(((Boolean) value).toString());
|
||||
break;
|
||||
|
||||
case FLOAT:
|
||||
generator.print(((Float) value).toString());
|
||||
break;
|
||||
|
||||
case DOUBLE:
|
||||
generator.print(((Double) value).toString());
|
||||
break;
|
||||
|
||||
case UINT32:
|
||||
case FIXED32:
|
||||
generator.print(unsignedToString((Integer) value));
|
||||
break;
|
||||
|
||||
case UINT64:
|
||||
case FIXED64:
|
||||
generator.print(unsignedToString((Long) value));
|
||||
break;
|
||||
|
||||
case STRING:
|
||||
generator.print("\"");
|
||||
generator.print(escapeText((String) value));
|
||||
generator.print("\"");
|
||||
break;
|
||||
|
||||
case BYTES:
|
||||
generator.print("\"");
|
||||
generator.print(escapeBytes((ByteString) value));
|
||||
generator.print("\"");
|
||||
break;
|
||||
|
||||
case ENUM:
|
||||
generator.print(((EnumValueDescriptor) value).getName());
|
||||
break;
|
||||
|
||||
case MESSAGE:
|
||||
case GROUP:
|
||||
print((Message) value, generator);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void printUnknownFields(final UnknownFieldSet unknownFields,
|
||||
final TextGenerator generator)
|
||||
throws IOException {
|
||||
for (Map.Entry<Integer, UnknownFieldSet.Field> entry :
|
||||
unknownFields.asMap().entrySet()) {
|
||||
final int number = entry.getKey();
|
||||
final UnknownFieldSet.Field field = entry.getValue();
|
||||
printUnknownField(number, WireFormat.WIRETYPE_VARINT,
|
||||
field.getVarintList(), generator);
|
||||
printUnknownField(number, WireFormat.WIRETYPE_FIXED32,
|
||||
field.getFixed32List(), generator);
|
||||
printUnknownField(number, WireFormat.WIRETYPE_FIXED64,
|
||||
field.getFixed64List(), generator);
|
||||
printUnknownField(number, WireFormat.WIRETYPE_LENGTH_DELIMITED,
|
||||
field.getLengthDelimitedList(), generator);
|
||||
for (final UnknownFieldSet value : field.getGroupList()) {
|
||||
generator.print(entry.getKey().toString());
|
||||
if (singleLineMode) {
|
||||
generator.print(" { ");
|
||||
} else {
|
||||
generator.print(" {\n");
|
||||
generator.indent();
|
||||
}
|
||||
printUnknownFields(value, generator);
|
||||
if (singleLineMode) {
|
||||
generator.print("} ");
|
||||
} else {
|
||||
generator.outdent();
|
||||
generator.print("}\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void printUnknownField(final int number,
|
||||
final int wireType,
|
||||
final List<?> values,
|
||||
final TextGenerator generator)
|
||||
throws IOException {
|
||||
for (final Object value : values) {
|
||||
generator.print(String.valueOf(number));
|
||||
generator.print(": ");
|
||||
generator.print(String.format((Locale) null, "0x%08x", value));
|
||||
generator.print("\n");
|
||||
}
|
||||
for (final long value : field.getFixed64List()) {
|
||||
generator.print(entry.getKey().toString());
|
||||
generator.print(": ");
|
||||
generator.print(String.format((Locale) null, "0x%016x", value));
|
||||
generator.print("\n");
|
||||
}
|
||||
for (final ByteString value : field.getLengthDelimitedList()) {
|
||||
generator.print(entry.getKey().toString());
|
||||
generator.print(": \"");
|
||||
generator.print(escapeBytes(value));
|
||||
generator.print("\"\n");
|
||||
}
|
||||
for (final UnknownFieldSet value : field.getGroupList()) {
|
||||
generator.print(entry.getKey().toString());
|
||||
generator.print(" {\n");
|
||||
generator.indent();
|
||||
printUnknownFields(value, generator);
|
||||
generator.outdent();
|
||||
generator.print("}\n");
|
||||
printUnknownFieldValue(wireType, value, generator);
|
||||
generator.print(singleLineMode ? " " : "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -312,9 +433,9 @@ public final class TextFormat {
|
||||
* An inner class for writing text to the output stream.
|
||||
*/
|
||||
private static final class TextGenerator {
|
||||
private Appendable output;
|
||||
private boolean atStartOfLine = true;
|
||||
private final Appendable output;
|
||||
private final StringBuilder indent = new StringBuilder();
|
||||
private boolean atStartOfLine = true;
|
||||
|
||||
private TextGenerator(final Appendable output) {
|
||||
this.output = output;
|
||||
@ -670,10 +791,14 @@ public final class TextFormat {
|
||||
* Otherwise, throw a {@link ParseException}.
|
||||
*/
|
||||
public boolean consumeBoolean() throws ParseException {
|
||||
if (currentToken.equals("true")) {
|
||||
if (currentToken.equals("true") ||
|
||||
currentToken.equals("t") ||
|
||||
currentToken.equals("1")) {
|
||||
nextToken();
|
||||
return true;
|
||||
} else if (currentToken.equals("false")) {
|
||||
} else if (currentToken.equals("false") ||
|
||||
currentToken.equals("f") ||
|
||||
currentToken.equals("0")) {
|
||||
nextToken();
|
||||
return false;
|
||||
} else {
|
||||
@ -1063,6 +1188,9 @@ public final class TextFormat {
|
||||
case '\'': builder.append("\\\'"); break;
|
||||
case '"' : builder.append("\\\""); break;
|
||||
default:
|
||||
// Note: Bytes with the high-order bit set should be escaped. Since
|
||||
// bytes are signed, such bytes will compare less than 0x20, hence
|
||||
// the following line is correct.
|
||||
if (b >= 0x20) {
|
||||
builder.append((char) b);
|
||||
} else {
|
||||
@ -1082,27 +1210,37 @@ public final class TextFormat {
|
||||
* {@link #escapeBytes(ByteString)}. Two-digit hex escapes (starting with
|
||||
* "\x") are also recognized.
|
||||
*/
|
||||
static ByteString unescapeBytes(final CharSequence input)
|
||||
static ByteString unescapeBytes(final CharSequence charString)
|
||||
throws InvalidEscapeSequenceException {
|
||||
final byte[] result = new byte[input.length()];
|
||||
// First convert the Java characater sequence to UTF-8 bytes.
|
||||
ByteString input = ByteString.copyFromUtf8(charString.toString());
|
||||
// Then unescape certain byte sequences introduced by ASCII '\\'. The valid
|
||||
// escapes can all be expressed with ASCII characters, so it is safe to
|
||||
// operate on bytes here.
|
||||
//
|
||||
// Unescaping the input byte array will result in a byte sequence that's no
|
||||
// longer than the input. That's because each escape sequence is between
|
||||
// two and four bytes long and stands for a single byte.
|
||||
final byte[] result = new byte[input.size()];
|
||||
int pos = 0;
|
||||
for (int i = 0; i < input.length(); i++) {
|
||||
char c = input.charAt(i);
|
||||
for (int i = 0; i < input.size(); i++) {
|
||||
byte c = input.byteAt(i);
|
||||
if (c == '\\') {
|
||||
if (i + 1 < input.length()) {
|
||||
if (i + 1 < input.size()) {
|
||||
++i;
|
||||
c = input.charAt(i);
|
||||
c = input.byteAt(i);
|
||||
if (isOctal(c)) {
|
||||
// Octal escape.
|
||||
int code = digitValue(c);
|
||||
if (i + 1 < input.length() && isOctal(input.charAt(i + 1))) {
|
||||
if (i + 1 < input.size() && isOctal(input.byteAt(i + 1))) {
|
||||
++i;
|
||||
code = code * 8 + digitValue(input.charAt(i));
|
||||
code = code * 8 + digitValue(input.byteAt(i));
|
||||
}
|
||||
if (i + 1 < input.length() && isOctal(input.charAt(i + 1))) {
|
||||
if (i + 1 < input.size() && isOctal(input.byteAt(i + 1))) {
|
||||
++i;
|
||||
code = code * 8 + digitValue(input.charAt(i));
|
||||
code = code * 8 + digitValue(input.byteAt(i));
|
||||
}
|
||||
// TODO: Check that 0 <= code && code <= 0xFF.
|
||||
result[pos++] = (byte)code;
|
||||
} else {
|
||||
switch (c) {
|
||||
@ -1120,31 +1258,31 @@ public final class TextFormat {
|
||||
case 'x':
|
||||
// hex escape
|
||||
int code = 0;
|
||||
if (i + 1 < input.length() && isHex(input.charAt(i + 1))) {
|
||||
if (i + 1 < input.size() && isHex(input.byteAt(i + 1))) {
|
||||
++i;
|
||||
code = digitValue(input.charAt(i));
|
||||
code = digitValue(input.byteAt(i));
|
||||
} else {
|
||||
throw new InvalidEscapeSequenceException(
|
||||
"Invalid escape sequence: '\\x' with no digits");
|
||||
"Invalid escape sequence: '\\x' with no digits");
|
||||
}
|
||||
if (i + 1 < input.length() && isHex(input.charAt(i + 1))) {
|
||||
if (i + 1 < input.size() && isHex(input.byteAt(i + 1))) {
|
||||
++i;
|
||||
code = code * 16 + digitValue(input.charAt(i));
|
||||
code = code * 16 + digitValue(input.byteAt(i));
|
||||
}
|
||||
result[pos++] = (byte)code;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidEscapeSequenceException(
|
||||
"Invalid escape sequence: '\\" + c + '\'');
|
||||
"Invalid escape sequence: '\\" + (char)c + '\'');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new InvalidEscapeSequenceException(
|
||||
"Invalid escape sequence: '\\' at end of string.");
|
||||
"Invalid escape sequence: '\\' at end of string.");
|
||||
}
|
||||
} else {
|
||||
result[pos++] = (byte)c;
|
||||
result[pos++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1182,12 +1320,12 @@ public final class TextFormat {
|
||||
}
|
||||
|
||||
/** Is this an octal digit? */
|
||||
private static boolean isOctal(final char c) {
|
||||
private static boolean isOctal(final byte c) {
|
||||
return '0' <= c && c <= '7';
|
||||
}
|
||||
|
||||
/** Is this a hex digit? */
|
||||
private static boolean isHex(final char c) {
|
||||
private static boolean isHex(final byte c) {
|
||||
return ('0' <= c && c <= '9') ||
|
||||
('a' <= c && c <= 'f') ||
|
||||
('A' <= c && c <= 'F');
|
||||
@ -1198,7 +1336,7 @@ public final class TextFormat {
|
||||
* numeric value. This is like {@code Character.digit()} but we don't accept
|
||||
* non-ASCII digits.
|
||||
*/
|
||||
private static int digitValue(final char c) {
|
||||
private static int digitValue(final byte c) {
|
||||
if ('0' <= c && c <= '9') {
|
||||
return c - '0';
|
||||
} else if ('a' <= c && c <= 'z') {
|
||||
|
@ -0,0 +1,146 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.RandomAccess;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* An implementation of {@link LazyStringList} that wraps another
|
||||
* {@link LazyStringList} such that it cannot be modified via the wrapper.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class UnmodifiableLazyStringList extends AbstractList<String>
|
||||
implements LazyStringList, RandomAccess {
|
||||
|
||||
private final LazyStringList list;
|
||||
|
||||
public UnmodifiableLazyStringList(LazyStringList list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(int index) {
|
||||
return list.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString getByteString(int index) {
|
||||
return list.getByteString(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(ByteString element) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<String> listIterator(final int index) {
|
||||
return new ListIterator<String>() {
|
||||
ListIterator<String> iter = list.listIterator(index);
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iter.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String next() {
|
||||
return iter.next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPrevious() {
|
||||
return iter.hasPrevious();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String previous() {
|
||||
return iter.previous();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextIndex() {
|
||||
return iter.nextIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int previousIndex() {
|
||||
return iter.previousIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(String o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(String o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> iterator() {
|
||||
return new Iterator<String>() {
|
||||
Iterator<String> iter = list.iterator();
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iter.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String next() {
|
||||
return iter.next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -45,12 +45,12 @@ public final class WireFormat {
|
||||
// Do not allow instantiation.
|
||||
private WireFormat() {}
|
||||
|
||||
static final int WIRETYPE_VARINT = 0;
|
||||
static final int WIRETYPE_FIXED64 = 1;
|
||||
static final int WIRETYPE_LENGTH_DELIMITED = 2;
|
||||
static final int WIRETYPE_START_GROUP = 3;
|
||||
static final int WIRETYPE_END_GROUP = 4;
|
||||
static final int WIRETYPE_FIXED32 = 5;
|
||||
public static final int WIRETYPE_VARINT = 0;
|
||||
public static final int WIRETYPE_FIXED64 = 1;
|
||||
public static final int WIRETYPE_LENGTH_DELIMITED = 2;
|
||||
public static final int WIRETYPE_START_GROUP = 3;
|
||||
public static final int WIRETYPE_END_GROUP = 4;
|
||||
public static final int WIRETYPE_FIXED32 = 5;
|
||||
|
||||
static final int TAG_TYPE_BITS = 3;
|
||||
static final int TAG_TYPE_MASK = (1 << TAG_TYPE_BITS) - 1;
|
||||
|
@ -366,7 +366,7 @@ public class AbstractMessageTest extends TestCase {
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Tests for equals and hashCode
|
||||
|
||||
|
||||
public void testEqualsAndHashCode() throws Exception {
|
||||
TestAllTypes a = TestUtil.getAllSet();
|
||||
TestAllTypes b = TestAllTypes.newBuilder().build();
|
||||
@ -382,7 +382,7 @@ public class AbstractMessageTest extends TestCase {
|
||||
checkEqualsIsConsistent(d);
|
||||
checkEqualsIsConsistent(e);
|
||||
checkEqualsIsConsistent(f);
|
||||
|
||||
|
||||
checkNotEqual(a, b);
|
||||
checkNotEqual(a, c);
|
||||
checkNotEqual(a, d);
|
||||
@ -413,19 +413,20 @@ public class AbstractMessageTest extends TestCase {
|
||||
checkEqualsIsConsistent(eUnknownFields);
|
||||
checkEqualsIsConsistent(fUnknownFields);
|
||||
|
||||
// Subseqent reconstitutions should be identical
|
||||
// Subsequent reconstitutions should be identical
|
||||
UnittestProto.TestEmptyMessage eUnknownFields2 =
|
||||
UnittestProto.TestEmptyMessage.parseFrom(e.toByteArray());
|
||||
checkEqualsIsConsistent(eUnknownFields, eUnknownFields2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that the given proto has symetric equals and hashCode methods.
|
||||
*/
|
||||
private void checkEqualsIsConsistent(Message message) {
|
||||
// Object should be equal to itself.
|
||||
assertEquals(message, message);
|
||||
|
||||
|
||||
// Object should be equal to a dynamic copy of itself.
|
||||
DynamicMessage dynamic = DynamicMessage.newBuilder(message).build();
|
||||
checkEqualsIsConsistent(message, dynamic);
|
||||
@ -442,7 +443,7 @@ public class AbstractMessageTest extends TestCase {
|
||||
|
||||
/**
|
||||
* Asserts that the given protos are not equal and have different hash codes.
|
||||
*
|
||||
*
|
||||
* @warning It's valid for non-equal objects to have the same hash code, so
|
||||
* this test is stricter than it needs to be. However, this should happen
|
||||
* relatively rarely.
|
||||
|
@ -325,6 +325,27 @@ public class CodedInputStreamTest extends TestCase {
|
||||
assertEquals(2, input.readRawByte());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a bug in skipRawBytes() has been fixed: if the skip skips
|
||||
* past the end of a buffer with a limit that has been set past the end of
|
||||
* that buffer, this should not break things.
|
||||
*/
|
||||
public void testSkipRawBytesPastEndOfBufferWithLimit() throws Exception {
|
||||
byte[] rawBytes = new byte[] { 1, 2, 3, 4, 5 };
|
||||
CodedInputStream input = CodedInputStream.newInstance(
|
||||
new SmallBlockInputStream(rawBytes, 3));
|
||||
|
||||
int limit = input.pushLimit(4);
|
||||
// In order to expose the bug we need to read at least one byte to prime the
|
||||
// buffer inside the CodedInputStream.
|
||||
assertEquals(1, input.readRawByte());
|
||||
// Skip to the end of the limit.
|
||||
input.skipRawBytes(3);
|
||||
assertTrue(input.isAtEnd());
|
||||
input.popLimit(limit);
|
||||
assertEquals(5, input.readRawByte());
|
||||
}
|
||||
|
||||
public void testReadHugeBlob() throws Exception {
|
||||
// Allocate and initialize a 1MB blob.
|
||||
byte[] blob = new byte[1 << 20];
|
||||
|
@ -0,0 +1,80 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import protobuf_unittest.UnittestProto.TestDeprecatedFields;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
/**
|
||||
* Test field deprecation
|
||||
*
|
||||
* @author birdo@google.com (Roberto Scaramuzzi)
|
||||
*/
|
||||
public class DeprecatedFieldTest extends TestCase {
|
||||
private String[] deprecatedGetterNames = {
|
||||
"hasDeprecatedInt32",
|
||||
"getDeprecatedInt32"};
|
||||
|
||||
private String[] deprecatedBuilderGetterNames = {
|
||||
"hasDeprecatedInt32",
|
||||
"getDeprecatedInt32",
|
||||
"clearDeprecatedInt32"};
|
||||
|
||||
private String[] deprecatedBuilderSetterNames = {
|
||||
"setDeprecatedInt32"};
|
||||
|
||||
public void testDeprecatedField() throws Exception {
|
||||
Class<?> deprecatedFields = TestDeprecatedFields.class;
|
||||
Class<?> deprecatedFieldsBuilder = TestDeprecatedFields.Builder.class;
|
||||
for (String name : deprecatedGetterNames) {
|
||||
Method method = deprecatedFields.getMethod(name);
|
||||
assertTrue("Method " + name + " should be deprecated",
|
||||
isDeprecated(method));
|
||||
}
|
||||
for (String name : deprecatedBuilderGetterNames) {
|
||||
Method method = deprecatedFieldsBuilder.getMethod(name);
|
||||
assertTrue("Method " + name + " should be deprecated",
|
||||
isDeprecated(method));
|
||||
}
|
||||
for (String name : deprecatedBuilderSetterNames) {
|
||||
Method method = deprecatedFieldsBuilder.getMethod(name, int.class);
|
||||
assertTrue("Method " + name + " should be deprecated",
|
||||
isDeprecated(method));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDeprecated(AnnotatedElement annotated) {
|
||||
return annotated.isAnnotationPresent(Deprecated.class);
|
||||
}
|
||||
}
|
@ -44,6 +44,7 @@ import com.google.protobuf.Descriptors.MethodDescriptor;
|
||||
|
||||
import com.google.protobuf.test.UnittestImport;
|
||||
import com.google.protobuf.test.UnittestImport.ImportEnum;
|
||||
import com.google.protobuf.test.UnittestImport.ImportMessage;
|
||||
import protobuf_unittest.UnittestProto;
|
||||
import protobuf_unittest.UnittestProto.ForeignEnum;
|
||||
import protobuf_unittest.UnittestProto.ForeignMessage;
|
||||
|
@ -0,0 +1,48 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
/**
|
||||
* A prerun for a test suite that allows running the full protocol buffer
|
||||
* tests in a mode that disables the optimization for not using
|
||||
* {@link RepeatedFieldBuilder} and {@link SingleFieldBuilder} until they are
|
||||
* requested. This allows us to run all the tests through both code paths
|
||||
* and ensures that both code paths produce identical results.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class ForceFieldBuildersPreRun implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
GeneratedMessage.enableAlwaysUseFieldBuildersForTesting();
|
||||
}
|
||||
}
|
@ -30,26 +30,43 @@
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
|
||||
import com.google.protobuf.test.UnittestImport;
|
||||
import protobuf_unittest.EnumWithNoOuter;
|
||||
import protobuf_unittest.MessageWithNoOuter;
|
||||
import protobuf_unittest.MultipleFilesTestProto;
|
||||
import protobuf_unittest.NestedExtension.MyNestedExtension;
|
||||
import protobuf_unittest.NestedExtensionLite.MyNestedExtensionLite;
|
||||
import protobuf_unittest.NonNestedExtension;
|
||||
import protobuf_unittest.NonNestedExtension.MessageToBeExtended;
|
||||
import protobuf_unittest.NonNestedExtension.MyNonNestedExtension;
|
||||
import protobuf_unittest.NonNestedExtensionLite;
|
||||
import protobuf_unittest.NonNestedExtensionLite.MessageLiteToBeExtended;
|
||||
import protobuf_unittest.NonNestedExtensionLite.MyNonNestedExtensionLite;
|
||||
import protobuf_unittest.ServiceWithNoOuter;
|
||||
import protobuf_unittest.UnittestOptimizeFor.TestOptimizedForSize;
|
||||
import protobuf_unittest.UnittestOptimizeFor.TestOptionalOptimizedForSize;
|
||||
import protobuf_unittest.UnittestOptimizeFor.TestRequiredOptimizedForSize;
|
||||
import protobuf_unittest.UnittestProto;
|
||||
import protobuf_unittest.UnittestProto.ForeignMessage;
|
||||
import protobuf_unittest.UnittestProto.ForeignEnum;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.ForeignMessage;
|
||||
import protobuf_unittest.UnittestProto.ForeignMessageOrBuilder;
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder;
|
||||
import protobuf_unittest.UnittestProto.TestExtremeDefaultValues;
|
||||
import protobuf_unittest.UnittestProto.TestPackedTypes;
|
||||
import protobuf_unittest.UnittestProto.TestUnpackedTypes;
|
||||
import protobuf_unittest.MultipleFilesTestProto;
|
||||
import protobuf_unittest.MessageWithNoOuter;
|
||||
import protobuf_unittest.EnumWithNoOuter;
|
||||
import protobuf_unittest.ServiceWithNoOuter;
|
||||
import com.google.protobuf.UnittestLite;
|
||||
import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Unit test for generated messages and generated code. See also
|
||||
@ -68,32 +85,111 @@ public class GeneratedMessageTest extends TestCase {
|
||||
TestAllTypes.newBuilder().getDefaultInstanceForType());
|
||||
}
|
||||
|
||||
public void testAccessors() throws Exception {
|
||||
public void testMessageOrBuilder() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TestUtil.setAllFields(builder);
|
||||
TestAllTypes message = builder.build();
|
||||
TestUtil.assertAllFieldsSet(message);
|
||||
}
|
||||
|
||||
public void testDoubleBuildError() throws Exception {
|
||||
public void testUsingBuilderMultipleTimes() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
builder.build();
|
||||
try {
|
||||
builder.build();
|
||||
fail("Should have thrown exception.");
|
||||
} catch (IllegalStateException e) {
|
||||
// Success.
|
||||
}
|
||||
// primitive field scalar and repeated
|
||||
builder.setOptionalSfixed64(100);
|
||||
builder.addRepeatedInt32(100);
|
||||
// enum field scalar and repeated
|
||||
builder.setOptionalImportEnum(UnittestImport.ImportEnum.IMPORT_BAR);
|
||||
builder.addRepeatedImportEnum(UnittestImport.ImportEnum.IMPORT_BAR);
|
||||
// proto field scalar and repeated
|
||||
builder.setOptionalForeignMessage(ForeignMessage.newBuilder().setC(1));
|
||||
builder.addRepeatedForeignMessage(ForeignMessage.newBuilder().setC(1));
|
||||
|
||||
TestAllTypes value1 = builder.build();
|
||||
|
||||
assertEquals(100, value1.getOptionalSfixed64());
|
||||
assertEquals(100, value1.getRepeatedInt32(0));
|
||||
assertEquals(UnittestImport.ImportEnum.IMPORT_BAR,
|
||||
value1.getOptionalImportEnum());
|
||||
assertEquals(UnittestImport.ImportEnum.IMPORT_BAR,
|
||||
value1.getRepeatedImportEnum(0));
|
||||
assertEquals(1, value1.getOptionalForeignMessage().getC());
|
||||
assertEquals(1, value1.getRepeatedForeignMessage(0).getC());
|
||||
|
||||
// Make sure that builder didn't update previously created values
|
||||
builder.setOptionalSfixed64(200);
|
||||
builder.setRepeatedInt32(0, 200);
|
||||
builder.setOptionalImportEnum(UnittestImport.ImportEnum.IMPORT_FOO);
|
||||
builder.setRepeatedImportEnum(0, UnittestImport.ImportEnum.IMPORT_FOO);
|
||||
builder.setOptionalForeignMessage(ForeignMessage.newBuilder().setC(2));
|
||||
builder.setRepeatedForeignMessage(0, ForeignMessage.newBuilder().setC(2));
|
||||
|
||||
TestAllTypes value2 = builder.build();
|
||||
|
||||
// Make sure value1 didn't change.
|
||||
assertEquals(100, value1.getOptionalSfixed64());
|
||||
assertEquals(100, value1.getRepeatedInt32(0));
|
||||
assertEquals(UnittestImport.ImportEnum.IMPORT_BAR,
|
||||
value1.getOptionalImportEnum());
|
||||
assertEquals(UnittestImport.ImportEnum.IMPORT_BAR,
|
||||
value1.getRepeatedImportEnum(0));
|
||||
assertEquals(1, value1.getOptionalForeignMessage().getC());
|
||||
assertEquals(1, value1.getRepeatedForeignMessage(0).getC());
|
||||
|
||||
// Make sure value2 is correct
|
||||
assertEquals(200, value2.getOptionalSfixed64());
|
||||
assertEquals(200, value2.getRepeatedInt32(0));
|
||||
assertEquals(UnittestImport.ImportEnum.IMPORT_FOO,
|
||||
value2.getOptionalImportEnum());
|
||||
assertEquals(UnittestImport.ImportEnum.IMPORT_FOO,
|
||||
value2.getRepeatedImportEnum(0));
|
||||
assertEquals(2, value2.getOptionalForeignMessage().getC());
|
||||
assertEquals(2, value2.getRepeatedForeignMessage(0).getC());
|
||||
}
|
||||
|
||||
public void testClearAfterBuildError() throws Exception {
|
||||
public void testProtosShareRepeatedArraysIfDidntChange() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
builder.build();
|
||||
try {
|
||||
builder.clear();
|
||||
fail("Should have thrown exception.");
|
||||
} catch (IllegalStateException e) {
|
||||
// Success.
|
||||
builder.addRepeatedInt32(100);
|
||||
builder.addRepeatedImportEnum(UnittestImport.ImportEnum.IMPORT_BAR);
|
||||
builder.addRepeatedForeignMessage(ForeignMessage.getDefaultInstance());
|
||||
|
||||
TestAllTypes value1 = builder.build();
|
||||
TestAllTypes value2 = value1.toBuilder().build();
|
||||
|
||||
assertSame(value1.getRepeatedInt32List(), value2.getRepeatedInt32List());
|
||||
assertSame(value1.getRepeatedImportEnumList(),
|
||||
value2.getRepeatedImportEnumList());
|
||||
assertSame(value1.getRepeatedForeignMessageList(),
|
||||
value2.getRepeatedForeignMessageList());
|
||||
}
|
||||
|
||||
public void testRepeatedArraysAreImmutable() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
builder.addRepeatedInt32(100);
|
||||
builder.addRepeatedImportEnum(UnittestImport.ImportEnum.IMPORT_BAR);
|
||||
builder.addRepeatedForeignMessage(ForeignMessage.getDefaultInstance());
|
||||
assertIsUnmodifiable(builder.getRepeatedInt32List());
|
||||
assertIsUnmodifiable(builder.getRepeatedImportEnumList());
|
||||
assertIsUnmodifiable(builder.getRepeatedForeignMessageList());
|
||||
assertIsUnmodifiable(builder.getRepeatedFloatList());
|
||||
|
||||
|
||||
TestAllTypes value = builder.build();
|
||||
assertIsUnmodifiable(value.getRepeatedInt32List());
|
||||
assertIsUnmodifiable(value.getRepeatedImportEnumList());
|
||||
assertIsUnmodifiable(value.getRepeatedForeignMessageList());
|
||||
assertIsUnmodifiable(value.getRepeatedFloatList());
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -316,9 +412,19 @@ public class GeneratedMessageTest extends TestCase {
|
||||
assertTrue(Float.isNaN(message.getNanFloat()));
|
||||
}
|
||||
|
||||
public void testClear() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TestUtil.assertClear(builder);
|
||||
TestUtil.setAllFields(builder);
|
||||
builder.clear();
|
||||
TestUtil.assertClear(builder);
|
||||
}
|
||||
|
||||
public void testReflectionGetters() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TestUtil.setAllFields(builder);
|
||||
reflectionTester.assertAllFieldsSetViaReflection(builder);
|
||||
|
||||
TestAllTypes message = builder.build();
|
||||
reflectionTester.assertAllFieldsSetViaReflection(message);
|
||||
}
|
||||
@ -326,6 +432,8 @@ public class GeneratedMessageTest extends TestCase {
|
||||
public void testReflectionSetters() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
reflectionTester.setAllFieldsViaReflection(builder);
|
||||
TestUtil.assertAllFieldsSet(builder);
|
||||
|
||||
TestAllTypes message = builder.build();
|
||||
TestUtil.assertAllFieldsSet(message);
|
||||
}
|
||||
@ -339,6 +447,8 @@ public class GeneratedMessageTest extends TestCase {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
reflectionTester.setAllFieldsViaReflection(builder);
|
||||
reflectionTester.modifyRepeatedFieldsViaReflection(builder);
|
||||
TestUtil.assertRepeatedFieldsModified(builder);
|
||||
|
||||
TestAllTypes message = builder.build();
|
||||
TestUtil.assertRepeatedFieldsModified(message);
|
||||
}
|
||||
@ -391,7 +501,7 @@ public class GeneratedMessageTest extends TestCase {
|
||||
new TestUtil.ReflectionTester(TestAllExtensions.getDescriptor(),
|
||||
TestUtil.getExtensionRegistry());
|
||||
|
||||
public void testExtensionAccessors() throws Exception {
|
||||
public void testExtensionMessageOrBuilder() throws Exception {
|
||||
TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
|
||||
TestUtil.setAllExtensions(builder);
|
||||
TestAllExtensions message = builder.build();
|
||||
@ -414,6 +524,8 @@ public class GeneratedMessageTest extends TestCase {
|
||||
public void testExtensionReflectionGetters() throws Exception {
|
||||
TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
|
||||
TestUtil.setAllExtensions(builder);
|
||||
extensionsReflectionTester.assertAllFieldsSetViaReflection(builder);
|
||||
|
||||
TestAllExtensions message = builder.build();
|
||||
extensionsReflectionTester.assertAllFieldsSetViaReflection(message);
|
||||
}
|
||||
@ -421,6 +533,8 @@ public class GeneratedMessageTest extends TestCase {
|
||||
public void testExtensionReflectionSetters() throws Exception {
|
||||
TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
|
||||
extensionsReflectionTester.setAllFieldsViaReflection(builder);
|
||||
TestUtil.assertAllExtensionsSet(builder);
|
||||
|
||||
TestAllExtensions message = builder.build();
|
||||
TestUtil.assertAllExtensionsSet(message);
|
||||
}
|
||||
@ -434,6 +548,8 @@ public class GeneratedMessageTest extends TestCase {
|
||||
TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
|
||||
extensionsReflectionTester.setAllFieldsViaReflection(builder);
|
||||
extensionsReflectionTester.modifyRepeatedFieldsViaReflection(builder);
|
||||
TestUtil.assertRepeatedExtensionsModified(builder);
|
||||
|
||||
TestAllExtensions message = builder.build();
|
||||
TestUtil.assertRepeatedExtensionsModified(message);
|
||||
}
|
||||
@ -491,9 +607,11 @@ public class GeneratedMessageTest extends TestCase {
|
||||
// lite fields directly since they are implemented exactly the same as
|
||||
// regular fields.
|
||||
|
||||
public void testLiteExtensionAccessors() throws Exception {
|
||||
public void testLiteExtensionMessageOrBuilder() throws Exception {
|
||||
TestAllExtensionsLite.Builder builder = TestAllExtensionsLite.newBuilder();
|
||||
TestUtil.setAllExtensions(builder);
|
||||
TestUtil.assertAllExtensionsSet(builder);
|
||||
|
||||
TestAllExtensionsLite message = builder.build();
|
||||
TestUtil.assertAllExtensionsSet(message);
|
||||
}
|
||||
@ -502,6 +620,8 @@ public class GeneratedMessageTest extends TestCase {
|
||||
TestAllExtensionsLite.Builder builder = TestAllExtensionsLite.newBuilder();
|
||||
TestUtil.setAllExtensions(builder);
|
||||
TestUtil.modifyRepeatedExtensions(builder);
|
||||
TestUtil.assertRepeatedExtensionsModified(builder);
|
||||
|
||||
TestAllExtensionsLite message = builder.build();
|
||||
TestUtil.assertRepeatedExtensionsModified(message);
|
||||
}
|
||||
@ -609,6 +729,7 @@ public class GeneratedMessageTest extends TestCase {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TestUtil.setAllFields(builder);
|
||||
TestAllTypes message = builder.build();
|
||||
TestUtil.assertAllFieldsSet(message);
|
||||
TestUtil.assertAllFieldsSet(message.toBuilder().build());
|
||||
}
|
||||
|
||||
@ -646,4 +767,207 @@ public class GeneratedMessageTest extends TestCase {
|
||||
assertTrue(message.getA() != null);
|
||||
assertTrue(message.getA() == message);
|
||||
}
|
||||
|
||||
public void testSerialize() throws Exception {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TestUtil.setAllFields(builder);
|
||||
TestAllTypes expected = builder.build();
|
||||
ObjectOutputStream out = new ObjectOutputStream(baos);
|
||||
out.writeObject(expected);
|
||||
out.close();
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
|
||||
ObjectInputStream in = new ObjectInputStream(bais);
|
||||
TestAllTypes actual = (TestAllTypes) in.readObject();
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
public void testSerializePartial() throws Exception {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TestAllTypes expected = builder.buildPartial();
|
||||
ObjectOutputStream out = new ObjectOutputStream(baos);
|
||||
out.writeObject(expected);
|
||||
out.close();
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
|
||||
ObjectInputStream in = new ObjectInputStream(bais);
|
||||
TestAllTypes actual = (TestAllTypes) in.readObject();
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
public void testEnumValues() {
|
||||
assertEquals(
|
||||
TestAllTypes.NestedEnum.BAR.getNumber(),
|
||||
TestAllTypes.NestedEnum.BAR_VALUE);
|
||||
assertEquals(
|
||||
TestAllTypes.NestedEnum.BAZ.getNumber(),
|
||||
TestAllTypes.NestedEnum.BAZ_VALUE);
|
||||
assertEquals(
|
||||
TestAllTypes.NestedEnum.FOO.getNumber(),
|
||||
TestAllTypes.NestedEnum.FOO_VALUE);
|
||||
}
|
||||
|
||||
public void testNonNestedExtensionInitialization() {
|
||||
assertTrue(NonNestedExtension.nonNestedExtension
|
||||
.getMessageDefaultInstance() instanceof MyNonNestedExtension);
|
||||
assertEquals("nonNestedExtension",
|
||||
NonNestedExtension.nonNestedExtension.getDescriptor().getName());
|
||||
}
|
||||
|
||||
public void testNestedExtensionInitialization() {
|
||||
assertTrue(MyNestedExtension.recursiveExtension.getMessageDefaultInstance()
|
||||
instanceof MessageToBeExtended);
|
||||
assertEquals("recursiveExtension",
|
||||
MyNestedExtension.recursiveExtension.getDescriptor().getName());
|
||||
}
|
||||
|
||||
public void testNonNestedExtensionLiteInitialization() {
|
||||
assertTrue(NonNestedExtensionLite.nonNestedExtensionLite
|
||||
.getMessageDefaultInstance() instanceof MyNonNestedExtensionLite);
|
||||
}
|
||||
|
||||
public void testNestedExtensionLiteInitialization() {
|
||||
assertTrue(MyNestedExtensionLite.recursiveExtensionLite
|
||||
.getMessageDefaultInstance() instanceof MessageLiteToBeExtended);
|
||||
}
|
||||
|
||||
public void testInvalidations() throws Exception {
|
||||
GeneratedMessage.enableAlwaysUseFieldBuildersForTesting();
|
||||
TestAllTypes.NestedMessage nestedMessage1 =
|
||||
TestAllTypes.NestedMessage.newBuilder().build();
|
||||
TestAllTypes.NestedMessage nestedMessage2 =
|
||||
TestAllTypes.NestedMessage.newBuilder().build();
|
||||
|
||||
// Set all three flavors (enum, primitive, message and singular/repeated)
|
||||
// and verify no invalidations fired
|
||||
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
|
||||
|
||||
TestAllTypes.Builder builder = (TestAllTypes.Builder)
|
||||
((GeneratedMessage) TestAllTypes.getDefaultInstance()).
|
||||
newBuilderForType(mockParent);
|
||||
builder.setOptionalInt32(1);
|
||||
builder.setOptionalNestedEnum(TestAllTypes.NestedEnum.BAR);
|
||||
builder.setOptionalNestedMessage(nestedMessage1);
|
||||
builder.addRepeatedInt32(1);
|
||||
builder.addRepeatedNestedEnum(TestAllTypes.NestedEnum.BAR);
|
||||
builder.addRepeatedNestedMessage(nestedMessage1);
|
||||
assertEquals(0, mockParent.getInvalidationCount());
|
||||
|
||||
// Now tell it we want changes and make sure it's only fired once
|
||||
// And do this for each flavor
|
||||
|
||||
// primitive single
|
||||
builder.buildPartial();
|
||||
builder.setOptionalInt32(2);
|
||||
builder.setOptionalInt32(3);
|
||||
assertEquals(1, mockParent.getInvalidationCount());
|
||||
|
||||
// enum single
|
||||
builder.buildPartial();
|
||||
builder.setOptionalNestedEnum(TestAllTypes.NestedEnum.BAZ);
|
||||
builder.setOptionalNestedEnum(TestAllTypes.NestedEnum.BAR);
|
||||
assertEquals(2, mockParent.getInvalidationCount());
|
||||
|
||||
// message single
|
||||
builder.buildPartial();
|
||||
builder.setOptionalNestedMessage(nestedMessage2);
|
||||
builder.setOptionalNestedMessage(nestedMessage1);
|
||||
assertEquals(3, mockParent.getInvalidationCount());
|
||||
|
||||
// primitive repated
|
||||
builder.buildPartial();
|
||||
builder.addRepeatedInt32(2);
|
||||
builder.addRepeatedInt32(3);
|
||||
assertEquals(4, mockParent.getInvalidationCount());
|
||||
|
||||
// enum repeated
|
||||
builder.buildPartial();
|
||||
builder.addRepeatedNestedEnum(TestAllTypes.NestedEnum.BAZ);
|
||||
builder.addRepeatedNestedEnum(TestAllTypes.NestedEnum.BAZ);
|
||||
assertEquals(5, mockParent.getInvalidationCount());
|
||||
|
||||
// message repeated
|
||||
builder.buildPartial();
|
||||
builder.addRepeatedNestedMessage(nestedMessage2);
|
||||
builder.addRepeatedNestedMessage(nestedMessage1);
|
||||
assertEquals(6, mockParent.getInvalidationCount());
|
||||
|
||||
}
|
||||
|
||||
public void testInvalidations_Extensions() throws Exception {
|
||||
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
|
||||
|
||||
TestAllExtensions.Builder builder = (TestAllExtensions.Builder)
|
||||
((GeneratedMessage) TestAllExtensions.getDefaultInstance()).
|
||||
newBuilderForType(mockParent);
|
||||
|
||||
builder.addExtension(UnittestProto.repeatedInt32Extension, 1);
|
||||
builder.setExtension(UnittestProto.repeatedInt32Extension, 0, 2);
|
||||
builder.clearExtension(UnittestProto.repeatedInt32Extension);
|
||||
assertEquals(0, mockParent.getInvalidationCount());
|
||||
|
||||
// Now tell it we want changes and make sure it's only fired once
|
||||
builder.buildPartial();
|
||||
builder.addExtension(UnittestProto.repeatedInt32Extension, 2);
|
||||
builder.addExtension(UnittestProto.repeatedInt32Extension, 3);
|
||||
assertEquals(1, mockParent.getInvalidationCount());
|
||||
|
||||
builder.buildPartial();
|
||||
builder.setExtension(UnittestProto.repeatedInt32Extension, 0, 4);
|
||||
builder.setExtension(UnittestProto.repeatedInt32Extension, 1, 5);
|
||||
assertEquals(2, mockParent.getInvalidationCount());
|
||||
|
||||
builder.buildPartial();
|
||||
builder.clearExtension(UnittestProto.repeatedInt32Extension);
|
||||
builder.clearExtension(UnittestProto.repeatedInt32Extension);
|
||||
assertEquals(3, mockParent.getInvalidationCount());
|
||||
}
|
||||
|
||||
public void testBaseMessageOrBuilder() {
|
||||
// Mostly just makes sure the base interface exists and has some methods.
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TestAllTypes message = builder.buildPartial();
|
||||
TestAllTypesOrBuilder builderAsInterface = (TestAllTypesOrBuilder) builder;
|
||||
TestAllTypesOrBuilder messageAsInterface = (TestAllTypesOrBuilder) message;
|
||||
|
||||
assertEquals(
|
||||
messageAsInterface.getDefaultBool(),
|
||||
messageAsInterface.getDefaultBool());
|
||||
assertEquals(
|
||||
messageAsInterface.getOptionalDouble(),
|
||||
messageAsInterface.getOptionalDouble());
|
||||
}
|
||||
|
||||
public void testMessageOrBuilderGetters() {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
|
||||
// single fields
|
||||
assertSame(ForeignMessage.getDefaultInstance(),
|
||||
builder.getOptionalForeignMessageOrBuilder());
|
||||
ForeignMessage.Builder subBuilder =
|
||||
builder.getOptionalForeignMessageBuilder();
|
||||
assertSame(subBuilder, builder.getOptionalForeignMessageOrBuilder());
|
||||
|
||||
// repeated fields
|
||||
ForeignMessage m0 = ForeignMessage.newBuilder().buildPartial();
|
||||
ForeignMessage m1 = ForeignMessage.newBuilder().buildPartial();
|
||||
ForeignMessage m2 = ForeignMessage.newBuilder().buildPartial();
|
||||
builder.addRepeatedForeignMessage(m0);
|
||||
builder.addRepeatedForeignMessage(m1);
|
||||
builder.addRepeatedForeignMessage(m2);
|
||||
assertSame(m0, builder.getRepeatedForeignMessageOrBuilder(0));
|
||||
assertSame(m1, builder.getRepeatedForeignMessageOrBuilder(1));
|
||||
assertSame(m2, builder.getRepeatedForeignMessageOrBuilder(2));
|
||||
ForeignMessage.Builder b0 = builder.getRepeatedForeignMessageBuilder(0);
|
||||
ForeignMessage.Builder b1 = builder.getRepeatedForeignMessageBuilder(1);
|
||||
assertSame(b0, builder.getRepeatedForeignMessageOrBuilder(0));
|
||||
assertSame(b1, builder.getRepeatedForeignMessageOrBuilder(1));
|
||||
assertSame(m2, builder.getRepeatedForeignMessageOrBuilder(2));
|
||||
|
||||
List<? extends ForeignMessageOrBuilder> messageOrBuilderList =
|
||||
builder.getRepeatedForeignMessageOrBuilderList();
|
||||
assertSame(b0, messageOrBuilderList.get(0));
|
||||
assertSame(b1, messageOrBuilderList.get(1));
|
||||
assertSame(m2, messageOrBuilderList.get(2));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,118 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests for {@link LazyStringArrayList}.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class LazyStringArrayListTest extends TestCase {
|
||||
|
||||
private static String STRING_A = "A";
|
||||
private static String STRING_B = "B";
|
||||
private static String STRING_C = "C";
|
||||
|
||||
private static ByteString BYTE_STRING_A = ByteString.copyFromUtf8("A");
|
||||
private static ByteString BYTE_STRING_B = ByteString.copyFromUtf8("B");
|
||||
private static ByteString BYTE_STRING_C = ByteString.copyFromUtf8("C");
|
||||
|
||||
public void testJustStrings() {
|
||||
LazyStringArrayList list = new LazyStringArrayList();
|
||||
list.add(STRING_A);
|
||||
list.add(STRING_B);
|
||||
list.add(STRING_C);
|
||||
|
||||
assertEquals(3, list.size());
|
||||
assertSame(STRING_A, list.get(0));
|
||||
assertSame(STRING_B, list.get(1));
|
||||
assertSame(STRING_C, list.get(2));
|
||||
|
||||
list.set(1, STRING_C);
|
||||
assertSame(STRING_C, list.get(1));
|
||||
|
||||
list.remove(1);
|
||||
assertSame(STRING_A, list.get(0));
|
||||
assertSame(STRING_C, list.get(1));
|
||||
}
|
||||
|
||||
public void testJustByteString() {
|
||||
LazyStringArrayList list = new LazyStringArrayList();
|
||||
list.add(BYTE_STRING_A);
|
||||
list.add(BYTE_STRING_B);
|
||||
list.add(BYTE_STRING_C);
|
||||
|
||||
assertEquals(3, list.size());
|
||||
assertSame(BYTE_STRING_A, list.getByteString(0));
|
||||
assertSame(BYTE_STRING_B, list.getByteString(1));
|
||||
assertSame(BYTE_STRING_C, list.getByteString(2));
|
||||
|
||||
list.remove(1);
|
||||
assertSame(BYTE_STRING_A, list.getByteString(0));
|
||||
assertSame(BYTE_STRING_C, list.getByteString(1));
|
||||
}
|
||||
|
||||
public void testConversionBackAndForth() {
|
||||
LazyStringArrayList list = new LazyStringArrayList();
|
||||
list.add(STRING_A);
|
||||
list.add(BYTE_STRING_B);
|
||||
list.add(BYTE_STRING_C);
|
||||
|
||||
// String a should be the same because it was originally a string
|
||||
assertSame(STRING_A, list.get(0));
|
||||
|
||||
// String b and c should be different because the string has to be computed
|
||||
// from the ByteString
|
||||
String bPrime = list.get(1);
|
||||
assertNotSame(STRING_B, bPrime);
|
||||
assertEquals(STRING_B, bPrime);
|
||||
String cPrime = list.get(2);
|
||||
assertNotSame(STRING_C, cPrime);
|
||||
assertEquals(STRING_C, cPrime);
|
||||
|
||||
// String c and c should stay the same once cached.
|
||||
assertSame(bPrime, list.get(1));
|
||||
assertSame(cPrime, list.get(2));
|
||||
|
||||
// ByteString needs to be computed from string for both a and b
|
||||
ByteString aPrimeByteString = list.getByteString(0);
|
||||
assertEquals(BYTE_STRING_A, aPrimeByteString);
|
||||
ByteString bPrimeByteString = list.getByteString(1);
|
||||
assertNotSame(BYTE_STRING_B, bPrimeByteString);
|
||||
assertEquals(BYTE_STRING_B, list.getByteString(1));
|
||||
|
||||
// Once cached, ByteString should stay cached.
|
||||
assertSame(aPrimeByteString, list.getByteString(0));
|
||||
assertSame(bPrimeByteString, list.getByteString(1));
|
||||
}
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
|
||||
import protobuf_unittest.UnittestProto;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Tests to make sure the lazy conversion of UTF8-encoded byte arrays to
|
||||
* strings works correctly.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class LazyStringEndToEndTest extends TestCase {
|
||||
|
||||
private static ByteString TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8 =
|
||||
ByteString.copyFrom(new byte[] {
|
||||
114, 4, -1, 0, -1, 0, -30, 2, 4, -1,
|
||||
0, -1, 0, -30, 2, 4, -1, 0, -1, 0, });
|
||||
|
||||
/**
|
||||
* Tests that an invalid UTF8 string will roundtrip through a parse
|
||||
* and serialization.
|
||||
*/
|
||||
public void testParseAndSerialize() throws InvalidProtocolBufferException {
|
||||
UnittestProto.TestAllTypes tV2 = UnittestProto.TestAllTypes.parseFrom(
|
||||
TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8);
|
||||
ByteString bytes = tV2.toByteString();
|
||||
assertEquals(TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8, bytes);
|
||||
|
||||
tV2.getOptionalString();
|
||||
bytes = tV2.toByteString();
|
||||
assertEquals(TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8, bytes);
|
||||
}
|
||||
|
||||
public void testParseAndWrite() throws IOException {
|
||||
UnittestProto.TestAllTypes tV2 = UnittestProto.TestAllTypes.parseFrom(
|
||||
TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8);
|
||||
byte[] sink = new byte[TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8.size()];
|
||||
CodedOutputStream outputStream = CodedOutputStream.newInstance(sink);
|
||||
tV2.writeTo(outputStream);
|
||||
outputStream.flush();
|
||||
assertEquals(
|
||||
TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8,
|
||||
ByteString.copyFrom(sink));
|
||||
}
|
||||
|
||||
public void testCaching() {
|
||||
String a = "a";
|
||||
String b = "b";
|
||||
String c = "c";
|
||||
UnittestProto.TestAllTypes proto = UnittestProto.TestAllTypes.newBuilder()
|
||||
.setOptionalString(a)
|
||||
.addRepeatedString(b)
|
||||
.addRepeatedString(c)
|
||||
.build();
|
||||
|
||||
// String should be the one we passed it.
|
||||
assertSame(a, proto.getOptionalString());
|
||||
assertSame(b, proto.getRepeatedString(0));
|
||||
assertSame(c, proto.getRepeatedString(1));
|
||||
|
||||
|
||||
// There's no way to directly observe that the ByteString is cached
|
||||
// correctly on serialization, but we can observe that it had to recompute
|
||||
// the string after serialization.
|
||||
proto.toByteString();
|
||||
String aPrime = proto.getOptionalString();
|
||||
assertNotSame(a, aPrime);
|
||||
assertEquals(a, aPrime);
|
||||
String bPrime = proto.getRepeatedString(0);
|
||||
assertNotSame(b, bPrime);
|
||||
assertEquals(b, bPrime);
|
||||
String cPrime = proto.getRepeatedString(1);
|
||||
assertNotSame(c, cPrime);
|
||||
assertEquals(c, cPrime);
|
||||
|
||||
// And now the string should stay cached.
|
||||
assertSame(aPrime, proto.getOptionalString());
|
||||
assertSame(bPrime, proto.getRepeatedString(0));
|
||||
assertSame(cPrime, proto.getRepeatedString(1));
|
||||
}
|
||||
}
|
@ -37,6 +37,11 @@ import com.google.protobuf.UnittestLite.TestNestedExtensionLite;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
|
||||
/**
|
||||
* Test lite runtime.
|
||||
*
|
||||
@ -113,4 +118,28 @@ public class LiteTest extends TestCase {
|
||||
assertEquals(7, message2.getExtension(
|
||||
UnittestLite.optionalNestedMessageExtensionLite).getBb());
|
||||
}
|
||||
|
||||
public void testSerialize() throws Exception {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
TestAllTypesLite expected =
|
||||
TestAllTypesLite.newBuilder()
|
||||
.setOptionalInt32(123)
|
||||
.addRepeatedString("hello")
|
||||
.setOptionalNestedMessage(
|
||||
TestAllTypesLite.NestedMessage.newBuilder().setBb(7))
|
||||
.build();
|
||||
ObjectOutputStream out = new ObjectOutputStream(baos);
|
||||
out.writeObject(expected);
|
||||
out.close();
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
|
||||
ObjectInputStream in = new ObjectInputStream(bais);
|
||||
TestAllTypesLite actual = (TestAllTypesLite) in.readObject();
|
||||
assertEquals(expected.getOptionalInt32(), actual.getOptionalInt32());
|
||||
assertEquals(expected.getRepeatedStringCount(),
|
||||
actual.getRepeatedStringCount());
|
||||
assertEquals(expected.getRepeatedString(0),
|
||||
actual.getRepeatedString(0));
|
||||
assertEquals(expected.getOptionalNestedMessage().getBb(),
|
||||
actual.getOptionalNestedMessage().getBb());
|
||||
}
|
||||
}
|
||||
|
185
java/src/test/java/com/google/protobuf/NestedBuildersTest.java
Normal file
185
java/src/test/java/com/google/protobuf/NestedBuildersTest.java
Normal file
@ -0,0 +1,185 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import protobuf_unittest.Vehicle;
|
||||
import protobuf_unittest.Wheel;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Test cases that exercise end-to-end use cases involving
|
||||
* {@link SingleFieldBuilder} and {@link RepeatedFieldBuilder}.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class NestedBuildersTest extends TestCase {
|
||||
|
||||
public void testMessagesAndBuilders() {
|
||||
Vehicle.Builder vehicleBuilder = Vehicle.newBuilder();
|
||||
vehicleBuilder.addWheelBuilder()
|
||||
.setRadius(4)
|
||||
.setWidth(1);
|
||||
vehicleBuilder.addWheelBuilder()
|
||||
.setRadius(4)
|
||||
.setWidth(2);
|
||||
vehicleBuilder.addWheelBuilder()
|
||||
.setRadius(4)
|
||||
.setWidth(3);
|
||||
vehicleBuilder.addWheelBuilder()
|
||||
.setRadius(4)
|
||||
.setWidth(4);
|
||||
vehicleBuilder.getEngineBuilder()
|
||||
.setLiters(10);
|
||||
|
||||
Vehicle vehicle = vehicleBuilder.build();
|
||||
assertEquals(4, vehicle.getWheelCount());
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Wheel wheel = vehicle.getWheel(i);
|
||||
assertEquals(4, wheel.getRadius());
|
||||
assertEquals(i + 1, wheel.getWidth());
|
||||
}
|
||||
assertEquals(10, vehicle.getEngine().getLiters());
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
vehicleBuilder.getWheelBuilder(i)
|
||||
.setRadius(5)
|
||||
.setWidth(i + 10);
|
||||
}
|
||||
vehicleBuilder.getEngineBuilder().setLiters(20);
|
||||
|
||||
vehicle = vehicleBuilder.build();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Wheel wheel = vehicle.getWheel(i);
|
||||
assertEquals(5, wheel.getRadius());
|
||||
assertEquals(i + 10, wheel.getWidth());
|
||||
}
|
||||
assertEquals(20, vehicle.getEngine().getLiters());
|
||||
assertTrue(vehicle.hasEngine());
|
||||
}
|
||||
|
||||
public void testMessagesAreCached() {
|
||||
Vehicle.Builder vehicleBuilder = Vehicle.newBuilder();
|
||||
vehicleBuilder.addWheelBuilder()
|
||||
.setRadius(1)
|
||||
.setWidth(2);
|
||||
vehicleBuilder.addWheelBuilder()
|
||||
.setRadius(3)
|
||||
.setWidth(4);
|
||||
vehicleBuilder.addWheelBuilder()
|
||||
.setRadius(5)
|
||||
.setWidth(6);
|
||||
vehicleBuilder.addWheelBuilder()
|
||||
.setRadius(7)
|
||||
.setWidth(8);
|
||||
|
||||
// Make sure messages are cached.
|
||||
List<Wheel> wheels = new ArrayList<Wheel>(vehicleBuilder.getWheelList());
|
||||
for (int i = 0; i < wheels.size(); i++) {
|
||||
assertSame(wheels.get(i), vehicleBuilder.getWheel(i));
|
||||
}
|
||||
|
||||
// Now get builders and check they didn't change.
|
||||
for (int i = 0; i < wheels.size(); i++) {
|
||||
vehicleBuilder.getWheel(i);
|
||||
}
|
||||
for (int i = 0; i < wheels.size(); i++) {
|
||||
assertSame(wheels.get(i), vehicleBuilder.getWheel(i));
|
||||
}
|
||||
|
||||
// Change just one
|
||||
vehicleBuilder.getWheelBuilder(3)
|
||||
.setRadius(20).setWidth(20);
|
||||
|
||||
// Now get wheels and check that only that one changed
|
||||
for (int i = 0; i < wheels.size(); i++) {
|
||||
if (i < 3) {
|
||||
assertSame(wheels.get(i), vehicleBuilder.getWheel(i));
|
||||
} else {
|
||||
assertNotSame(wheels.get(i), vehicleBuilder.getWheel(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testRemove_WithNestedBuilders() {
|
||||
Vehicle.Builder vehicleBuilder = Vehicle.newBuilder();
|
||||
vehicleBuilder.addWheelBuilder()
|
||||
.setRadius(1)
|
||||
.setWidth(1);
|
||||
vehicleBuilder.addWheelBuilder()
|
||||
.setRadius(2)
|
||||
.setWidth(2);
|
||||
vehicleBuilder.removeWheel(0);
|
||||
|
||||
assertEquals(1, vehicleBuilder.getWheelCount());
|
||||
assertEquals(2, vehicleBuilder.getWheel(0).getRadius());
|
||||
}
|
||||
|
||||
public void testRemove_WithNestedMessages() {
|
||||
Vehicle.Builder vehicleBuilder = Vehicle.newBuilder();
|
||||
vehicleBuilder.addWheel(Wheel.newBuilder()
|
||||
.setRadius(1)
|
||||
.setWidth(1));
|
||||
vehicleBuilder.addWheel(Wheel.newBuilder()
|
||||
.setRadius(2)
|
||||
.setWidth(2));
|
||||
vehicleBuilder.removeWheel(0);
|
||||
|
||||
assertEquals(1, vehicleBuilder.getWheelCount());
|
||||
assertEquals(2, vehicleBuilder.getWheel(0).getRadius());
|
||||
}
|
||||
|
||||
public void testMerge() {
|
||||
Vehicle vehicle1 = Vehicle.newBuilder()
|
||||
.addWheel(Wheel.newBuilder().setRadius(1).build())
|
||||
.addWheel(Wheel.newBuilder().setRadius(2).build())
|
||||
.build();
|
||||
|
||||
Vehicle vehicle2 = Vehicle.newBuilder()
|
||||
.mergeFrom(vehicle1)
|
||||
.build();
|
||||
// List should be the same -- no allocation
|
||||
assertSame(vehicle1.getWheelList(), vehicle2.getWheelList());
|
||||
|
||||
Vehicle vehicle3 = vehicle1.toBuilder().build();
|
||||
assertSame(vehicle1.getWheelList(), vehicle3.getWheelList());
|
||||
}
|
||||
|
||||
public void testGettingBuilderMarksFieldAsHaving() {
|
||||
Vehicle.Builder vehicleBuilder = Vehicle.newBuilder();
|
||||
vehicleBuilder.getEngineBuilder();
|
||||
Vehicle vehicle = vehicleBuilder.buildPartial();
|
||||
assertTrue(vehicle.hasEngine());
|
||||
}
|
||||
}
|
@ -0,0 +1,190 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tests for {@link RepeatedFieldBuilder}. This tests basic functionality.
|
||||
* More extensive testing is provided via other tests that exercise the
|
||||
* builder.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class RepeatedFieldBuilderTest extends TestCase {
|
||||
|
||||
public void testBasicUse() {
|
||||
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
|
||||
RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder> builder = newRepeatedFieldBuilder(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();
|
||||
RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder> builder = newRepeatedFieldBuilder(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();
|
||||
RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder> builder = newRepeatedFieldBuilder(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();
|
||||
RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder> builder = newRepeatedFieldBuilder(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 RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder>
|
||||
newRepeatedFieldBuilder(GeneratedMessage.BuilderParent parent) {
|
||||
return new RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder>(Collections.<TestAllTypes>emptyList(), false,
|
||||
parent, false);
|
||||
}
|
||||
}
|
@ -244,6 +244,14 @@ public class ServiceTest extends TestCase {
|
||||
// make assumptions, so I'm just going to accept any character as the
|
||||
// separator.
|
||||
assertTrue(fullName.startsWith(outerName));
|
||||
|
||||
if (!Service.class.isAssignableFrom(innerClass) &&
|
||||
!Message.class.isAssignableFrom(innerClass) &&
|
||||
!ProtocolMessageEnum.class.isAssignableFrom(innerClass)) {
|
||||
// Ignore any classes not generated by the base code generator.
|
||||
continue;
|
||||
}
|
||||
|
||||
innerClassNames.add(fullName.substring(outerName.length() + 1));
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,155 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests for {@link SingleFieldBuilder}. This tests basic functionality.
|
||||
* More extensive testing is provided via other tests that exercise the
|
||||
* builder.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class SingleFieldBuilderTest extends TestCase {
|
||||
|
||||
public void testBasicUseAndInvalidations() {
|
||||
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
|
||||
SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder> builder =
|
||||
new SingleFieldBuilder<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();
|
||||
SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder> builder =
|
||||
new SingleFieldBuilder<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();
|
||||
SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder> builder =
|
||||
new SingleFieldBuilder<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();
|
||||
SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder> builder =
|
||||
new SingleFieldBuilder<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());
|
||||
}
|
||||
}
|
378
java/src/test/java/com/google/protobuf/SmallSortedMapTest.java
Normal file
378
java/src/test/java/com/google/protobuf/SmallSortedMapTest.java
Normal file
@ -0,0 +1,378 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* @author darick@google.com Darick Tong
|
||||
*/
|
||||
public class SmallSortedMapTest extends TestCase {
|
||||
|
||||
public void testPutAndGetArrayEntriesOnly() {
|
||||
runPutAndGetTest(3);
|
||||
}
|
||||
|
||||
public void testPutAndGetOverflowEntries() {
|
||||
runPutAndGetTest(6);
|
||||
}
|
||||
|
||||
private void runPutAndGetTest(int numElements) {
|
||||
// Test with even and odd arraySize
|
||||
SmallSortedMap<Integer, Integer> map1 =
|
||||
SmallSortedMap.newInstanceForTest(3);
|
||||
SmallSortedMap<Integer, Integer> map2 =
|
||||
SmallSortedMap.newInstanceForTest(4);
|
||||
SmallSortedMap<Integer, Integer> map3 =
|
||||
SmallSortedMap.newInstanceForTest(3);
|
||||
SmallSortedMap<Integer, Integer> map4 =
|
||||
SmallSortedMap.newInstanceForTest(4);
|
||||
|
||||
// Test with puts in ascending order.
|
||||
for (int i = 0; i < numElements; i++) {
|
||||
assertNull(map1.put(i, i + 1));
|
||||
assertNull(map2.put(i, i + 1));
|
||||
}
|
||||
// Test with puts in descending order.
|
||||
for (int i = numElements - 1; i >= 0; i--) {
|
||||
assertNull(map3.put(i, i + 1));
|
||||
assertNull(map4.put(i, i + 1));
|
||||
}
|
||||
|
||||
assertEquals(Math.min(3, numElements), map1.getNumArrayEntries());
|
||||
assertEquals(Math.min(4, numElements), map2.getNumArrayEntries());
|
||||
assertEquals(Math.min(3, numElements), map3.getNumArrayEntries());
|
||||
assertEquals(Math.min(4, numElements), map4.getNumArrayEntries());
|
||||
|
||||
List<SmallSortedMap<Integer, Integer>> allMaps =
|
||||
new ArrayList<SmallSortedMap<Integer, Integer>>();
|
||||
allMaps.add(map1);
|
||||
allMaps.add(map2);
|
||||
allMaps.add(map3);
|
||||
allMaps.add(map4);
|
||||
|
||||
for (SmallSortedMap<Integer, Integer> map : allMaps) {
|
||||
assertEquals(numElements, map.size());
|
||||
for (int i = 0; i < numElements; i++) {
|
||||
assertEquals(new Integer(i + 1), map.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(map1, map2);
|
||||
assertEquals(map2, map3);
|
||||
assertEquals(map3, map4);
|
||||
}
|
||||
|
||||
public void testReplacingPut() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertNull(map.put(i, i + 1));
|
||||
assertNull(map.remove(i + 1));
|
||||
}
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertEquals(new Integer(i + 1), map.put(i, i + 2));
|
||||
}
|
||||
}
|
||||
|
||||
public void testRemove() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertNull(map.put(i, i + 1));
|
||||
assertNull(map.remove(i + 1));
|
||||
}
|
||||
|
||||
assertEquals(3, map.getNumArrayEntries());
|
||||
assertEquals(3, map.getNumOverflowEntries());
|
||||
assertEquals(6, map.size());
|
||||
assertEquals(makeSortedKeySet(0, 1, 2, 3, 4, 5), map.keySet());
|
||||
|
||||
assertEquals(new Integer(2), map.remove(1));
|
||||
assertEquals(3, map.getNumArrayEntries());
|
||||
assertEquals(2, map.getNumOverflowEntries());
|
||||
assertEquals(5, map.size());
|
||||
assertEquals(makeSortedKeySet(0, 2, 3, 4, 5), map.keySet());
|
||||
|
||||
assertEquals(new Integer(5), map.remove(4));
|
||||
assertEquals(3, map.getNumArrayEntries());
|
||||
assertEquals(1, map.getNumOverflowEntries());
|
||||
assertEquals(4, map.size());
|
||||
assertEquals(makeSortedKeySet(0, 2, 3, 5), map.keySet());
|
||||
|
||||
assertEquals(new Integer(4), map.remove(3));
|
||||
assertEquals(3, map.getNumArrayEntries());
|
||||
assertEquals(0, map.getNumOverflowEntries());
|
||||
assertEquals(3, map.size());
|
||||
assertEquals(makeSortedKeySet(0, 2, 5), map.keySet());
|
||||
|
||||
assertNull(map.remove(3));
|
||||
assertEquals(3, map.getNumArrayEntries());
|
||||
assertEquals(0, map.getNumOverflowEntries());
|
||||
assertEquals(3, map.size());
|
||||
|
||||
assertEquals(new Integer(1), map.remove(0));
|
||||
assertEquals(2, map.getNumArrayEntries());
|
||||
assertEquals(0, map.getNumOverflowEntries());
|
||||
assertEquals(2, map.size());
|
||||
}
|
||||
|
||||
public void testClear() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertNull(map.put(i, i + 1));
|
||||
}
|
||||
map.clear();
|
||||
assertEquals(0, map.getNumArrayEntries());
|
||||
assertEquals(0, map.getNumOverflowEntries());
|
||||
assertEquals(0, map.size());
|
||||
}
|
||||
|
||||
public void testGetArrayEntryAndOverflowEntries() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertNull(map.put(i, i + 1));
|
||||
}
|
||||
assertEquals(3, map.getNumArrayEntries());
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Map.Entry<Integer, Integer> entry = map.getArrayEntryAt(i);
|
||||
assertEquals(new Integer(i), entry.getKey());
|
||||
assertEquals(new Integer(i + 1), entry.getValue());
|
||||
}
|
||||
Iterator<Map.Entry<Integer, Integer>> it =
|
||||
map.getOverflowEntries().iterator();
|
||||
for (int i = 3; i < 6; i++) {
|
||||
assertTrue(it.hasNext());
|
||||
Map.Entry<Integer, Integer> entry = it.next();
|
||||
assertEquals(new Integer(i), entry.getKey());
|
||||
assertEquals(new Integer(i + 1), entry.getValue());
|
||||
}
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
public void testEntrySetContains() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertNull(map.put(i, i + 1));
|
||||
}
|
||||
Set<Map.Entry<Integer, Integer>> entrySet = map.entrySet();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertTrue(
|
||||
entrySet.contains(new SimpleEntry<Integer, Integer>(i, i + 1)));
|
||||
assertFalse(
|
||||
entrySet.contains(new SimpleEntry<Integer, Integer>(i, i)));
|
||||
}
|
||||
}
|
||||
|
||||
public void testEntrySetAdd() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
Set<Map.Entry<Integer, Integer>> entrySet = map.entrySet();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
Map.Entry<Integer, Integer> entry =
|
||||
new SimpleEntry<Integer, Integer>(i, i + 1);
|
||||
assertTrue(entrySet.add(entry));
|
||||
assertFalse(entrySet.add(entry));
|
||||
}
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertEquals(new Integer(i + 1), map.get(i));
|
||||
}
|
||||
assertEquals(3, map.getNumArrayEntries());
|
||||
assertEquals(3, map.getNumOverflowEntries());
|
||||
assertEquals(6, map.size());
|
||||
}
|
||||
|
||||
public void testEntrySetRemove() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
Set<Map.Entry<Integer, Integer>> entrySet = map.entrySet();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertNull(map.put(i, i + 1));
|
||||
}
|
||||
for (int i = 0; i < 6; i++) {
|
||||
Map.Entry<Integer, Integer> entry =
|
||||
new SimpleEntry<Integer, Integer>(i, i + 1);
|
||||
assertTrue(entrySet.remove(entry));
|
||||
assertFalse(entrySet.remove(entry));
|
||||
}
|
||||
assertTrue(map.isEmpty());
|
||||
assertEquals(0, map.getNumArrayEntries());
|
||||
assertEquals(0, map.getNumOverflowEntries());
|
||||
assertEquals(0, map.size());
|
||||
}
|
||||
|
||||
public void testEntrySetClear() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertNull(map.put(i, i + 1));
|
||||
}
|
||||
map.entrySet().clear();
|
||||
assertTrue(map.isEmpty());
|
||||
assertEquals(0, map.getNumArrayEntries());
|
||||
assertEquals(0, map.getNumOverflowEntries());
|
||||
assertEquals(0, map.size());
|
||||
}
|
||||
|
||||
public void testEntrySetIteratorNext() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertNull(map.put(i, i + 1));
|
||||
}
|
||||
Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertTrue(it.hasNext());
|
||||
Map.Entry<Integer, Integer> entry = it.next();
|
||||
assertEquals(new Integer(i), entry.getKey());
|
||||
assertEquals(new Integer(i + 1), entry.getValue());
|
||||
}
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
public void testEntrySetIteratorRemove() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertNull(map.put(i, i + 1));
|
||||
}
|
||||
Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertTrue(map.containsKey(i));
|
||||
it.next();
|
||||
it.remove();
|
||||
assertFalse(map.containsKey(i));
|
||||
assertEquals(6 - i - 1, map.size());
|
||||
}
|
||||
}
|
||||
|
||||
public void testMapEntryModification() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertNull(map.put(i, i + 1));
|
||||
}
|
||||
Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
Map.Entry<Integer, Integer> entry = it.next();
|
||||
entry.setValue(i + 23);
|
||||
}
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertEquals(new Integer(i + 23), map.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
public void testMakeImmutable() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertNull(map.put(i, i + 1));
|
||||
}
|
||||
map.makeImmutable();
|
||||
assertEquals(new Integer(1), map.get(0));
|
||||
assertEquals(6, map.size());
|
||||
|
||||
try {
|
||||
map.put(23, 23);
|
||||
fail("Expected UnsupportedOperationException");
|
||||
} catch (UnsupportedOperationException expected) {
|
||||
}
|
||||
|
||||
Map<Integer, Integer> other = new HashMap<Integer, Integer>();
|
||||
other.put(23, 23);
|
||||
try {
|
||||
map.putAll(other);
|
||||
fail("Expected UnsupportedOperationException");
|
||||
} catch (UnsupportedOperationException expected) {
|
||||
}
|
||||
|
||||
try {
|
||||
map.remove(0);
|
||||
fail("Expected UnsupportedOperationException");
|
||||
} catch (UnsupportedOperationException expected) {
|
||||
}
|
||||
|
||||
try {
|
||||
map.clear();
|
||||
fail("Expected UnsupportedOperationException");
|
||||
} catch (UnsupportedOperationException expected) {
|
||||
}
|
||||
|
||||
Set<Map.Entry<Integer, Integer>> entrySet = map.entrySet();
|
||||
try {
|
||||
entrySet.clear();
|
||||
fail("Expected UnsupportedOperationException");
|
||||
} catch (UnsupportedOperationException expected) {
|
||||
}
|
||||
|
||||
Iterator<Map.Entry<Integer, Integer>> it = entrySet.iterator();
|
||||
while (it.hasNext()) {
|
||||
Map.Entry<Integer, Integer> entry = it.next();
|
||||
try {
|
||||
entry.setValue(0);
|
||||
fail("Expected UnsupportedOperationException");
|
||||
} catch (UnsupportedOperationException expected) {
|
||||
}
|
||||
try {
|
||||
it.remove();
|
||||
fail("Expected UnsupportedOperationException");
|
||||
} catch (UnsupportedOperationException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
Set<Integer> keySet = map.keySet();
|
||||
try {
|
||||
keySet.clear();
|
||||
fail("Expected UnsupportedOperationException");
|
||||
} catch (UnsupportedOperationException expected) {
|
||||
}
|
||||
|
||||
Iterator<Integer> keys = keySet.iterator();
|
||||
while (keys.hasNext()) {
|
||||
Integer key = keys.next();
|
||||
try {
|
||||
keySet.remove(key);
|
||||
fail("Expected UnsupportedOperationException");
|
||||
} catch (UnsupportedOperationException expected) {
|
||||
}
|
||||
try {
|
||||
keys.remove();
|
||||
fail("Expected UnsupportedOperationException");
|
||||
} catch (UnsupportedOperationException expected) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Set<Integer> makeSortedKeySet(Integer... keys) {
|
||||
return new TreeSet<Integer>(Arrays.<Integer>asList(keys));
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests that proto2 api generation doesn't cause compile errors when
|
||||
* compiling protocol buffers that have names that would otherwise conflict
|
||||
* if not fully qualified (like @Deprecated and @Override).
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class TestBadIdentifiers extends TestCase {
|
||||
|
||||
public void testCompilation() {
|
||||
// If this compiles, it means the generation was correct.
|
||||
TestBadIdentifiersProto.Deprecated.newBuilder();
|
||||
TestBadIdentifiersProto.Override.newBuilder();
|
||||
}
|
||||
}
|
@ -214,7 +214,9 @@ import static com.google.protobuf.UnittestLite.packedBoolExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedEnumExtensionLite;
|
||||
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensionsOrBuilder;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder;
|
||||
import protobuf_unittest.UnittestProto.TestPackedExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestPackedTypes;
|
||||
import protobuf_unittest.UnittestProto.TestUnpackedTypes;
|
||||
@ -225,6 +227,7 @@ import com.google.protobuf.test.UnittestImport.ImportEnum;
|
||||
|
||||
import com.google.protobuf.UnittestLite.TestAllTypesLite;
|
||||
import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
|
||||
import com.google.protobuf.UnittestLite.TestAllExtensionsLiteOrBuilder;
|
||||
import com.google.protobuf.UnittestLite.TestPackedExtensionsLite;
|
||||
import com.google.protobuf.UnittestLite.ForeignMessageLite;
|
||||
import com.google.protobuf.UnittestLite.ForeignEnumLite;
|
||||
@ -244,9 +247,12 @@ import java.io.RandomAccessFile;
|
||||
* set all fields of a message, serialize it, parse it, and check that all
|
||||
* fields are set.
|
||||
*
|
||||
* <p>This code is not to be used outside of {@code com.google.protobuf} and
|
||||
* subpackages.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
class TestUtil {
|
||||
public final class TestUtil {
|
||||
private TestUtil() {}
|
||||
|
||||
/** Helper to convert a String to ByteString. */
|
||||
@ -485,7 +491,7 @@ class TestUtil {
|
||||
* Assert (using {@code junit.framework.Assert}} that all fields of
|
||||
* {@code message} are set to the values assigned by {@code setAllFields}.
|
||||
*/
|
||||
public static void assertAllFieldsSet(TestAllTypes message) {
|
||||
public static void assertAllFieldsSet(TestAllTypesOrBuilder message) {
|
||||
Assert.assertTrue(message.hasOptionalInt32 ());
|
||||
Assert.assertTrue(message.hasOptionalInt64 ());
|
||||
Assert.assertTrue(message.hasOptionalUint32 ());
|
||||
@ -682,13 +688,12 @@ class TestUtil {
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Assert (using {@code junit.framework.Assert}} that all fields of
|
||||
* {@code message} are cleared, and that getting the fields returns their
|
||||
* default values.
|
||||
*/
|
||||
public static void assertClear(TestAllTypes message) {
|
||||
public static void assertClear(TestAllTypesOrBuilder message) {
|
||||
// hasBlah() should initially be false for all optional fields.
|
||||
Assert.assertFalse(message.hasOptionalInt32 ());
|
||||
Assert.assertFalse(message.hasOptionalInt64 ());
|
||||
@ -838,7 +843,8 @@ class TestUtil {
|
||||
* {@code message} are set to the values assigned by {@code setAllFields}
|
||||
* followed by {@code modifyRepeatedFields}.
|
||||
*/
|
||||
public static void assertRepeatedFieldsModified(TestAllTypes message) {
|
||||
public static void assertRepeatedFieldsModified(
|
||||
TestAllTypesOrBuilder message) {
|
||||
// ModifyRepeatedFields only sets the second repeated element of each
|
||||
// field. In addition to verifying this, we also verify that the first
|
||||
// element and size were *not* modified.
|
||||
@ -1352,7 +1358,8 @@ class TestUtil {
|
||||
* Assert (using {@code junit.framework.Assert}} that all extensions of
|
||||
* {@code message} are set to the values assigned by {@code setAllExtensions}.
|
||||
*/
|
||||
public static void assertAllExtensionsSet(TestAllExtensions message) {
|
||||
public static void assertAllExtensionsSet(
|
||||
TestAllExtensionsOrBuilder message) {
|
||||
Assert.assertTrue(message.hasExtension(optionalInt32Extension ));
|
||||
Assert.assertTrue(message.hasExtension(optionalInt64Extension ));
|
||||
Assert.assertTrue(message.hasExtension(optionalUint32Extension ));
|
||||
@ -1567,7 +1574,7 @@ class TestUtil {
|
||||
* {@code message} are cleared, and that getting the extensions returns their
|
||||
* default values.
|
||||
*/
|
||||
public static void assertExtensionsClear(TestAllExtensions message) {
|
||||
public static void assertExtensionsClear(TestAllExtensionsOrBuilder message) {
|
||||
// hasBlah() should initially be false for all optional fields.
|
||||
Assert.assertFalse(message.hasExtension(optionalInt32Extension ));
|
||||
Assert.assertFalse(message.hasExtension(optionalInt64Extension ));
|
||||
@ -1752,7 +1759,7 @@ class TestUtil {
|
||||
* followed by {@code modifyRepeatedExtensions}.
|
||||
*/
|
||||
public static void assertRepeatedExtensionsModified(
|
||||
TestAllExtensions message) {
|
||||
TestAllExtensionsOrBuilder message) {
|
||||
// ModifyRepeatedFields only sets the second repeated element of each
|
||||
// field. In addition to verifying this, we also verify that the first
|
||||
// element and size were *not* modified.
|
||||
@ -2106,7 +2113,8 @@ class TestUtil {
|
||||
* Assert (using {@code junit.framework.Assert}} that all extensions of
|
||||
* {@code message} are set to the values assigned by {@code setAllExtensions}.
|
||||
*/
|
||||
public static void assertAllExtensionsSet(TestAllExtensionsLite message) {
|
||||
public static void assertAllExtensionsSet(
|
||||
TestAllExtensionsLiteOrBuilder message) {
|
||||
Assert.assertTrue(message.hasExtension(optionalInt32ExtensionLite ));
|
||||
Assert.assertTrue(message.hasExtension(optionalInt64ExtensionLite ));
|
||||
Assert.assertTrue(message.hasExtension(optionalUint32ExtensionLite ));
|
||||
@ -2321,7 +2329,8 @@ class TestUtil {
|
||||
* {@code message} are cleared, and that getting the extensions returns their
|
||||
* default values.
|
||||
*/
|
||||
public static void assertExtensionsClear(TestAllExtensionsLite message) {
|
||||
public static void assertExtensionsClear(
|
||||
TestAllExtensionsLiteOrBuilder message) {
|
||||
// hasBlah() should initially be false for all optional fields.
|
||||
Assert.assertFalse(message.hasExtension(optionalInt32ExtensionLite ));
|
||||
Assert.assertFalse(message.hasExtension(optionalInt64ExtensionLite ));
|
||||
@ -2478,7 +2487,7 @@ class TestUtil {
|
||||
* followed by {@code modifyRepeatedExtensions}.
|
||||
*/
|
||||
public static void assertRepeatedExtensionsModified(
|
||||
TestAllExtensionsLite message) {
|
||||
TestAllExtensionsLiteOrBuilder message) {
|
||||
// ModifyRepeatedFields only sets the second repeated element of each
|
||||
// field. In addition to verifying this, we also verify that the first
|
||||
// element and size were *not* modified.
|
||||
@ -3015,7 +3024,7 @@ class TestUtil {
|
||||
* {@code message} are set to the values assigned by {@code setAllFields},
|
||||
* using the {@link Message} reflection interface.
|
||||
*/
|
||||
public void assertAllFieldsSetViaReflection(Message message) {
|
||||
public void assertAllFieldsSetViaReflection(MessageOrBuilder message) {
|
||||
Assert.assertTrue(message.hasField(f("optional_int32" )));
|
||||
Assert.assertTrue(message.hasField(f("optional_int64" )));
|
||||
Assert.assertTrue(message.hasField(f("optional_uint32" )));
|
||||
@ -3248,7 +3257,7 @@ class TestUtil {
|
||||
* {@code message} are cleared, and that getting the fields returns their
|
||||
* default values, using the {@link Message} reflection interface.
|
||||
*/
|
||||
public void assertClearViaReflection(Message message) {
|
||||
public void assertClearViaReflection(MessageOrBuilder message) {
|
||||
// has_blah() should initially be false for all optional fields.
|
||||
Assert.assertFalse(message.hasField(f("optional_int32" )));
|
||||
Assert.assertFalse(message.hasField(f("optional_int64" )));
|
||||
@ -3405,9 +3414,11 @@ class TestUtil {
|
||||
Assert.assertEquals("123", message.getField(f("default_cord")));
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public void assertRepeatedFieldsModifiedViaReflection(Message message) {
|
||||
public void assertRepeatedFieldsModifiedViaReflection(
|
||||
MessageOrBuilder message) {
|
||||
// ModifyRepeatedFields only sets the second repeated element of each
|
||||
// field. In addition to verifying this, we also verify that the first
|
||||
// element and size were *not* modified.
|
||||
@ -3543,7 +3554,7 @@ class TestUtil {
|
||||
message.addRepeatedField(f("packed_enum" ), foreignBaz);
|
||||
}
|
||||
|
||||
public void assertPackedFieldsSetViaReflection(Message message) {
|
||||
public void assertPackedFieldsSetViaReflection(MessageOrBuilder message) {
|
||||
Assert.assertEquals(2, message.getRepeatedFieldCount(f("packed_int32" )));
|
||||
Assert.assertEquals(2, message.getRepeatedFieldCount(f("packed_int64" )));
|
||||
Assert.assertEquals(2, message.getRepeatedFieldCount(f("packed_uint32" )));
|
||||
@ -3699,7 +3710,7 @@ class TestUtil {
|
||||
|
||||
/**
|
||||
* @param filePath The path relative to
|
||||
* {@link com.google.testing.util.TestUtil#getDefaultSrcDir}.
|
||||
* {@link #getTestDataDir}.
|
||||
*/
|
||||
public static String readTextFromFile(String filePath) {
|
||||
return readBytesFromFile(filePath).toStringUtf8();
|
||||
@ -3728,8 +3739,8 @@ class TestUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param filePath The path relative to
|
||||
* {@link com.google.testing.util.TestUtil#getDefaultSrcDir}.
|
||||
* @param filename The path relative to
|
||||
* {@link #getTestDataDir}.
|
||||
*/
|
||||
public static ByteString readBytesFromFile(String filename) {
|
||||
File fullPath = new File(getTestDataDir(), filename);
|
||||
@ -3749,7 +3760,7 @@ class TestUtil {
|
||||
/**
|
||||
* Get the bytes of the "golden message". This is a serialized TestAllTypes
|
||||
* with all fields set as they would be by
|
||||
* {@link setAllFields(TestAllTypes.Builder)}, but it is loaded from a file
|
||||
* {@link #setAllFields(TestAllTypes.Builder)}, but it is loaded from a file
|
||||
* on disk rather than generated dynamically. The file is actually generated
|
||||
* by C++ code, so testing against it verifies compatibility with C++.
|
||||
*/
|
||||
@ -3764,7 +3775,7 @@ class TestUtil {
|
||||
/**
|
||||
* Get the bytes of the "golden packed fields message". This is a serialized
|
||||
* TestPackedTypes with all fields set as they would be by
|
||||
* {@link setPackedFields(TestPackedTypes.Builder)}, but it is loaded from a
|
||||
* {@link #setPackedFields(TestPackedTypes.Builder)}, but it is loaded from a
|
||||
* file on disk rather than generated dynamically. The file is actually
|
||||
* generated by C++ code, so testing against it verifies compatibility with
|
||||
* C++.
|
||||
@ -3777,4 +3788,24 @@ class TestUtil {
|
||||
return goldenPackedFieldsMessage;
|
||||
}
|
||||
private static ByteString goldenPackedFieldsMessage = null;
|
||||
|
||||
/**
|
||||
* Mock implementation of {@link GeneratedMessage.BuilderParent} for testing.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public static class MockBuilderParent
|
||||
implements GeneratedMessage.BuilderParent {
|
||||
|
||||
private int invalidations;
|
||||
|
||||
@Override
|
||||
public void markDirty() {
|
||||
invalidations++;
|
||||
}
|
||||
|
||||
public int getInvalidationCount() {
|
||||
return invalidations;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,14 +31,14 @@
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.Descriptors.FieldDescriptor;
|
||||
import protobuf_unittest.UnittestProto.OneString;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestEmptyMessage;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage;
|
||||
import protobuf_unittest.UnittestMset.TestMessageSet;
|
||||
import protobuf_unittest.UnittestMset.TestMessageSetExtension1;
|
||||
import protobuf_unittest.UnittestMset.TestMessageSetExtension2;
|
||||
import protobuf_unittest.UnittestProto.OneString;
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage;
|
||||
import protobuf_unittest.UnittestProto.TestEmptyMessage;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
@ -133,40 +133,44 @@ public class TextFormatTest extends TestCase {
|
||||
assertEquals(allExtensionsSetText, javaText);
|
||||
}
|
||||
|
||||
// Creates an example unknown field set.
|
||||
private UnknownFieldSet makeUnknownFieldSet() {
|
||||
return UnknownFieldSet.newBuilder()
|
||||
.addField(5,
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addVarint(1)
|
||||
.addFixed32(2)
|
||||
.addFixed64(3)
|
||||
.addLengthDelimited(ByteString.copyFromUtf8("4"))
|
||||
.addGroup(
|
||||
UnknownFieldSet.newBuilder()
|
||||
.addField(10,
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addVarint(5)
|
||||
.build())
|
||||
.build())
|
||||
.build())
|
||||
.addField(8,
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addVarint(1)
|
||||
.addVarint(2)
|
||||
.addVarint(3)
|
||||
.build())
|
||||
.addField(15,
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addVarint(0xABCDEF1234567890L)
|
||||
.addFixed32(0xABCD1234)
|
||||
.addFixed64(0xABCDEF1234567890L)
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
public void testPrintUnknownFields() throws Exception {
|
||||
// Test printing of unknown fields in a message.
|
||||
|
||||
TestEmptyMessage message =
|
||||
TestEmptyMessage.newBuilder()
|
||||
.setUnknownFields(
|
||||
UnknownFieldSet.newBuilder()
|
||||
.addField(5,
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addVarint(1)
|
||||
.addFixed32(2)
|
||||
.addFixed64(3)
|
||||
.addLengthDelimited(ByteString.copyFromUtf8("4"))
|
||||
.addGroup(
|
||||
UnknownFieldSet.newBuilder()
|
||||
.addField(10,
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addVarint(5)
|
||||
.build())
|
||||
.build())
|
||||
.build())
|
||||
.addField(8,
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addVarint(1)
|
||||
.addVarint(2)
|
||||
.addVarint(3)
|
||||
.build())
|
||||
.addField(15,
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addVarint(0xABCDEF1234567890L)
|
||||
.addFixed32(0xABCD1234)
|
||||
.addFixed64(0xABCDEF1234567890L)
|
||||
.build())
|
||||
.build())
|
||||
.setUnknownFields(makeUnknownFieldSet())
|
||||
.build();
|
||||
|
||||
assertEquals(
|
||||
@ -408,6 +412,9 @@ public class TextFormatTest extends TestCase {
|
||||
assertParseError(
|
||||
"1:16: Expected \"true\" or \"false\".",
|
||||
"optional_bool: maybe");
|
||||
assertParseError(
|
||||
"1:16: Expected \"true\" or \"false\".",
|
||||
"optional_bool: 2");
|
||||
assertParseError(
|
||||
"1:18: Expected string.",
|
||||
"optional_string: 123");
|
||||
@ -485,6 +492,11 @@ public class TextFormatTest extends TestCase {
|
||||
assertEquals(bytes(0xe1, 0x88, 0xb4),
|
||||
TextFormat.unescapeBytes("\\xe1\\x88\\xb4"));
|
||||
|
||||
// Handling of strings with unescaped Unicode characters > 255.
|
||||
final String zh = "\u9999\u6e2f\u4e0a\u6d77\ud84f\udf80\u8c50\u9280\u884c";
|
||||
ByteString zhByteString = ByteString.copyFromUtf8(zh);
|
||||
assertEquals(zhByteString, TextFormat.unescapeBytes(zh));
|
||||
|
||||
// Errors.
|
||||
try {
|
||||
TextFormat.unescapeText("\\x");
|
||||
@ -626,6 +638,13 @@ public class TextFormatTest extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testParseString() throws Exception {
|
||||
final String zh = "\u9999\u6e2f\u4e0a\u6d77\ud84f\udf80\u8c50\u9280\u884c";
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TextFormat.merge("optional_string: \"" + zh + "\"", builder);
|
||||
assertEquals(zh, builder.getOptionalString());
|
||||
}
|
||||
|
||||
public void testParseLongString() throws Exception {
|
||||
String longText =
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
@ -654,9 +673,80 @@ public class TextFormatTest extends TestCase {
|
||||
assertEquals(longText, builder.getOptionalString());
|
||||
}
|
||||
|
||||
public void testParseBoolean() throws Exception {
|
||||
String goodText =
|
||||
"repeated_bool: t repeated_bool : 0\n" +
|
||||
"repeated_bool :f repeated_bool:1";
|
||||
String goodTextCanonical =
|
||||
"repeated_bool: true\n" +
|
||||
"repeated_bool: false\n" +
|
||||
"repeated_bool: false\n" +
|
||||
"repeated_bool: true\n";
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TextFormat.merge(goodText, builder);
|
||||
assertEquals(goodTextCanonical, builder.build().toString());
|
||||
|
||||
try {
|
||||
TestAllTypes.Builder badBuilder = TestAllTypes.newBuilder();
|
||||
TextFormat.merge("optional_bool:2", badBuilder);
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (TextFormat.ParseException e) {
|
||||
// success
|
||||
}
|
||||
try {
|
||||
TestAllTypes.Builder badBuilder = TestAllTypes.newBuilder();
|
||||
TextFormat.merge("optional_bool: foo", badBuilder);
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (TextFormat.ParseException e) {
|
||||
// success
|
||||
}
|
||||
}
|
||||
|
||||
public void testParseAdjacentStringLiterals() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TextFormat.merge("optional_string: \"foo\" 'corge' \"grault\"", builder);
|
||||
assertEquals("foocorgegrault", builder.getOptionalString());
|
||||
}
|
||||
|
||||
public void testPrintFieldValue() throws Exception {
|
||||
assertPrintFieldValue("\"Hello\"", "Hello", "repeated_string");
|
||||
assertPrintFieldValue("123.0", 123f, "repeated_float");
|
||||
assertPrintFieldValue("123.0", 123d, "repeated_double");
|
||||
assertPrintFieldValue("123", 123, "repeated_int32");
|
||||
assertPrintFieldValue("123", 123L, "repeated_int64");
|
||||
assertPrintFieldValue("true", true, "repeated_bool");
|
||||
assertPrintFieldValue("4294967295", 0xFFFFFFFF, "repeated_uint32");
|
||||
assertPrintFieldValue("18446744073709551615", 0xFFFFFFFFFFFFFFFFL,
|
||||
"repeated_uint64");
|
||||
assertPrintFieldValue("\"\\001\\002\\003\"",
|
||||
ByteString.copyFrom(new byte[] {1, 2, 3}), "repeated_bytes");
|
||||
}
|
||||
|
||||
private void assertPrintFieldValue(String expect, Object value,
|
||||
String fieldName) throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
TextFormat.printFieldValue(
|
||||
TestAllTypes.getDescriptor().findFieldByName(fieldName),
|
||||
value, sb);
|
||||
assertEquals(expect, sb.toString());
|
||||
}
|
||||
|
||||
public void testShortDebugString() {
|
||||
assertEquals("optional_nested_message { bb: 42 } repeated_int32: 1"
|
||||
+ " repeated_uint32: 2",
|
||||
TextFormat.shortDebugString(TestAllTypes.newBuilder()
|
||||
.addRepeatedInt32(1)
|
||||
.addRepeatedUint32(2)
|
||||
.setOptionalNestedMessage(
|
||||
NestedMessage.newBuilder().setBb(42).build())
|
||||
.build()));
|
||||
}
|
||||
|
||||
public void testShortDebugString_unknown() {
|
||||
assertEquals("5: 1 5: 0x00000002 5: 0x0000000000000003 5: \"4\" 5 { 10: 5 }"
|
||||
+ " 8: 1 8: 2 8: 3 15: 12379813812177893520 15: 0xabcd1234 15:"
|
||||
+ " 0xabcdef1234567890",
|
||||
TextFormat.shortDebugString(makeUnknownFieldSet()));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,152 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.ListIterator;
|
||||
|
||||
/**
|
||||
* Tests for {@link UnmodifiableLazyStringList}.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class UnmodifiableLazyStringListTest extends TestCase {
|
||||
|
||||
private static String STRING_A = "A";
|
||||
private static String STRING_B = "B";
|
||||
private static String STRING_C = "C";
|
||||
|
||||
private static ByteString BYTE_STRING_A = ByteString.copyFromUtf8("A");
|
||||
private static ByteString BYTE_STRING_B = ByteString.copyFromUtf8("B");
|
||||
private static ByteString BYTE_STRING_C = ByteString.copyFromUtf8("C");
|
||||
|
||||
public void testReadOnlyMethods() {
|
||||
LazyStringArrayList rawList = createSampleList();
|
||||
UnmodifiableLazyStringList list = new UnmodifiableLazyStringList(rawList);
|
||||
assertEquals(3, list.size());
|
||||
assertSame(STRING_A, list.get(0));
|
||||
assertSame(STRING_B, list.get(1));
|
||||
assertSame(STRING_C, list.get(2));
|
||||
assertEquals(BYTE_STRING_A, list.getByteString(0));
|
||||
assertEquals(BYTE_STRING_B, list.getByteString(1));
|
||||
assertEquals(BYTE_STRING_C, list.getByteString(2));
|
||||
}
|
||||
|
||||
public void testModifyMethods() {
|
||||
LazyStringArrayList rawList = createSampleList();
|
||||
UnmodifiableLazyStringList list = new UnmodifiableLazyStringList(rawList);
|
||||
|
||||
try {
|
||||
list.remove(0);
|
||||
fail();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// expected
|
||||
}
|
||||
assertEquals(3, list.size());
|
||||
|
||||
try {
|
||||
list.add(STRING_B);
|
||||
fail();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// expected
|
||||
}
|
||||
assertEquals(3, list.size());
|
||||
|
||||
try {
|
||||
list.set(1, STRING_B);
|
||||
fail();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
public void testIterator() {
|
||||
LazyStringArrayList rawList = createSampleList();
|
||||
UnmodifiableLazyStringList list = new UnmodifiableLazyStringList(rawList);
|
||||
|
||||
Iterator<String> iter = list.iterator();
|
||||
int count = 0;
|
||||
while (iter.hasNext()) {
|
||||
iter.next();
|
||||
count++;
|
||||
try {
|
||||
iter.remove();
|
||||
fail();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
assertEquals(3, count);
|
||||
|
||||
}
|
||||
|
||||
public void testListIterator() {
|
||||
LazyStringArrayList rawList = createSampleList();
|
||||
UnmodifiableLazyStringList list = new UnmodifiableLazyStringList(rawList);
|
||||
|
||||
ListIterator<String> iter = list.listIterator();
|
||||
int count = 0;
|
||||
while (iter.hasNext()) {
|
||||
iter.next();
|
||||
count++;
|
||||
try {
|
||||
iter.remove();
|
||||
fail();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
iter.set("bar");
|
||||
fail();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
iter.add("bar");
|
||||
fail();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
assertEquals(3, count);
|
||||
|
||||
}
|
||||
|
||||
private LazyStringArrayList createSampleList() {
|
||||
LazyStringArrayList rawList = new LazyStringArrayList();
|
||||
rawList.add(STRING_A);
|
||||
rawList.add(STRING_B);
|
||||
rawList.add(STRING_C);
|
||||
return rawList;
|
||||
}
|
||||
}
|
@ -33,6 +33,10 @@
|
||||
// A proto file which tests the java_multiple_files option.
|
||||
|
||||
|
||||
// Some generic_services option(s) added automatically.
|
||||
// See: http://go/proto2-generic-services-default
|
||||
option java_generic_services = true; // auto-added
|
||||
|
||||
import "google/protobuf/unittest.proto";
|
||||
|
||||
package protobuf_unittest;
|
||||
|
@ -0,0 +1,53 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Author: jonp@google.com (Jon Perlow)
|
||||
//
|
||||
|
||||
package protobuf_unittest;
|
||||
|
||||
option java_multiple_files = true;
|
||||
option java_outer_classname = "NestedBuilders";
|
||||
|
||||
|
||||
message Vehicle {
|
||||
optional Engine engine = 1;
|
||||
repeated Wheel wheel = 2;
|
||||
}
|
||||
|
||||
message Engine {
|
||||
optional int32 cylinder = 1;
|
||||
optional int32 liters = 2;
|
||||
}
|
||||
|
||||
message Wheel {
|
||||
optional int32 radius = 1;
|
||||
optional int32 width = 2;
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Author: Darick Tong (darick@google.com)
|
||||
//
|
||||
// A proto file with nested extensions. Note that this must be defined in
|
||||
// a separate file to properly test the initialization of the outer class.
|
||||
|
||||
|
||||
import "com/google/protobuf/non_nested_extension.proto";
|
||||
|
||||
package protobuf_unittest;
|
||||
|
||||
message MyNestedExtension {
|
||||
extend MessageToBeExtended {
|
||||
optional MessageToBeExtended recursiveExtension = 2;
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Author: Darick Tong (darick@google.com)
|
||||
//
|
||||
// A proto file with nested extensions for a MessageLite messages. Note that
|
||||
// this must be defined in a separate file to properly test the initialization
|
||||
// of the outer class.
|
||||
|
||||
|
||||
package protobuf_unittest;
|
||||
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
import "com/google/protobuf/non_nested_extension_lite.proto";
|
||||
|
||||
message MyNestedExtensionLite {
|
||||
extend MessageLiteToBeExtended {
|
||||
optional MessageLiteToBeExtended recursiveExtensionLite = 3;
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Author: Darick Tong (darick@google.com)
|
||||
//
|
||||
// A proto file with extensions.
|
||||
|
||||
|
||||
package protobuf_unittest;
|
||||
|
||||
message MessageToBeExtended {
|
||||
extensions 1 to max;
|
||||
}
|
||||
|
||||
message MyNonNestedExtension {
|
||||
}
|
||||
|
||||
extend MessageToBeExtended {
|
||||
optional MyNonNestedExtension nonNestedExtension = 1;
|
||||
}
|
||||
|
@ -0,0 +1,50 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Author: Darick Tong (darick@google.com)
|
||||
//
|
||||
// A proto file with extensions for a MessageLite messages.
|
||||
|
||||
|
||||
package protobuf_unittest;
|
||||
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
message MessageLiteToBeExtended {
|
||||
extensions 1 to max;
|
||||
}
|
||||
|
||||
message MyNonNestedExtensionLite {
|
||||
}
|
||||
|
||||
extend MessageLiteToBeExtended {
|
||||
optional MyNonNestedExtensionLite nonNestedExtensionLite = 1;
|
||||
}
|
||||
|
@ -0,0 +1,66 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Author: jonp@google.com (Jon Perlow)
|
||||
|
||||
// This file tests that various identifiers work as field and type names even
|
||||
// though the same identifiers are used internally by the java code generator.
|
||||
|
||||
|
||||
// Some generic_services option(s) added automatically.
|
||||
// See: http://go/proto2-generic-services-default
|
||||
option java_generic_services = true; // auto-added
|
||||
|
||||
package io_protocol_tests;
|
||||
|
||||
option java_package = "com.google.protobuf";
|
||||
option java_outer_classname = "TestBadIdentifiersProto";
|
||||
|
||||
message TestMessage {
|
||||
}
|
||||
|
||||
message Deprecated {
|
||||
enum TestEnum {
|
||||
FOO = 1;
|
||||
}
|
||||
|
||||
optional int32 field1 = 1 [deprecated=true];
|
||||
optional TestEnum field2 = 2 [deprecated=true];
|
||||
optional TestMessage field3 = 3 [deprecated=true];
|
||||
}
|
||||
|
||||
message Override {
|
||||
optional int32 override = 1;
|
||||
}
|
||||
|
||||
service TestConflictingMethodNames {
|
||||
rpc Override(TestMessage) returns (TestMessage);
|
||||
}
|
||||
|
@ -28,16 +28,6 @@
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# TODO(robinson): We probably need to provide deep-copy methods for
|
||||
# descriptor types. When a FieldDescriptor is passed into
|
||||
# Descriptor.__init__(), we should make a deep copy and then set
|
||||
# containing_type on it. Alternatively, we could just get
|
||||
# rid of containing_type (iit's not needed for reflection.py, at least).
|
||||
#
|
||||
# TODO(robinson): Print method?
|
||||
#
|
||||
# TODO(robinson): Useful __repr__?
|
||||
|
||||
"""Descriptors essentially contain exactly the information found in a .proto
|
||||
file, in types that make this information accessible in Python.
|
||||
"""
|
||||
@ -45,6 +35,13 @@ file, in types that make this information accessible in Python.
|
||||
__author__ = 'robinson@google.com (Will Robinson)'
|
||||
|
||||
|
||||
from google.protobuf.internal import api_implementation
|
||||
|
||||
|
||||
if api_implementation.Type() == 'cpp':
|
||||
from google.protobuf.internal import cpp_message
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
"""Base error for this module."""
|
||||
|
||||
@ -396,6 +393,13 @@ class FieldDescriptor(DescriptorBase):
|
||||
self.enum_type = enum_type
|
||||
self.is_extension = is_extension
|
||||
self.extension_scope = extension_scope
|
||||
if api_implementation.Type() == 'cpp':
|
||||
if is_extension:
|
||||
self._cdescriptor = cpp_message.GetExtensionDescriptor(full_name)
|
||||
else:
|
||||
self._cdescriptor = cpp_message.GetFieldDescriptor(full_name)
|
||||
else:
|
||||
self._cdescriptor = None
|
||||
|
||||
|
||||
class EnumDescriptor(_NestedDescriptorBase):
|
||||
@ -567,9 +571,13 @@ class FileDescriptor(DescriptorBase):
|
||||
"""Constructor."""
|
||||
super(FileDescriptor, self).__init__(options, 'FileOptions')
|
||||
|
||||
self.message_types_by_name = {}
|
||||
self.name = name
|
||||
self.package = package
|
||||
self.serialized_pb = serialized_pb
|
||||
if (api_implementation.Type() == 'cpp' and
|
||||
self.serialized_pb is not None):
|
||||
cpp_message.BuildFile(self.serialized_pb)
|
||||
|
||||
def CopyToProto(self, proto):
|
||||
"""Copies this to a descriptor_pb2.FileDescriptorProto.
|
||||
|
64
python/google/protobuf/internal/api_implementation.py
Executable file
64
python/google/protobuf/internal/api_implementation.py
Executable file
@ -0,0 +1,64 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2008 Google Inc. All rights reserved.
|
||||
# http://code.google.com/p/protobuf/
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
This module is the central entity that determines which implementation of the
|
||||
API is used.
|
||||
"""
|
||||
|
||||
__author__ = 'petar@google.com (Petar Petrov)'
|
||||
|
||||
import os
|
||||
# This environment variable can be used to switch to a certain implementation
|
||||
# of the Python API. Right now only 'python' and 'cpp' are valid values. Any
|
||||
# other value will be ignored.
|
||||
_implementation_type = os.getenv('PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION',
|
||||
'python')
|
||||
|
||||
|
||||
if _implementation_type != 'python':
|
||||
# For now, by default use the pure-Python implementation.
|
||||
# The code below checks if the C extension is available and
|
||||
# uses it if it is available.
|
||||
_implementation_type = 'cpp'
|
||||
## Determine automatically which implementation to use.
|
||||
#try:
|
||||
# from google.protobuf.internal import cpp_message
|
||||
# _implementation_type = 'cpp'
|
||||
#except ImportError, e:
|
||||
# _implementation_type = 'python'
|
||||
|
||||
|
||||
# Usage of this function is discouraged. Clients shouldn't care which
|
||||
# implementation of the API is in use. Note that there is no guarantee
|
||||
# that differences between APIs will be maintained.
|
||||
# Please don't use this function if possible.
|
||||
def Type():
|
||||
return _implementation_type
|
@ -72,9 +72,15 @@ class BaseContainer(object):
|
||||
# The concrete classes should define __eq__.
|
||||
return not self == other
|
||||
|
||||
def __hash__(self):
|
||||
raise TypeError('unhashable object')
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self._values)
|
||||
|
||||
def sort(self, sort_function=cmp):
|
||||
self._values.sort(sort_function)
|
||||
|
||||
|
||||
class RepeatedScalarFieldContainer(BaseContainer):
|
||||
|
||||
@ -198,28 +204,37 @@ class RepeatedCompositeFieldContainer(BaseContainer):
|
||||
super(RepeatedCompositeFieldContainer, self).__init__(message_listener)
|
||||
self._message_descriptor = message_descriptor
|
||||
|
||||
def add(self):
|
||||
new_element = self._message_descriptor._concrete_class()
|
||||
def add(self, **kwargs):
|
||||
"""Adds a new element at the end of the list and returns it. Keyword
|
||||
arguments may be used to initialize the element.
|
||||
"""
|
||||
new_element = self._message_descriptor._concrete_class(**kwargs)
|
||||
new_element._SetListener(self._message_listener)
|
||||
self._values.append(new_element)
|
||||
if not self._message_listener.dirty:
|
||||
self._message_listener.Modified()
|
||||
return new_element
|
||||
|
||||
def MergeFrom(self, other):
|
||||
"""Appends the contents of another repeated field of the same type to this
|
||||
one, copying each individual message.
|
||||
def extend(self, elem_seq):
|
||||
"""Extends by appending the given sequence of elements of the same type
|
||||
as this one, copying each individual message.
|
||||
"""
|
||||
message_class = self._message_descriptor._concrete_class
|
||||
listener = self._message_listener
|
||||
values = self._values
|
||||
for message in other._values:
|
||||
for message in elem_seq:
|
||||
new_element = message_class()
|
||||
new_element._SetListener(listener)
|
||||
new_element.MergeFrom(message)
|
||||
values.append(new_element)
|
||||
listener.Modified()
|
||||
|
||||
def MergeFrom(self, other):
|
||||
"""Appends the contents of another repeated field of the same type to this
|
||||
one, copying each individual message.
|
||||
"""
|
||||
self.extend(other._values)
|
||||
|
||||
def __getslice__(self, start, stop):
|
||||
"""Retrieves the subset of items from between the specified indices."""
|
||||
return self._values[start:stop]
|
||||
|
616
python/google/protobuf/internal/cpp_message.py
Executable file
616
python/google/protobuf/internal/cpp_message.py
Executable file
@ -0,0 +1,616 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2008 Google Inc. All rights reserved.
|
||||
# http://code.google.com/p/protobuf/
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""Contains helper functions used to create protocol message classes from
|
||||
Descriptor objects at runtime backed by the protocol buffer C++ API.
|
||||
"""
|
||||
|
||||
__author__ = 'petar@google.com (Petar Petrov)'
|
||||
|
||||
import operator
|
||||
from google.protobuf.internal import _net_proto2___python
|
||||
from google.protobuf import message
|
||||
|
||||
|
||||
_LABEL_REPEATED = _net_proto2___python.LABEL_REPEATED
|
||||
_LABEL_OPTIONAL = _net_proto2___python.LABEL_OPTIONAL
|
||||
_CPPTYPE_MESSAGE = _net_proto2___python.CPPTYPE_MESSAGE
|
||||
_TYPE_MESSAGE = _net_proto2___python.TYPE_MESSAGE
|
||||
|
||||
|
||||
def GetDescriptorPool():
|
||||
"""Creates a new DescriptorPool C++ object."""
|
||||
return _net_proto2___python.NewCDescriptorPool()
|
||||
|
||||
|
||||
_pool = GetDescriptorPool()
|
||||
|
||||
|
||||
def GetFieldDescriptor(full_field_name):
|
||||
"""Searches for a field descriptor given a full field name."""
|
||||
return _pool.FindFieldByName(full_field_name)
|
||||
|
||||
|
||||
def BuildFile(content):
|
||||
"""Registers a new proto file in the underlying C++ descriptor pool."""
|
||||
_net_proto2___python.BuildFile(content)
|
||||
|
||||
|
||||
def GetExtensionDescriptor(full_extension_name):
|
||||
"""Searches for extension descriptor given a full field name."""
|
||||
return _pool.FindExtensionByName(full_extension_name)
|
||||
|
||||
|
||||
def NewCMessage(full_message_name):
|
||||
"""Creates a new C++ protocol message by its name."""
|
||||
return _net_proto2___python.NewCMessage(full_message_name)
|
||||
|
||||
|
||||
def ScalarProperty(cdescriptor):
|
||||
"""Returns a scalar property for the given descriptor."""
|
||||
|
||||
def Getter(self):
|
||||
return self._cmsg.GetScalar(cdescriptor)
|
||||
|
||||
def Setter(self, value):
|
||||
self._cmsg.SetScalar(cdescriptor, value)
|
||||
|
||||
return property(Getter, Setter)
|
||||
|
||||
|
||||
def CompositeProperty(cdescriptor, message_type):
|
||||
"""Returns a Python property the given composite field."""
|
||||
|
||||
def Getter(self):
|
||||
sub_message = self._composite_fields.get(cdescriptor.name, None)
|
||||
if sub_message is None:
|
||||
cmessage = self._cmsg.NewSubMessage(cdescriptor)
|
||||
sub_message = message_type._concrete_class(__cmessage=cmessage)
|
||||
self._composite_fields[cdescriptor.name] = sub_message
|
||||
return sub_message
|
||||
|
||||
return property(Getter)
|
||||
|
||||
|
||||
class RepeatedScalarContainer(object):
|
||||
"""Container for repeated scalar fields."""
|
||||
|
||||
__slots__ = ['_message', '_cfield_descriptor', '_cmsg']
|
||||
|
||||
def __init__(self, msg, cfield_descriptor):
|
||||
self._message = msg
|
||||
self._cmsg = msg._cmsg
|
||||
self._cfield_descriptor = cfield_descriptor
|
||||
|
||||
def append(self, value):
|
||||
self._cmsg.AddRepeatedScalar(
|
||||
self._cfield_descriptor, value)
|
||||
|
||||
def extend(self, sequence):
|
||||
for element in sequence:
|
||||
self.append(element)
|
||||
|
||||
def insert(self, key, value):
|
||||
values = self[slice(None, None, None)]
|
||||
values.insert(key, value)
|
||||
self._cmsg.AssignRepeatedScalar(self._cfield_descriptor, values)
|
||||
|
||||
def remove(self, value):
|
||||
values = self[slice(None, None, None)]
|
||||
values.remove(value)
|
||||
self._cmsg.AssignRepeatedScalar(self._cfield_descriptor, values)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
values = self[slice(None, None, None)]
|
||||
values[key] = value
|
||||
self._cmsg.AssignRepeatedScalar(self._cfield_descriptor, values)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self._cmsg.GetRepeatedScalar(self._cfield_descriptor, key)
|
||||
|
||||
def __delitem__(self, key):
|
||||
self._cmsg.DeleteRepeatedField(self._cfield_descriptor, key)
|
||||
|
||||
def __len__(self):
|
||||
return len(self[slice(None, None, None)])
|
||||
|
||||
def __eq__(self, other):
|
||||
if self is other:
|
||||
return True
|
||||
if not operator.isSequenceType(other):
|
||||
raise TypeError(
|
||||
'Can only compare repeated scalar fields against sequences.')
|
||||
# We are presumably comparing against some other sequence type.
|
||||
return other == self[slice(None, None, None)]
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __hash__(self):
|
||||
raise TypeError('unhashable object')
|
||||
|
||||
def sort(self, sort_function=cmp):
|
||||
values = self[slice(None, None, None)]
|
||||
values.sort(sort_function)
|
||||
self._cmsg.AssignRepeatedScalar(self._cfield_descriptor, values)
|
||||
|
||||
|
||||
def RepeatedScalarProperty(cdescriptor):
|
||||
"""Returns a Python property the given repeated scalar field."""
|
||||
|
||||
def Getter(self):
|
||||
container = self._composite_fields.get(cdescriptor.name, None)
|
||||
if container is None:
|
||||
container = RepeatedScalarContainer(self, cdescriptor)
|
||||
self._composite_fields[cdescriptor.name] = container
|
||||
return container
|
||||
|
||||
def Setter(self, new_value):
|
||||
raise AttributeError('Assignment not allowed to repeated field '
|
||||
'"%s" in protocol message object.' % cdescriptor.name)
|
||||
|
||||
doc = 'Magic attribute generated for "%s" proto field.' % cdescriptor.name
|
||||
return property(Getter, Setter, doc=doc)
|
||||
|
||||
|
||||
class RepeatedCompositeContainer(object):
|
||||
"""Container for repeated composite fields."""
|
||||
|
||||
__slots__ = ['_message', '_subclass', '_cfield_descriptor', '_cmsg']
|
||||
|
||||
def __init__(self, msg, cfield_descriptor, subclass):
|
||||
self._message = msg
|
||||
self._cmsg = msg._cmsg
|
||||
self._subclass = subclass
|
||||
self._cfield_descriptor = cfield_descriptor
|
||||
|
||||
def add(self, **kwargs):
|
||||
cmessage = self._cmsg.AddMessage(self._cfield_descriptor)
|
||||
return self._subclass(__cmessage=cmessage, __owner=self._message, **kwargs)
|
||||
|
||||
def extend(self, elem_seq):
|
||||
"""Extends by appending the given sequence of elements of the same type
|
||||
as this one, copying each individual message.
|
||||
"""
|
||||
for message in elem_seq:
|
||||
self.add().MergeFrom(message)
|
||||
|
||||
def MergeFrom(self, other):
|
||||
for message in other[:]:
|
||||
self.add().MergeFrom(message)
|
||||
|
||||
def __getitem__(self, key):
|
||||
cmessages = self._cmsg.GetRepeatedMessage(
|
||||
self._cfield_descriptor, key)
|
||||
subclass = self._subclass
|
||||
if not isinstance(cmessages, list):
|
||||
return subclass(__cmessage=cmessages, __owner=self._message)
|
||||
|
||||
return [subclass(__cmessage=m, __owner=self._message) for m in cmessages]
|
||||
|
||||
def __delitem__(self, key):
|
||||
self._cmsg.DeleteRepeatedField(
|
||||
self._cfield_descriptor, key)
|
||||
|
||||
def __len__(self):
|
||||
return self._cmsg.FieldLength(self._cfield_descriptor)
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Compares the current instance with another one."""
|
||||
if self is other:
|
||||
return True
|
||||
if not isinstance(other, self.__class__):
|
||||
raise TypeError('Can only compare repeated composite fields against '
|
||||
'other repeated composite fields.')
|
||||
messages = self[slice(None, None, None)]
|
||||
other_messages = other[slice(None, None, None)]
|
||||
return messages == other_messages
|
||||
|
||||
def __hash__(self):
|
||||
raise TypeError('unhashable object')
|
||||
|
||||
def sort(self, sort_function=cmp):
|
||||
messages = []
|
||||
for index in range(len(self)):
|
||||
# messages[i][0] is where the i-th element of the new array has to come
|
||||
# from.
|
||||
# messages[i][1] is where the i-th element of the old array has to go.
|
||||
messages.append([index, 0, self[index]])
|
||||
messages.sort(lambda x,y: sort_function(x[2], y[2]))
|
||||
|
||||
# Remember which position each elements has to move to.
|
||||
for i in range(len(messages)):
|
||||
messages[messages[i][0]][1] = i
|
||||
|
||||
# Apply the transposition.
|
||||
for i in range(len(messages)):
|
||||
from_position = messages[i][0]
|
||||
if i == from_position:
|
||||
continue
|
||||
self._cmsg.SwapRepeatedFieldElements(
|
||||
self._cfield_descriptor, i, from_position)
|
||||
messages[messages[i][1]][0] = from_position
|
||||
|
||||
|
||||
def RepeatedCompositeProperty(cdescriptor, message_type):
|
||||
"""Returns a Python property for the given repeated composite field."""
|
||||
|
||||
def Getter(self):
|
||||
container = self._composite_fields.get(cdescriptor.name, None)
|
||||
if container is None:
|
||||
container = RepeatedCompositeContainer(
|
||||
self, cdescriptor, message_type._concrete_class)
|
||||
self._composite_fields[cdescriptor.name] = container
|
||||
return container
|
||||
|
||||
def Setter(self, new_value):
|
||||
raise AttributeError('Assignment not allowed to repeated field '
|
||||
'"%s" in protocol message object.' % cdescriptor.name)
|
||||
|
||||
doc = 'Magic attribute generated for "%s" proto field.' % cdescriptor.name
|
||||
return property(Getter, Setter, doc=doc)
|
||||
|
||||
|
||||
class ExtensionDict(object):
|
||||
"""Extension dictionary added to each protocol message."""
|
||||
|
||||
def __init__(self, msg):
|
||||
self._message = msg
|
||||
self._cmsg = msg._cmsg
|
||||
self._values = {}
|
||||
|
||||
def __setitem__(self, extension, value):
|
||||
from google.protobuf import descriptor
|
||||
if not isinstance(extension, descriptor.FieldDescriptor):
|
||||
raise KeyError('Bad extension %r.' % (extension,))
|
||||
cdescriptor = extension._cdescriptor
|
||||
if (cdescriptor.label != _LABEL_OPTIONAL or
|
||||
cdescriptor.cpp_type == _CPPTYPE_MESSAGE):
|
||||
raise TypeError('Extension %r is repeated and/or a composite type.' % (
|
||||
extension.full_name,))
|
||||
self._cmsg.SetScalar(cdescriptor, value)
|
||||
self._values[extension] = value
|
||||
|
||||
def __getitem__(self, extension):
|
||||
from google.protobuf import descriptor
|
||||
if not isinstance(extension, descriptor.FieldDescriptor):
|
||||
raise KeyError('Bad extension %r.' % (extension,))
|
||||
|
||||
cdescriptor = extension._cdescriptor
|
||||
if (cdescriptor.label != _LABEL_REPEATED and
|
||||
cdescriptor.cpp_type != _CPPTYPE_MESSAGE):
|
||||
return self._cmsg.GetScalar(cdescriptor)
|
||||
|
||||
ext = self._values.get(extension, None)
|
||||
if ext is not None:
|
||||
return ext
|
||||
|
||||
ext = self._CreateNewHandle(extension)
|
||||
self._values[extension] = ext
|
||||
return ext
|
||||
|
||||
def ClearExtension(self, extension):
|
||||
from google.protobuf import descriptor
|
||||
if not isinstance(extension, descriptor.FieldDescriptor):
|
||||
raise KeyError('Bad extension %r.' % (extension,))
|
||||
self._cmsg.ClearFieldByDescriptor(extension._cdescriptor)
|
||||
if extension in self._values:
|
||||
del self._values[extension]
|
||||
|
||||
def HasExtension(self, extension):
|
||||
from google.protobuf import descriptor
|
||||
if not isinstance(extension, descriptor.FieldDescriptor):
|
||||
raise KeyError('Bad extension %r.' % (extension,))
|
||||
return self._cmsg.HasFieldByDescriptor(extension._cdescriptor)
|
||||
|
||||
def _FindExtensionByName(self, name):
|
||||
"""Tries to find a known extension with the specified name.
|
||||
|
||||
Args:
|
||||
name: Extension full name.
|
||||
|
||||
Returns:
|
||||
Extension field descriptor.
|
||||
"""
|
||||
return self._message._extensions_by_name.get(name, None)
|
||||
|
||||
def _CreateNewHandle(self, extension):
|
||||
cdescriptor = extension._cdescriptor
|
||||
if (cdescriptor.label != _LABEL_REPEATED and
|
||||
cdescriptor.cpp_type == _CPPTYPE_MESSAGE):
|
||||
cmessage = self._cmsg.NewSubMessage(cdescriptor)
|
||||
return extension.message_type._concrete_class(__cmessage=cmessage)
|
||||
|
||||
if cdescriptor.label == _LABEL_REPEATED:
|
||||
if cdescriptor.cpp_type == _CPPTYPE_MESSAGE:
|
||||
return RepeatedCompositeContainer(
|
||||
self._message, cdescriptor, extension.message_type._concrete_class)
|
||||
else:
|
||||
return RepeatedScalarContainer(self._message, cdescriptor)
|
||||
# This shouldn't happen!
|
||||
assert False
|
||||
return None
|
||||
|
||||
|
||||
def NewMessage(message_descriptor, dictionary):
|
||||
"""Creates a new protocol message *class*."""
|
||||
_AddClassAttributesForNestedExtensions(message_descriptor, dictionary)
|
||||
_AddEnumValues(message_descriptor, dictionary)
|
||||
_AddDescriptors(message_descriptor, dictionary)
|
||||
|
||||
|
||||
def InitMessage(message_descriptor, cls):
|
||||
"""Constructs a new message instance (called before instance's __init__)."""
|
||||
cls._extensions_by_name = {}
|
||||
_AddInitMethod(message_descriptor, cls)
|
||||
_AddMessageMethods(message_descriptor, cls)
|
||||
_AddPropertiesForExtensions(message_descriptor, cls)
|
||||
|
||||
|
||||
def _AddDescriptors(message_descriptor, dictionary):
|
||||
"""Sets up a new protocol message class dictionary.
|
||||
|
||||
Args:
|
||||
message_descriptor: A Descriptor instance describing this message type.
|
||||
dictionary: Class dictionary to which we'll add a '__slots__' entry.
|
||||
"""
|
||||
dictionary['__descriptors'] = {}
|
||||
for field in message_descriptor.fields:
|
||||
dictionary['__descriptors'][field.name] = GetFieldDescriptor(
|
||||
field.full_name)
|
||||
|
||||
dictionary['__slots__'] = list(dictionary['__descriptors'].iterkeys()) + [
|
||||
'_cmsg', '_owner', '_composite_fields', 'Extensions']
|
||||
|
||||
|
||||
def _AddEnumValues(message_descriptor, dictionary):
|
||||
"""Sets class-level attributes for all enum fields defined in this message.
|
||||
|
||||
Args:
|
||||
message_descriptor: Descriptor object for this message type.
|
||||
dictionary: Class dictionary that should be populated.
|
||||
"""
|
||||
for enum_type in message_descriptor.enum_types:
|
||||
for enum_value in enum_type.values:
|
||||
dictionary[enum_value.name] = enum_value.number
|
||||
|
||||
|
||||
def _AddClassAttributesForNestedExtensions(message_descriptor, dictionary):
|
||||
"""Adds class attributes for the nested extensions."""
|
||||
extension_dict = message_descriptor.extensions_by_name
|
||||
for extension_name, extension_field in extension_dict.iteritems():
|
||||
assert extension_name not in dictionary
|
||||
dictionary[extension_name] = extension_field
|
||||
|
||||
|
||||
def _AddInitMethod(message_descriptor, cls):
|
||||
"""Adds an __init__ method to cls."""
|
||||
|
||||
# Create and attach message field properties to the message class.
|
||||
# This can be done just once per message class, since property setters and
|
||||
# getters are passed the message instance.
|
||||
# This makes message instantiation extremely fast, and at the same time it
|
||||
# doesn't require the creation of property objects for each message instance,
|
||||
# which saves a lot of memory.
|
||||
for field in message_descriptor.fields:
|
||||
field_cdescriptor = cls.__descriptors[field.name]
|
||||
if field.label == _LABEL_REPEATED:
|
||||
if field.cpp_type == _CPPTYPE_MESSAGE:
|
||||
value = RepeatedCompositeProperty(field_cdescriptor, field.message_type)
|
||||
else:
|
||||
value = RepeatedScalarProperty(field_cdescriptor)
|
||||
elif field.cpp_type == _CPPTYPE_MESSAGE:
|
||||
value = CompositeProperty(field_cdescriptor, field.message_type)
|
||||
else:
|
||||
value = ScalarProperty(field_cdescriptor)
|
||||
setattr(cls, field.name, value)
|
||||
|
||||
# Attach a constant with the field number.
|
||||
constant_name = field.name.upper() + '_FIELD_NUMBER'
|
||||
setattr(cls, constant_name, field.number)
|
||||
|
||||
def Init(self, **kwargs):
|
||||
"""Message constructor."""
|
||||
cmessage = kwargs.pop('__cmessage', None)
|
||||
if cmessage is None:
|
||||
self._cmsg = NewCMessage(message_descriptor.full_name)
|
||||
else:
|
||||
self._cmsg = cmessage
|
||||
|
||||
# Keep a reference to the owner, as the owner keeps a reference to the
|
||||
# underlying protocol buffer message.
|
||||
owner = kwargs.pop('__owner', None)
|
||||
if owner is not None:
|
||||
self._owner = owner
|
||||
|
||||
self.Extensions = ExtensionDict(self)
|
||||
self._composite_fields = {}
|
||||
|
||||
for field_name, field_value in kwargs.iteritems():
|
||||
field_cdescriptor = self.__descriptors.get(field_name, None)
|
||||
if field_cdescriptor is None:
|
||||
raise ValueError('Protocol message has no "%s" field.' % field_name)
|
||||
if field_cdescriptor.label == _LABEL_REPEATED:
|
||||
if field_cdescriptor.cpp_type == _CPPTYPE_MESSAGE:
|
||||
for val in field_value:
|
||||
getattr(self, field_name).add().MergeFrom(val)
|
||||
else:
|
||||
getattr(self, field_name).extend(field_value)
|
||||
elif field_cdescriptor.cpp_type == _CPPTYPE_MESSAGE:
|
||||
getattr(self, field_name).MergeFrom(field_value)
|
||||
else:
|
||||
setattr(self, field_name, field_value)
|
||||
|
||||
Init.__module__ = None
|
||||
Init.__doc__ = None
|
||||
cls.__init__ = Init
|
||||
|
||||
|
||||
def _IsMessageSetExtension(field):
|
||||
"""Checks if a field is a message set extension."""
|
||||
return (field.is_extension and
|
||||
field.containing_type.has_options and
|
||||
field.containing_type.GetOptions().message_set_wire_format and
|
||||
field.type == _TYPE_MESSAGE and
|
||||
field.message_type == field.extension_scope and
|
||||
field.label == _LABEL_OPTIONAL)
|
||||
|
||||
|
||||
def _AddMessageMethods(message_descriptor, cls):
|
||||
"""Adds the methods to a protocol message class."""
|
||||
if message_descriptor.is_extendable:
|
||||
|
||||
def ClearExtension(self, extension):
|
||||
self.Extensions.ClearExtension(extension)
|
||||
|
||||
def HasExtension(self, extension):
|
||||
return self.Extensions.HasExtension(extension)
|
||||
|
||||
def HasField(self, field_name):
|
||||
return self._cmsg.HasField(field_name)
|
||||
|
||||
def ClearField(self, field_name):
|
||||
if field_name in self._composite_fields:
|
||||
del self._composite_fields[field_name]
|
||||
self._cmsg.ClearField(field_name)
|
||||
|
||||
def Clear(self):
|
||||
return self._cmsg.Clear()
|
||||
|
||||
def IsInitialized(self, errors=None):
|
||||
if self._cmsg.IsInitialized():
|
||||
return True
|
||||
if errors is not None:
|
||||
errors.extend(self.FindInitializationErrors());
|
||||
return False
|
||||
|
||||
def SerializeToString(self):
|
||||
if not self.IsInitialized():
|
||||
raise message.EncodeError(
|
||||
'Message is missing required fields: ' +
|
||||
','.join(self.FindInitializationErrors()))
|
||||
return self._cmsg.SerializeToString()
|
||||
|
||||
def SerializePartialToString(self):
|
||||
return self._cmsg.SerializePartialToString()
|
||||
|
||||
def ParseFromString(self, serialized):
|
||||
self.Clear()
|
||||
self.MergeFromString(serialized)
|
||||
|
||||
def MergeFromString(self, serialized):
|
||||
byte_size = self._cmsg.MergeFromString(serialized)
|
||||
if byte_size < 0:
|
||||
raise message.DecodeError('Unable to merge from string.')
|
||||
return byte_size
|
||||
|
||||
def MergeFrom(self, msg):
|
||||
if not isinstance(msg, cls):
|
||||
raise TypeError(
|
||||
"Parameter to MergeFrom() must be instance of same class.")
|
||||
self._cmsg.MergeFrom(msg._cmsg)
|
||||
|
||||
def CopyFrom(self, msg):
|
||||
self._cmsg.CopyFrom(msg._cmsg)
|
||||
|
||||
def ByteSize(self):
|
||||
return self._cmsg.ByteSize()
|
||||
|
||||
def SetInParent(self):
|
||||
return self._cmsg.SetInParent()
|
||||
|
||||
def ListFields(self):
|
||||
all_fields = []
|
||||
field_list = self._cmsg.ListFields()
|
||||
fields_by_name = cls.DESCRIPTOR.fields_by_name
|
||||
for is_extension, field_name in field_list:
|
||||
if is_extension:
|
||||
extension = cls._extensions_by_name[field_name]
|
||||
all_fields.append((extension, self.Extensions[extension]))
|
||||
else:
|
||||
field_descriptor = fields_by_name[field_name]
|
||||
all_fields.append(
|
||||
(field_descriptor, getattr(self, field_name)))
|
||||
all_fields.sort(key=lambda item: item[0].number)
|
||||
return all_fields
|
||||
|
||||
def FindInitializationErrors(self):
|
||||
return self._cmsg.FindInitializationErrors()
|
||||
|
||||
def __str__(self):
|
||||
return self._cmsg.DebugString()
|
||||
|
||||
def __eq__(self, other):
|
||||
if self is other:
|
||||
return True
|
||||
if not isinstance(other, self.__class__):
|
||||
return False
|
||||
return self.ListFields() == other.ListFields()
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __hash__(self):
|
||||
raise TypeError('unhashable object')
|
||||
|
||||
def __unicode__(self):
|
||||
return text_format.MessageToString(self, as_utf8=True).decode('utf-8')
|
||||
|
||||
# Attach the local methods to the message class.
|
||||
for key, value in locals().copy().iteritems():
|
||||
if key not in ('key', 'value', '__builtins__', '__name__', '__doc__'):
|
||||
setattr(cls, key, value)
|
||||
|
||||
# Static methods:
|
||||
|
||||
def RegisterExtension(extension_handle):
|
||||
extension_handle.containing_type = cls.DESCRIPTOR
|
||||
cls._extensions_by_name[extension_handle.full_name] = extension_handle
|
||||
|
||||
if _IsMessageSetExtension(extension_handle):
|
||||
# MessageSet extension. Also register under type name.
|
||||
cls._extensions_by_name[
|
||||
extension_handle.message_type.full_name] = extension_handle
|
||||
cls.RegisterExtension = staticmethod(RegisterExtension)
|
||||
|
||||
def FromString(string):
|
||||
msg = cls()
|
||||
msg.MergeFromString(string)
|
||||
return msg
|
||||
cls.FromString = staticmethod(FromString)
|
||||
|
||||
|
||||
|
||||
def _AddPropertiesForExtensions(message_descriptor, cls):
|
||||
"""Adds properties for all fields in this protocol message type."""
|
||||
extension_dict = message_descriptor.extensions_by_name
|
||||
for extension_name, extension_field in extension_dict.iteritems():
|
||||
constant_name = extension_name.upper() + '_FIELD_NUMBER'
|
||||
setattr(cls, constant_name, extension_field.number)
|
@ -86,6 +86,13 @@ from google.protobuf.internal import wire_format
|
||||
from google.protobuf import message
|
||||
|
||||
|
||||
# This will overflow and thus become IEEE-754 "infinity". We would use
|
||||
# "float('inf')" but it doesn't work on Windows pre-Python-2.6.
|
||||
_POS_INF = 1e10000
|
||||
_NEG_INF = -_POS_INF
|
||||
_NAN = _POS_INF * 0
|
||||
|
||||
|
||||
# This is not for optimization, but rather to avoid conflicts with local
|
||||
# variables named "message".
|
||||
_DecodeError = message.DecodeError
|
||||
@ -269,6 +276,72 @@ def _StructPackDecoder(wire_type, format):
|
||||
return _SimpleDecoder(wire_type, InnerDecode)
|
||||
|
||||
|
||||
def _FloatDecoder():
|
||||
"""Returns a decoder for a float field.
|
||||
|
||||
This code works around a bug in struct.unpack for non-finite 32-bit
|
||||
floating-point values.
|
||||
"""
|
||||
|
||||
local_unpack = struct.unpack
|
||||
|
||||
def InnerDecode(buffer, pos):
|
||||
# We expect a 32-bit value in little-endian byte order. Bit 1 is the sign
|
||||
# bit, bits 2-9 represent the exponent, and bits 10-32 are the significand.
|
||||
new_pos = pos + 4
|
||||
float_bytes = buffer[pos:new_pos]
|
||||
|
||||
# If this value has all its exponent bits set, then it's non-finite.
|
||||
# In Python 2.4, struct.unpack will convert it to a finite 64-bit value.
|
||||
# To avoid that, we parse it specially.
|
||||
if ((float_bytes[3] in '\x7F\xFF')
|
||||
and (float_bytes[2] >= '\x80')):
|
||||
# If at least one significand bit is set...
|
||||
if float_bytes[0:3] != '\x00\x00\x80':
|
||||
return (_NAN, new_pos)
|
||||
# If sign bit is set...
|
||||
if float_bytes[3] == '\xFF':
|
||||
return (_NEG_INF, new_pos)
|
||||
return (_POS_INF, new_pos)
|
||||
|
||||
# Note that we expect someone up-stack to catch struct.error and convert
|
||||
# it to _DecodeError -- this way we don't have to set up exception-
|
||||
# handling blocks every time we parse one value.
|
||||
result = local_unpack('<f', float_bytes)[0]
|
||||
return (result, new_pos)
|
||||
return _SimpleDecoder(wire_format.WIRETYPE_FIXED32, InnerDecode)
|
||||
|
||||
|
||||
def _DoubleDecoder():
|
||||
"""Returns a decoder for a double field.
|
||||
|
||||
This code works around a bug in struct.unpack for not-a-number.
|
||||
"""
|
||||
|
||||
local_unpack = struct.unpack
|
||||
|
||||
def InnerDecode(buffer, pos):
|
||||
# We expect a 64-bit value in little-endian byte order. Bit 1 is the sign
|
||||
# bit, bits 2-12 represent the exponent, and bits 13-64 are the significand.
|
||||
new_pos = pos + 8
|
||||
double_bytes = buffer[pos:new_pos]
|
||||
|
||||
# If this value has all its exponent bits set and at least one significand
|
||||
# bit set, it's not a number. In Python 2.4, struct.unpack will treat it
|
||||
# as inf or -inf. To avoid that, we treat it specially.
|
||||
if ((double_bytes[7] in '\x7F\xFF')
|
||||
and (double_bytes[6] >= '\xF0')
|
||||
and (double_bytes[0:7] != '\x00\x00\x00\x00\x00\x00\xF0')):
|
||||
return (_NAN, new_pos)
|
||||
|
||||
# Note that we expect someone up-stack to catch struct.error and convert
|
||||
# it to _DecodeError -- this way we don't have to set up exception-
|
||||
# handling blocks every time we parse one value.
|
||||
result = local_unpack('<d', double_bytes)[0]
|
||||
return (result, new_pos)
|
||||
return _SimpleDecoder(wire_format.WIRETYPE_FIXED64, InnerDecode)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
|
||||
@ -294,8 +367,8 @@ Fixed32Decoder = _StructPackDecoder(wire_format.WIRETYPE_FIXED32, '<I')
|
||||
Fixed64Decoder = _StructPackDecoder(wire_format.WIRETYPE_FIXED64, '<Q')
|
||||
SFixed32Decoder = _StructPackDecoder(wire_format.WIRETYPE_FIXED32, '<i')
|
||||
SFixed64Decoder = _StructPackDecoder(wire_format.WIRETYPE_FIXED64, '<q')
|
||||
FloatDecoder = _StructPackDecoder(wire_format.WIRETYPE_FIXED32, '<f')
|
||||
DoubleDecoder = _StructPackDecoder(wire_format.WIRETYPE_FIXED64, '<d')
|
||||
FloatDecoder = _FloatDecoder()
|
||||
DoubleDecoder = _DoubleDecoder()
|
||||
|
||||
BoolDecoder = _ModifiedDecoder(
|
||||
wire_format.WIRETYPE_VARINT, _DecodeVarint, bool)
|
||||
|
@ -70,6 +70,12 @@ import struct
|
||||
from google.protobuf.internal import wire_format
|
||||
|
||||
|
||||
# This will overflow and thus become IEEE-754 "infinity". We would use
|
||||
# "float('inf')" but it doesn't work on Windows pre-Python-2.6.
|
||||
_POS_INF = 1e10000
|
||||
_NEG_INF = -_POS_INF
|
||||
|
||||
|
||||
def _VarintSize(value):
|
||||
"""Compute the size of a varint value."""
|
||||
if value <= 0x7f: return 1
|
||||
@ -502,6 +508,83 @@ def _StructPackEncoder(wire_type, format):
|
||||
return SpecificEncoder
|
||||
|
||||
|
||||
def _FloatingPointEncoder(wire_type, format):
|
||||
"""Return a constructor for an encoder for float fields.
|
||||
|
||||
This is like StructPackEncoder, but catches errors that may be due to
|
||||
passing non-finite floating-point values to struct.pack, and makes a
|
||||
second attempt to encode those values.
|
||||
|
||||
Args:
|
||||
wire_type: The field's wire type, for encoding tags.
|
||||
format: The format string to pass to struct.pack().
|
||||
"""
|
||||
|
||||
value_size = struct.calcsize(format)
|
||||
if value_size == 4:
|
||||
def EncodeNonFiniteOrRaise(write, value):
|
||||
# Remember that the serialized form uses little-endian byte order.
|
||||
if value == _POS_INF:
|
||||
write('\x00\x00\x80\x7F')
|
||||
elif value == _NEG_INF:
|
||||
write('\x00\x00\x80\xFF')
|
||||
elif value != value: # NaN
|
||||
write('\x00\x00\xC0\x7F')
|
||||
else:
|
||||
raise
|
||||
elif value_size == 8:
|
||||
def EncodeNonFiniteOrRaise(write, value):
|
||||
if value == _POS_INF:
|
||||
write('\x00\x00\x00\x00\x00\x00\xF0\x7F')
|
||||
elif value == _NEG_INF:
|
||||
write('\x00\x00\x00\x00\x00\x00\xF0\xFF')
|
||||
elif value != value: # NaN
|
||||
write('\x00\x00\x00\x00\x00\x00\xF8\x7F')
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
raise ValueError('Can\'t encode floating-point values that are '
|
||||
'%d bytes long (only 4 or 8)' % value_size)
|
||||
|
||||
def SpecificEncoder(field_number, is_repeated, is_packed):
|
||||
local_struct_pack = struct.pack
|
||||
if is_packed:
|
||||
tag_bytes = TagBytes(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED)
|
||||
local_EncodeVarint = _EncodeVarint
|
||||
def EncodePackedField(write, value):
|
||||
write(tag_bytes)
|
||||
local_EncodeVarint(write, len(value) * value_size)
|
||||
for element in value:
|
||||
# This try/except block is going to be faster than any code that
|
||||
# we could write to check whether element is finite.
|
||||
try:
|
||||
write(local_struct_pack(format, element))
|
||||
except SystemError:
|
||||
EncodeNonFiniteOrRaise(write, element)
|
||||
return EncodePackedField
|
||||
elif is_repeated:
|
||||
tag_bytes = TagBytes(field_number, wire_type)
|
||||
def EncodeRepeatedField(write, value):
|
||||
for element in value:
|
||||
write(tag_bytes)
|
||||
try:
|
||||
write(local_struct_pack(format, element))
|
||||
except SystemError:
|
||||
EncodeNonFiniteOrRaise(write, element)
|
||||
return EncodeRepeatedField
|
||||
else:
|
||||
tag_bytes = TagBytes(field_number, wire_type)
|
||||
def EncodeField(write, value):
|
||||
write(tag_bytes)
|
||||
try:
|
||||
write(local_struct_pack(format, value))
|
||||
except SystemError:
|
||||
EncodeNonFiniteOrRaise(write, value)
|
||||
return EncodeField
|
||||
|
||||
return SpecificEncoder
|
||||
|
||||
|
||||
# ====================================================================
|
||||
# Here we declare an encoder constructor for each field type. These work
|
||||
# very similarly to sizer constructors, described earlier.
|
||||
@ -525,8 +608,8 @@ Fixed32Encoder = _StructPackEncoder(wire_format.WIRETYPE_FIXED32, '<I')
|
||||
Fixed64Encoder = _StructPackEncoder(wire_format.WIRETYPE_FIXED64, '<Q')
|
||||
SFixed32Encoder = _StructPackEncoder(wire_format.WIRETYPE_FIXED32, '<i')
|
||||
SFixed64Encoder = _StructPackEncoder(wire_format.WIRETYPE_FIXED64, '<q')
|
||||
FloatEncoder = _StructPackEncoder(wire_format.WIRETYPE_FIXED32, '<f')
|
||||
DoubleEncoder = _StructPackEncoder(wire_format.WIRETYPE_FIXED64, '<d')
|
||||
FloatEncoder = _FloatingPointEncoder(wire_format.WIRETYPE_FIXED32, '<f')
|
||||
DoubleEncoder = _FloatingPointEncoder(wire_format.WIRETYPE_FIXED64, '<d')
|
||||
|
||||
|
||||
def BoolEncoder(field_number, is_repeated, is_packed):
|
||||
|
@ -42,11 +42,12 @@ further ensures that we can use Python protocol message objects as we expect.
|
||||
__author__ = 'robinson@google.com (Will Robinson)'
|
||||
|
||||
import unittest
|
||||
from google.protobuf import unittest_custom_options_pb2
|
||||
from google.protobuf import unittest_import_pb2
|
||||
from google.protobuf import unittest_mset_pb2
|
||||
from google.protobuf import unittest_pb2
|
||||
from google.protobuf import unittest_no_generic_services_pb2
|
||||
|
||||
from google.protobuf import service
|
||||
|
||||
MAX_EXTENSION = 536870912
|
||||
|
||||
@ -140,6 +141,13 @@ class GeneratorTest(unittest.TestCase):
|
||||
proto = unittest_mset_pb2.TestMessageSet()
|
||||
self.assertTrue(proto.DESCRIPTOR.GetOptions().message_set_wire_format)
|
||||
|
||||
def testMessageWithCustomOptions(self):
|
||||
proto = unittest_custom_options_pb2.TestMessageWithCustomOptions()
|
||||
enum_options = proto.DESCRIPTOR.enum_types_by_name['AnEnum'].GetOptions()
|
||||
self.assertTrue(enum_options is not None)
|
||||
# TODO(gps): We really should test for the presense of the enum_opt1
|
||||
# extension and for its value to be set to -789.
|
||||
|
||||
def testNestedTypes(self):
|
||||
self.assertEquals(
|
||||
set(unittest_pb2.TestAllTypes.DESCRIPTOR.nested_types),
|
||||
@ -208,12 +216,27 @@ class GeneratorTest(unittest.TestCase):
|
||||
self.assertFalse(unittest_pb2.DESCRIPTOR.serialized_pb is None)
|
||||
|
||||
def testNoGenericServices(self):
|
||||
# unittest_no_generic_services.proto should contain defs for everything
|
||||
# except services.
|
||||
self.assertTrue(hasattr(unittest_no_generic_services_pb2, "TestMessage"))
|
||||
self.assertTrue(hasattr(unittest_no_generic_services_pb2, "FOO"))
|
||||
self.assertTrue(hasattr(unittest_no_generic_services_pb2, "test_extension"))
|
||||
self.assertFalse(hasattr(unittest_no_generic_services_pb2, "TestService"))
|
||||
|
||||
# Make sure unittest_no_generic_services_pb2 has no services subclassing
|
||||
# Proto2 Service class.
|
||||
if hasattr(unittest_no_generic_services_pb2, "TestService"):
|
||||
self.assertFalse(issubclass(unittest_no_generic_services_pb2.TestService,
|
||||
service.Service))
|
||||
|
||||
def testMessageTypesByName(self):
|
||||
file_type = unittest_pb2.DESCRIPTOR
|
||||
self.assertEqual(
|
||||
unittest_pb2._TESTALLTYPES,
|
||||
file_type.message_types_by_name[unittest_pb2._TESTALLTYPES.name])
|
||||
|
||||
# Nested messages shouldn't be included in the message_types_by_name
|
||||
# dictionary (like in the C++ API).
|
||||
self.assertFalse(
|
||||
unittest_pb2._TESTALLTYPES_NESTEDMESSAGE.name in
|
||||
file_type.message_types_by_name)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -43,11 +43,25 @@ abstract interface.
|
||||
|
||||
__author__ = 'gps@google.com (Gregory P. Smith)'
|
||||
|
||||
import copy
|
||||
import math
|
||||
import unittest
|
||||
from google.protobuf import unittest_import_pb2
|
||||
from google.protobuf import unittest_pb2
|
||||
from google.protobuf.internal import test_util
|
||||
|
||||
# Python pre-2.6 does not have isinf() or isnan() functions, so we have
|
||||
# to provide our own.
|
||||
def isnan(val):
|
||||
# NaN is never equal to itself.
|
||||
return val != val
|
||||
def isinf(val):
|
||||
# Infinity times zero equals NaN.
|
||||
return not isnan(val) and isnan(val * 0)
|
||||
def IsPosInf(val):
|
||||
return isinf(val) and (val > 0)
|
||||
def IsNegInf(val):
|
||||
return isinf(val) and (val < 0)
|
||||
|
||||
class MessageTest(unittest.TestCase):
|
||||
|
||||
@ -57,6 +71,8 @@ class MessageTest(unittest.TestCase):
|
||||
golden_message.ParseFromString(golden_data)
|
||||
test_util.ExpectAllFieldsSet(self, golden_message)
|
||||
self.assertTrue(golden_message.SerializeToString() == golden_data)
|
||||
golden_copy = copy.deepcopy(golden_message)
|
||||
self.assertTrue(golden_copy.SerializeToString() == golden_data)
|
||||
|
||||
def testGoldenExtensions(self):
|
||||
golden_data = test_util.GoldenFile('golden_message').read()
|
||||
@ -66,6 +82,8 @@ class MessageTest(unittest.TestCase):
|
||||
test_util.SetAllExtensions(all_set)
|
||||
self.assertEquals(all_set, golden_message)
|
||||
self.assertTrue(golden_message.SerializeToString() == golden_data)
|
||||
golden_copy = copy.deepcopy(golden_message)
|
||||
self.assertTrue(golden_copy.SerializeToString() == golden_data)
|
||||
|
||||
def testGoldenPackedMessage(self):
|
||||
golden_data = test_util.GoldenFile('golden_packed_fields_message').read()
|
||||
@ -75,6 +93,8 @@ class MessageTest(unittest.TestCase):
|
||||
test_util.SetAllPackedFields(all_set)
|
||||
self.assertEquals(all_set, golden_message)
|
||||
self.assertTrue(all_set.SerializeToString() == golden_data)
|
||||
golden_copy = copy.deepcopy(golden_message)
|
||||
self.assertTrue(golden_copy.SerializeToString() == golden_data)
|
||||
|
||||
def testGoldenPackedExtensions(self):
|
||||
golden_data = test_util.GoldenFile('golden_packed_fields_message').read()
|
||||
@ -84,6 +104,240 @@ class MessageTest(unittest.TestCase):
|
||||
test_util.SetAllPackedExtensions(all_set)
|
||||
self.assertEquals(all_set, golden_message)
|
||||
self.assertTrue(all_set.SerializeToString() == golden_data)
|
||||
golden_copy = copy.deepcopy(golden_message)
|
||||
self.assertTrue(golden_copy.SerializeToString() == golden_data)
|
||||
|
||||
def testPositiveInfinity(self):
|
||||
golden_data = ('\x5D\x00\x00\x80\x7F'
|
||||
'\x61\x00\x00\x00\x00\x00\x00\xF0\x7F'
|
||||
'\xCD\x02\x00\x00\x80\x7F'
|
||||
'\xD1\x02\x00\x00\x00\x00\x00\x00\xF0\x7F')
|
||||
golden_message = unittest_pb2.TestAllTypes()
|
||||
golden_message.ParseFromString(golden_data)
|
||||
self.assertTrue(IsPosInf(golden_message.optional_float))
|
||||
self.assertTrue(IsPosInf(golden_message.optional_double))
|
||||
self.assertTrue(IsPosInf(golden_message.repeated_float[0]))
|
||||
self.assertTrue(IsPosInf(golden_message.repeated_double[0]))
|
||||
self.assertTrue(golden_message.SerializeToString() == golden_data)
|
||||
|
||||
def testNegativeInfinity(self):
|
||||
golden_data = ('\x5D\x00\x00\x80\xFF'
|
||||
'\x61\x00\x00\x00\x00\x00\x00\xF0\xFF'
|
||||
'\xCD\x02\x00\x00\x80\xFF'
|
||||
'\xD1\x02\x00\x00\x00\x00\x00\x00\xF0\xFF')
|
||||
golden_message = unittest_pb2.TestAllTypes()
|
||||
golden_message.ParseFromString(golden_data)
|
||||
self.assertTrue(IsNegInf(golden_message.optional_float))
|
||||
self.assertTrue(IsNegInf(golden_message.optional_double))
|
||||
self.assertTrue(IsNegInf(golden_message.repeated_float[0]))
|
||||
self.assertTrue(IsNegInf(golden_message.repeated_double[0]))
|
||||
self.assertTrue(golden_message.SerializeToString() == golden_data)
|
||||
|
||||
def testNotANumber(self):
|
||||
golden_data = ('\x5D\x00\x00\xC0\x7F'
|
||||
'\x61\x00\x00\x00\x00\x00\x00\xF8\x7F'
|
||||
'\xCD\x02\x00\x00\xC0\x7F'
|
||||
'\xD1\x02\x00\x00\x00\x00\x00\x00\xF8\x7F')
|
||||
golden_message = unittest_pb2.TestAllTypes()
|
||||
golden_message.ParseFromString(golden_data)
|
||||
self.assertTrue(isnan(golden_message.optional_float))
|
||||
self.assertTrue(isnan(golden_message.optional_double))
|
||||
self.assertTrue(isnan(golden_message.repeated_float[0]))
|
||||
self.assertTrue(isnan(golden_message.repeated_double[0]))
|
||||
self.assertTrue(golden_message.SerializeToString() == golden_data)
|
||||
|
||||
def testPositiveInfinityPacked(self):
|
||||
golden_data = ('\xA2\x06\x04\x00\x00\x80\x7F'
|
||||
'\xAA\x06\x08\x00\x00\x00\x00\x00\x00\xF0\x7F')
|
||||
golden_message = unittest_pb2.TestPackedTypes()
|
||||
golden_message.ParseFromString(golden_data)
|
||||
self.assertTrue(IsPosInf(golden_message.packed_float[0]))
|
||||
self.assertTrue(IsPosInf(golden_message.packed_double[0]))
|
||||
self.assertTrue(golden_message.SerializeToString() == golden_data)
|
||||
|
||||
def testNegativeInfinityPacked(self):
|
||||
golden_data = ('\xA2\x06\x04\x00\x00\x80\xFF'
|
||||
'\xAA\x06\x08\x00\x00\x00\x00\x00\x00\xF0\xFF')
|
||||
golden_message = unittest_pb2.TestPackedTypes()
|
||||
golden_message.ParseFromString(golden_data)
|
||||
self.assertTrue(IsNegInf(golden_message.packed_float[0]))
|
||||
self.assertTrue(IsNegInf(golden_message.packed_double[0]))
|
||||
self.assertTrue(golden_message.SerializeToString() == golden_data)
|
||||
|
||||
def testNotANumberPacked(self):
|
||||
golden_data = ('\xA2\x06\x04\x00\x00\xC0\x7F'
|
||||
'\xAA\x06\x08\x00\x00\x00\x00\x00\x00\xF8\x7F')
|
||||
golden_message = unittest_pb2.TestPackedTypes()
|
||||
golden_message.ParseFromString(golden_data)
|
||||
self.assertTrue(isnan(golden_message.packed_float[0]))
|
||||
self.assertTrue(isnan(golden_message.packed_double[0]))
|
||||
self.assertTrue(golden_message.SerializeToString() == golden_data)
|
||||
|
||||
def testExtremeFloatValues(self):
|
||||
message = unittest_pb2.TestAllTypes()
|
||||
|
||||
# Most positive exponent, no significand bits set.
|
||||
kMostPosExponentNoSigBits = math.pow(2, 127)
|
||||
message.optional_float = kMostPosExponentNoSigBits
|
||||
message.ParseFromString(message.SerializeToString())
|
||||
self.assertTrue(message.optional_float == kMostPosExponentNoSigBits)
|
||||
|
||||
# Most positive exponent, one significand bit set.
|
||||
kMostPosExponentOneSigBit = 1.5 * math.pow(2, 127)
|
||||
message.optional_float = kMostPosExponentOneSigBit
|
||||
message.ParseFromString(message.SerializeToString())
|
||||
self.assertTrue(message.optional_float == kMostPosExponentOneSigBit)
|
||||
|
||||
# Repeat last two cases with values of same magnitude, but negative.
|
||||
message.optional_float = -kMostPosExponentNoSigBits
|
||||
message.ParseFromString(message.SerializeToString())
|
||||
self.assertTrue(message.optional_float == -kMostPosExponentNoSigBits)
|
||||
|
||||
message.optional_float = -kMostPosExponentOneSigBit
|
||||
message.ParseFromString(message.SerializeToString())
|
||||
self.assertTrue(message.optional_float == -kMostPosExponentOneSigBit)
|
||||
|
||||
# Most negative exponent, no significand bits set.
|
||||
kMostNegExponentNoSigBits = math.pow(2, -127)
|
||||
message.optional_float = kMostNegExponentNoSigBits
|
||||
message.ParseFromString(message.SerializeToString())
|
||||
self.assertTrue(message.optional_float == kMostNegExponentNoSigBits)
|
||||
|
||||
# Most negative exponent, one significand bit set.
|
||||
kMostNegExponentOneSigBit = 1.5 * math.pow(2, -127)
|
||||
message.optional_float = kMostNegExponentOneSigBit
|
||||
message.ParseFromString(message.SerializeToString())
|
||||
self.assertTrue(message.optional_float == kMostNegExponentOneSigBit)
|
||||
|
||||
# Repeat last two cases with values of the same magnitude, but negative.
|
||||
message.optional_float = -kMostNegExponentNoSigBits
|
||||
message.ParseFromString(message.SerializeToString())
|
||||
self.assertTrue(message.optional_float == -kMostNegExponentNoSigBits)
|
||||
|
||||
message.optional_float = -kMostNegExponentOneSigBit
|
||||
message.ParseFromString(message.SerializeToString())
|
||||
self.assertTrue(message.optional_float == -kMostNegExponentOneSigBit)
|
||||
|
||||
def testExtremeFloatValues(self):
|
||||
message = unittest_pb2.TestAllTypes()
|
||||
|
||||
# Most positive exponent, no significand bits set.
|
||||
kMostPosExponentNoSigBits = math.pow(2, 1023)
|
||||
message.optional_double = kMostPosExponentNoSigBits
|
||||
message.ParseFromString(message.SerializeToString())
|
||||
self.assertTrue(message.optional_double == kMostPosExponentNoSigBits)
|
||||
|
||||
# Most positive exponent, one significand bit set.
|
||||
kMostPosExponentOneSigBit = 1.5 * math.pow(2, 1023)
|
||||
message.optional_double = kMostPosExponentOneSigBit
|
||||
message.ParseFromString(message.SerializeToString())
|
||||
self.assertTrue(message.optional_double == kMostPosExponentOneSigBit)
|
||||
|
||||
# Repeat last two cases with values of same magnitude, but negative.
|
||||
message.optional_double = -kMostPosExponentNoSigBits
|
||||
message.ParseFromString(message.SerializeToString())
|
||||
self.assertTrue(message.optional_double == -kMostPosExponentNoSigBits)
|
||||
|
||||
message.optional_double = -kMostPosExponentOneSigBit
|
||||
message.ParseFromString(message.SerializeToString())
|
||||
self.assertTrue(message.optional_double == -kMostPosExponentOneSigBit)
|
||||
|
||||
# Most negative exponent, no significand bits set.
|
||||
kMostNegExponentNoSigBits = math.pow(2, -1023)
|
||||
message.optional_double = kMostNegExponentNoSigBits
|
||||
message.ParseFromString(message.SerializeToString())
|
||||
self.assertTrue(message.optional_double == kMostNegExponentNoSigBits)
|
||||
|
||||
# Most negative exponent, one significand bit set.
|
||||
kMostNegExponentOneSigBit = 1.5 * math.pow(2, -1023)
|
||||
message.optional_double = kMostNegExponentOneSigBit
|
||||
message.ParseFromString(message.SerializeToString())
|
||||
self.assertTrue(message.optional_double == kMostNegExponentOneSigBit)
|
||||
|
||||
# Repeat last two cases with values of the same magnitude, but negative.
|
||||
message.optional_double = -kMostNegExponentNoSigBits
|
||||
message.ParseFromString(message.SerializeToString())
|
||||
self.assertTrue(message.optional_double == -kMostNegExponentNoSigBits)
|
||||
|
||||
message.optional_double = -kMostNegExponentOneSigBit
|
||||
message.ParseFromString(message.SerializeToString())
|
||||
self.assertTrue(message.optional_double == -kMostNegExponentOneSigBit)
|
||||
|
||||
def testSortingRepeatedScalarFieldsDefaultComparator(self):
|
||||
"""Check some different types with the default comparator."""
|
||||
message = unittest_pb2.TestAllTypes()
|
||||
|
||||
# TODO(mattp): would testing more scalar types strengthen test?
|
||||
message.repeated_int32.append(1)
|
||||
message.repeated_int32.append(3)
|
||||
message.repeated_int32.append(2)
|
||||
message.repeated_int32.sort()
|
||||
self.assertEqual(message.repeated_int32[0], 1)
|
||||
self.assertEqual(message.repeated_int32[1], 2)
|
||||
self.assertEqual(message.repeated_int32[2], 3)
|
||||
|
||||
message.repeated_float.append(1.1)
|
||||
message.repeated_float.append(1.3)
|
||||
message.repeated_float.append(1.2)
|
||||
message.repeated_float.sort()
|
||||
self.assertAlmostEqual(message.repeated_float[0], 1.1)
|
||||
self.assertAlmostEqual(message.repeated_float[1], 1.2)
|
||||
self.assertAlmostEqual(message.repeated_float[2], 1.3)
|
||||
|
||||
message.repeated_string.append('a')
|
||||
message.repeated_string.append('c')
|
||||
message.repeated_string.append('b')
|
||||
message.repeated_string.sort()
|
||||
self.assertEqual(message.repeated_string[0], 'a')
|
||||
self.assertEqual(message.repeated_string[1], 'b')
|
||||
self.assertEqual(message.repeated_string[2], 'c')
|
||||
|
||||
message.repeated_bytes.append('a')
|
||||
message.repeated_bytes.append('c')
|
||||
message.repeated_bytes.append('b')
|
||||
message.repeated_bytes.sort()
|
||||
self.assertEqual(message.repeated_bytes[0], 'a')
|
||||
self.assertEqual(message.repeated_bytes[1], 'b')
|
||||
self.assertEqual(message.repeated_bytes[2], 'c')
|
||||
|
||||
def testSortingRepeatedScalarFieldsCustomComparator(self):
|
||||
"""Check some different types with custom comparator."""
|
||||
message = unittest_pb2.TestAllTypes()
|
||||
|
||||
message.repeated_int32.append(-3)
|
||||
message.repeated_int32.append(-2)
|
||||
message.repeated_int32.append(-1)
|
||||
message.repeated_int32.sort(lambda x,y: cmp(abs(x), abs(y)))
|
||||
self.assertEqual(message.repeated_int32[0], -1)
|
||||
self.assertEqual(message.repeated_int32[1], -2)
|
||||
self.assertEqual(message.repeated_int32[2], -3)
|
||||
|
||||
message.repeated_string.append('aaa')
|
||||
message.repeated_string.append('bb')
|
||||
message.repeated_string.append('c')
|
||||
message.repeated_string.sort(lambda x,y: cmp(len(x), len(y)))
|
||||
self.assertEqual(message.repeated_string[0], 'c')
|
||||
self.assertEqual(message.repeated_string[1], 'bb')
|
||||
self.assertEqual(message.repeated_string[2], 'aaa')
|
||||
|
||||
def testSortingRepeatedCompositeFieldsCustomComparator(self):
|
||||
"""Check passing a custom comparator to sort a repeated composite field."""
|
||||
message = unittest_pb2.TestAllTypes()
|
||||
|
||||
message.repeated_nested_message.add().bb = 1
|
||||
message.repeated_nested_message.add().bb = 3
|
||||
message.repeated_nested_message.add().bb = 2
|
||||
message.repeated_nested_message.add().bb = 6
|
||||
message.repeated_nested_message.add().bb = 5
|
||||
message.repeated_nested_message.add().bb = 4
|
||||
message.repeated_nested_message.sort(lambda x,y: cmp(x.bb, y.bb))
|
||||
self.assertEqual(message.repeated_nested_message[0].bb, 1)
|
||||
self.assertEqual(message.repeated_nested_message[1].bb, 2)
|
||||
self.assertEqual(message.repeated_nested_message[2].bb, 3)
|
||||
self.assertEqual(message.repeated_nested_message[3].bb, 4)
|
||||
self.assertEqual(message.repeated_nested_message[4].bb, 5)
|
||||
self.assertEqual(message.repeated_nested_message[5].bb, 6)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
1098
python/google/protobuf/internal/python_message.py
Executable file
1098
python/google/protobuf/internal/python_message.py
Executable file
File diff suppressed because it is too large
Load Diff
@ -41,8 +41,6 @@ import operator
|
||||
import struct
|
||||
|
||||
import unittest
|
||||
# TODO(robinson): When we split this test in two, only some of these imports
|
||||
# will be necessary in each test.
|
||||
from google.protobuf import unittest_import_pb2
|
||||
from google.protobuf import unittest_mset_pb2
|
||||
from google.protobuf import unittest_pb2
|
||||
@ -50,6 +48,7 @@ from google.protobuf import descriptor_pb2
|
||||
from google.protobuf import descriptor
|
||||
from google.protobuf import message
|
||||
from google.protobuf import reflection
|
||||
from google.protobuf.internal import api_implementation
|
||||
from google.protobuf.internal import more_extensions_pb2
|
||||
from google.protobuf.internal import more_messages_pb2
|
||||
from google.protobuf.internal import wire_format
|
||||
@ -104,10 +103,10 @@ class _MiniDecoder(object):
|
||||
|
||||
class ReflectionTest(unittest.TestCase):
|
||||
|
||||
def assertIs(self, values, others):
|
||||
def assertListsEqual(self, values, others):
|
||||
self.assertEqual(len(values), len(others))
|
||||
for i in range(len(values)):
|
||||
self.assertTrue(values[i] is others[i])
|
||||
self.assertEqual(values[i], others[i])
|
||||
|
||||
def testScalarConstructor(self):
|
||||
# Constructor with only scalar types should succeed.
|
||||
@ -201,15 +200,24 @@ class ReflectionTest(unittest.TestCase):
|
||||
list(proto.repeated_foreign_message))
|
||||
|
||||
def testConstructorTypeError(self):
|
||||
self.assertRaises(TypeError, unittest_pb2.TestAllTypes, optional_int32="foo")
|
||||
self.assertRaises(TypeError, unittest_pb2.TestAllTypes, optional_string=1234)
|
||||
self.assertRaises(TypeError, unittest_pb2.TestAllTypes, optional_nested_message=1234)
|
||||
self.assertRaises(TypeError, unittest_pb2.TestAllTypes, repeated_int32=1234)
|
||||
self.assertRaises(TypeError, unittest_pb2.TestAllTypes, repeated_int32=["foo"])
|
||||
self.assertRaises(TypeError, unittest_pb2.TestAllTypes, repeated_string=1234)
|
||||
self.assertRaises(TypeError, unittest_pb2.TestAllTypes, repeated_string=[1234])
|
||||
self.assertRaises(TypeError, unittest_pb2.TestAllTypes, repeated_nested_message=1234)
|
||||
self.assertRaises(TypeError, unittest_pb2.TestAllTypes, repeated_nested_message=[1234])
|
||||
self.assertRaises(
|
||||
TypeError, unittest_pb2.TestAllTypes, optional_int32="foo")
|
||||
self.assertRaises(
|
||||
TypeError, unittest_pb2.TestAllTypes, optional_string=1234)
|
||||
self.assertRaises(
|
||||
TypeError, unittest_pb2.TestAllTypes, optional_nested_message=1234)
|
||||
self.assertRaises(
|
||||
TypeError, unittest_pb2.TestAllTypes, repeated_int32=1234)
|
||||
self.assertRaises(
|
||||
TypeError, unittest_pb2.TestAllTypes, repeated_int32=["foo"])
|
||||
self.assertRaises(
|
||||
TypeError, unittest_pb2.TestAllTypes, repeated_string=1234)
|
||||
self.assertRaises(
|
||||
TypeError, unittest_pb2.TestAllTypes, repeated_string=[1234])
|
||||
self.assertRaises(
|
||||
TypeError, unittest_pb2.TestAllTypes, repeated_nested_message=1234)
|
||||
self.assertRaises(
|
||||
TypeError, unittest_pb2.TestAllTypes, repeated_nested_message=[1234])
|
||||
|
||||
def testConstructorInvalidatesCachedByteSize(self):
|
||||
message = unittest_pb2.TestAllTypes(optional_int32 = 12)
|
||||
@ -311,11 +319,14 @@ class ReflectionTest(unittest.TestCase):
|
||||
self.assertEqual(0, getattr(composite_field, scalar_field_name))
|
||||
|
||||
# Finally, ensure that modifications to the old composite field object
|
||||
# don't have any effect on the parent.
|
||||
# don't have any effect on the parent. Possible only with the pure-python
|
||||
# implementation of the API.
|
||||
#
|
||||
# (NOTE that when we clear the composite field in the parent, we actually
|
||||
# don't recursively clear down the tree. Instead, we just disconnect the
|
||||
# cleared composite from the tree.)
|
||||
if api_implementation.Type() != 'python':
|
||||
return
|
||||
self.assertTrue(old_composite_field is not composite_field)
|
||||
setattr(old_composite_field, scalar_field_name, new_val)
|
||||
self.assertTrue(not composite_field.HasField(scalar_field_name))
|
||||
@ -337,6 +348,8 @@ class ReflectionTest(unittest.TestCase):
|
||||
nested.bb = 23
|
||||
|
||||
def testDisconnectingNestedMessageBeforeSettingField(self):
|
||||
if api_implementation.Type() != 'python':
|
||||
return
|
||||
proto = unittest_pb2.TestAllTypes()
|
||||
nested = proto.optional_nested_message
|
||||
proto.ClearField('optional_nested_message') # Should disconnect from parent
|
||||
@ -526,7 +539,6 @@ class ReflectionTest(unittest.TestCase):
|
||||
# proto.nonexistent_field = 23 should fail as well.
|
||||
self.assertRaises(AttributeError, setattr, proto, 'nonexistent_field', 23)
|
||||
|
||||
# TODO(robinson): Add type-safety check for enums.
|
||||
def testSingleScalarTypeSafety(self):
|
||||
proto = unittest_pb2.TestAllTypes()
|
||||
self.assertRaises(TypeError, setattr, proto, 'optional_int32', 1.1)
|
||||
@ -538,7 +550,9 @@ class ReflectionTest(unittest.TestCase):
|
||||
def TestMinAndMaxIntegers(field_name, expected_min, expected_max):
|
||||
pb = unittest_pb2.TestAllTypes()
|
||||
setattr(pb, field_name, expected_min)
|
||||
self.assertEqual(expected_min, getattr(pb, field_name))
|
||||
setattr(pb, field_name, expected_max)
|
||||
self.assertEqual(expected_max, getattr(pb, field_name))
|
||||
self.assertRaises(ValueError, setattr, pb, field_name, expected_min - 1)
|
||||
self.assertRaises(ValueError, setattr, pb, field_name, expected_max + 1)
|
||||
|
||||
@ -546,7 +560,33 @@ class ReflectionTest(unittest.TestCase):
|
||||
TestMinAndMaxIntegers('optional_uint32', 0, 0xffffffff)
|
||||
TestMinAndMaxIntegers('optional_int64', -(1 << 63), (1 << 63) - 1)
|
||||
TestMinAndMaxIntegers('optional_uint64', 0, 0xffffffffffffffff)
|
||||
TestMinAndMaxIntegers('optional_nested_enum', -(1 << 31), (1 << 31) - 1)
|
||||
|
||||
pb = unittest_pb2.TestAllTypes()
|
||||
pb.optional_nested_enum = 1
|
||||
self.assertEqual(1, pb.optional_nested_enum)
|
||||
|
||||
# Invalid enum values.
|
||||
pb.optional_nested_enum = 0
|
||||
self.assertEqual(0, pb.optional_nested_enum)
|
||||
|
||||
bytes_size_before = pb.ByteSize()
|
||||
|
||||
pb.optional_nested_enum = 4
|
||||
self.assertEqual(4, pb.optional_nested_enum)
|
||||
|
||||
pb.optional_nested_enum = 0
|
||||
self.assertEqual(0, pb.optional_nested_enum)
|
||||
|
||||
# Make sure that setting the same enum field doesn't just add unknown
|
||||
# fields (but overwrites them).
|
||||
self.assertEqual(bytes_size_before, pb.ByteSize())
|
||||
|
||||
# Is the invalid value preserved after serialization?
|
||||
serialized = pb.SerializeToString()
|
||||
pb2 = unittest_pb2.TestAllTypes()
|
||||
pb2.ParseFromString(serialized)
|
||||
self.assertEqual(0, pb2.optional_nested_enum)
|
||||
self.assertEqual(pb, pb2)
|
||||
|
||||
def testRepeatedScalarTypeSafety(self):
|
||||
proto = unittest_pb2.TestAllTypes()
|
||||
@ -560,11 +600,19 @@ class ReflectionTest(unittest.TestCase):
|
||||
self.assertRaises(IndexError, proto.repeated_int32.__setitem__, 500, 23)
|
||||
self.assertRaises(TypeError, proto.repeated_int32.__setitem__, 0, 'abc')
|
||||
|
||||
# Repeated enums tests.
|
||||
#proto.repeated_nested_enum.append(0)
|
||||
|
||||
def testSingleScalarGettersAndSetters(self):
|
||||
proto = unittest_pb2.TestAllTypes()
|
||||
self.assertEqual(0, proto.optional_int32)
|
||||
proto.optional_int32 = 1
|
||||
self.assertEqual(1, proto.optional_int32)
|
||||
|
||||
proto.optional_uint64 = 0xffffffffffff
|
||||
self.assertEqual(0xffffffffffff, proto.optional_uint64)
|
||||
proto.optional_uint64 = 0xffffffffffffffff
|
||||
self.assertEqual(0xffffffffffffffff, proto.optional_uint64)
|
||||
# TODO(robinson): Test all other scalar field types.
|
||||
|
||||
def testSingleScalarClearField(self):
|
||||
@ -645,11 +693,38 @@ class ReflectionTest(unittest.TestCase):
|
||||
del proto.repeated_int32[2:]
|
||||
self.assertEqual([5, 35], proto.repeated_int32)
|
||||
|
||||
# Test extending.
|
||||
proto.repeated_int32.extend([3, 13])
|
||||
self.assertEqual([5, 35, 3, 13], proto.repeated_int32)
|
||||
|
||||
# Test clearing.
|
||||
proto.ClearField('repeated_int32')
|
||||
self.assertTrue(not proto.repeated_int32)
|
||||
self.assertEqual(0, len(proto.repeated_int32))
|
||||
|
||||
proto.repeated_int32.append(1)
|
||||
self.assertEqual(1, proto.repeated_int32[-1])
|
||||
# Test assignment to a negative index.
|
||||
proto.repeated_int32[-1] = 2
|
||||
self.assertEqual(2, proto.repeated_int32[-1])
|
||||
|
||||
# Test deletion at negative indices.
|
||||
proto.repeated_int32[:] = [0, 1, 2, 3]
|
||||
del proto.repeated_int32[-1]
|
||||
self.assertEqual([0, 1, 2], proto.repeated_int32)
|
||||
|
||||
del proto.repeated_int32[-2]
|
||||
self.assertEqual([0, 2], proto.repeated_int32)
|
||||
|
||||
self.assertRaises(IndexError, proto.repeated_int32.__delitem__, -3)
|
||||
self.assertRaises(IndexError, proto.repeated_int32.__delitem__, 300)
|
||||
|
||||
del proto.repeated_int32[-2:-1]
|
||||
self.assertEqual([2], proto.repeated_int32)
|
||||
|
||||
del proto.repeated_int32[100:10000]
|
||||
self.assertEqual([2], proto.repeated_int32)
|
||||
|
||||
def testRepeatedScalarsRemove(self):
|
||||
proto = unittest_pb2.TestAllTypes()
|
||||
|
||||
@ -687,7 +762,7 @@ class ReflectionTest(unittest.TestCase):
|
||||
m1 = proto.repeated_nested_message.add()
|
||||
self.assertTrue(proto.repeated_nested_message)
|
||||
self.assertEqual(2, len(proto.repeated_nested_message))
|
||||
self.assertIs([m0, m1], proto.repeated_nested_message)
|
||||
self.assertListsEqual([m0, m1], proto.repeated_nested_message)
|
||||
self.assertTrue(isinstance(m0, unittest_pb2.TestAllTypes.NestedMessage))
|
||||
|
||||
# Test out-of-bounds indices.
|
||||
@ -706,32 +781,57 @@ class ReflectionTest(unittest.TestCase):
|
||||
m2 = proto.repeated_nested_message.add()
|
||||
m3 = proto.repeated_nested_message.add()
|
||||
m4 = proto.repeated_nested_message.add()
|
||||
self.assertIs([m1, m2, m3], proto.repeated_nested_message[1:4])
|
||||
self.assertIs([m0, m1, m2, m3, m4], proto.repeated_nested_message[:])
|
||||
self.assertListsEqual(
|
||||
[m1, m2, m3], proto.repeated_nested_message[1:4])
|
||||
self.assertListsEqual(
|
||||
[m0, m1, m2, m3, m4], proto.repeated_nested_message[:])
|
||||
self.assertListsEqual(
|
||||
[m0, m1], proto.repeated_nested_message[:2])
|
||||
self.assertListsEqual(
|
||||
[m2, m3, m4], proto.repeated_nested_message[2:])
|
||||
self.assertEqual(
|
||||
m0, proto.repeated_nested_message[0])
|
||||
self.assertListsEqual(
|
||||
[m0], proto.repeated_nested_message[:1])
|
||||
|
||||
# Test that we can use the field as an iterator.
|
||||
result = []
|
||||
for i in proto.repeated_nested_message:
|
||||
result.append(i)
|
||||
self.assertIs([m0, m1, m2, m3, m4], result)
|
||||
self.assertListsEqual([m0, m1, m2, m3, m4], result)
|
||||
|
||||
# Test single deletion.
|
||||
del proto.repeated_nested_message[2]
|
||||
self.assertIs([m0, m1, m3, m4], proto.repeated_nested_message)
|
||||
self.assertListsEqual([m0, m1, m3, m4], proto.repeated_nested_message)
|
||||
|
||||
# Test slice deletion.
|
||||
del proto.repeated_nested_message[2:]
|
||||
self.assertIs([m0, m1], proto.repeated_nested_message)
|
||||
self.assertListsEqual([m0, m1], proto.repeated_nested_message)
|
||||
|
||||
# Test extending.
|
||||
n1 = unittest_pb2.TestAllTypes.NestedMessage(bb=1)
|
||||
n2 = unittest_pb2.TestAllTypes.NestedMessage(bb=2)
|
||||
proto.repeated_nested_message.extend([n1,n2])
|
||||
self.assertEqual(4, len(proto.repeated_nested_message))
|
||||
self.assertEqual(n1, proto.repeated_nested_message[2])
|
||||
self.assertEqual(n2, proto.repeated_nested_message[3])
|
||||
|
||||
# Test clearing.
|
||||
proto.ClearField('repeated_nested_message')
|
||||
self.assertTrue(not proto.repeated_nested_message)
|
||||
self.assertEqual(0, len(proto.repeated_nested_message))
|
||||
|
||||
# Test constructing an element while adding it.
|
||||
proto.repeated_nested_message.add(bb=23)
|
||||
self.assertEqual(1, len(proto.repeated_nested_message))
|
||||
self.assertEqual(23, proto.repeated_nested_message[0].bb)
|
||||
|
||||
def testHandWrittenReflection(self):
|
||||
# TODO(robinson): We probably need a better way to specify
|
||||
# protocol types by hand. But then again, this isn't something
|
||||
# we expect many people to do. Hmm.
|
||||
# Hand written extensions are only supported by the pure-Python
|
||||
# implementation of the API.
|
||||
if api_implementation.Type() != 'python':
|
||||
return
|
||||
|
||||
FieldDescriptor = descriptor.FieldDescriptor
|
||||
foo_field_descriptor = FieldDescriptor(
|
||||
name='foo_field', full_name='MyProto.foo_field',
|
||||
@ -894,7 +994,7 @@ class ReflectionTest(unittest.TestCase):
|
||||
self.assertTrue(not toplevel.HasField('submessage'))
|
||||
foreign = toplevel.submessage.Extensions[
|
||||
more_extensions_pb2.repeated_message_extension].add()
|
||||
self.assertTrue(foreign is toplevel.submessage.Extensions[
|
||||
self.assertEqual(foreign, toplevel.submessage.Extensions[
|
||||
more_extensions_pb2.repeated_message_extension][0])
|
||||
self.assertTrue(toplevel.HasField('submessage'))
|
||||
|
||||
@ -997,6 +1097,12 @@ class ReflectionTest(unittest.TestCase):
|
||||
self.assertEqual(123, proto2.repeated_nested_message[1].bb)
|
||||
self.assertEqual(321, proto2.repeated_nested_message[2].bb)
|
||||
|
||||
proto3 = unittest_pb2.TestAllTypes()
|
||||
proto3.repeated_nested_message.MergeFrom(proto2.repeated_nested_message)
|
||||
self.assertEqual(999, proto3.repeated_nested_message[0].bb)
|
||||
self.assertEqual(123, proto3.repeated_nested_message[1].bb)
|
||||
self.assertEqual(321, proto3.repeated_nested_message[2].bb)
|
||||
|
||||
def testMergeFromAllFields(self):
|
||||
# With all fields set.
|
||||
proto1 = unittest_pb2.TestAllTypes()
|
||||
@ -1126,6 +1232,15 @@ class ReflectionTest(unittest.TestCase):
|
||||
self.assertEqual(2, proto1.optional_int32)
|
||||
self.assertEqual('important-text', proto1.optional_string)
|
||||
|
||||
def testCopyFromBadType(self):
|
||||
# The python implementation doesn't raise an exception in this
|
||||
# case. In theory it should.
|
||||
if api_implementation.Type() == 'python':
|
||||
return
|
||||
proto1 = unittest_pb2.TestAllTypes()
|
||||
proto2 = unittest_pb2.TestAllExtensions()
|
||||
self.assertRaises(TypeError, proto1.CopyFrom, proto2)
|
||||
|
||||
def testClear(self):
|
||||
proto = unittest_pb2.TestAllTypes()
|
||||
test_util.SetAllFields(proto)
|
||||
@ -1231,9 +1346,10 @@ class ReflectionTest(unittest.TestCase):
|
||||
proto.optional_string = str('Testing')
|
||||
self.assertEqual(proto.optional_string, unicode('Testing'))
|
||||
|
||||
# Values of type 'str' are also accepted as long as they can be encoded in
|
||||
# UTF-8.
|
||||
self.assertEqual(type(proto.optional_string), str)
|
||||
if api_implementation.Type() == 'python':
|
||||
# Values of type 'str' are also accepted as long as they can be
|
||||
# encoded in UTF-8.
|
||||
self.assertEqual(type(proto.optional_string), str)
|
||||
|
||||
# Try to assign a 'str' value which contains bytes that aren't 7-bit ASCII.
|
||||
self.assertRaises(ValueError,
|
||||
@ -1271,7 +1387,7 @@ class ReflectionTest(unittest.TestCase):
|
||||
# Check that the type_id is the same as the tag ID in the .proto file.
|
||||
self.assertEqual(raw.item[0].type_id, 1547769)
|
||||
|
||||
# Check the actually bytes on the wire.
|
||||
# Check the actual bytes on the wire.
|
||||
self.assertTrue(
|
||||
raw.item[0].message.endswith(test_utf8_bytes))
|
||||
message2.MergeFromString(raw.item[0].message)
|
||||
@ -1279,10 +1395,23 @@ class ReflectionTest(unittest.TestCase):
|
||||
self.assertEqual(type(message2.str), unicode)
|
||||
self.assertEqual(message2.str, test_utf8)
|
||||
|
||||
# How about if the bytes on the wire aren't a valid UTF-8 encoded string.
|
||||
# The pure Python API throws an exception on MergeFromString(),
|
||||
# if any of the string fields of the message can't be UTF-8 decoded.
|
||||
# The C++ implementation of the API has no way to check that on
|
||||
# MergeFromString and thus has no way to throw the exception.
|
||||
#
|
||||
# The pure Python API always returns objects of type 'unicode' (UTF-8
|
||||
# encoded), or 'str' (in 7 bit ASCII).
|
||||
bytes = raw.item[0].message.replace(
|
||||
test_utf8_bytes, len(test_utf8_bytes) * '\xff')
|
||||
self.assertRaises(UnicodeDecodeError, message2.MergeFromString, bytes)
|
||||
|
||||
unicode_decode_failed = False
|
||||
try:
|
||||
message2.MergeFromString(bytes)
|
||||
except UnicodeDecodeError, e:
|
||||
unicode_decode_failed = True
|
||||
string_field = message2.str
|
||||
self.assertTrue(unicode_decode_failed or type(string_field) == str)
|
||||
|
||||
def testEmptyNestedMessage(self):
|
||||
proto = unittest_pb2.TestAllTypes()
|
||||
@ -1325,6 +1454,9 @@ class TestAllTypesEqualityTest(unittest.TestCase):
|
||||
self.first_proto = unittest_pb2.TestAllTypes()
|
||||
self.second_proto = unittest_pb2.TestAllTypes()
|
||||
|
||||
def testNotHashable(self):
|
||||
self.assertRaises(TypeError, hash, self.first_proto)
|
||||
|
||||
def testSelfEquality(self):
|
||||
self.assertEqual(self.first_proto, self.first_proto)
|
||||
|
||||
@ -1342,6 +1474,9 @@ class FullProtosEqualityTest(unittest.TestCase):
|
||||
test_util.SetAllFields(self.first_proto)
|
||||
test_util.SetAllFields(self.second_proto)
|
||||
|
||||
def testNotHashable(self):
|
||||
self.assertRaises(TypeError, hash, self.first_proto)
|
||||
|
||||
def testNoneNotEqual(self):
|
||||
self.assertNotEqual(self.first_proto, None)
|
||||
self.assertNotEqual(None, self.second_proto)
|
||||
@ -1410,9 +1545,6 @@ class FullProtosEqualityTest(unittest.TestCase):
|
||||
self.first_proto.ClearField('optional_nested_message')
|
||||
self.second_proto.optional_nested_message.ClearField('bb')
|
||||
self.assertNotEqual(self.first_proto, self.second_proto)
|
||||
# TODO(robinson): Replace next two lines with method
|
||||
# to set the "has" bit without changing the value,
|
||||
# if/when such a method exists.
|
||||
self.first_proto.optional_nested_message.bb = 0
|
||||
self.first_proto.optional_nested_message.ClearField('bb')
|
||||
self.assertEqual(self.first_proto, self.second_proto)
|
||||
@ -1477,6 +1609,14 @@ class ByteSizeTest(unittest.TestCase):
|
||||
def testEmptyMessage(self):
|
||||
self.assertEqual(0, self.proto.ByteSize())
|
||||
|
||||
def testSizedOnKwargs(self):
|
||||
# Use a separate message to ensure testing right after creation.
|
||||
proto = unittest_pb2.TestAllTypes()
|
||||
self.assertEqual(0, proto.ByteSize())
|
||||
proto_kwargs = unittest_pb2.TestAllTypes(optional_int64 = 1)
|
||||
# One byte for the tag, one to encode varint 1.
|
||||
self.assertEqual(2, proto_kwargs.ByteSize())
|
||||
|
||||
def testVarints(self):
|
||||
def Test(i, expected_varint_size):
|
||||
self.proto.Clear()
|
||||
@ -1668,10 +1808,13 @@ class ByteSizeTest(unittest.TestCase):
|
||||
self.assertEqual(3, self.proto.ByteSize())
|
||||
self.proto.ClearField('optional_foreign_message')
|
||||
self.assertEqual(0, self.proto.ByteSize())
|
||||
child = self.proto.optional_foreign_message
|
||||
self.proto.ClearField('optional_foreign_message')
|
||||
child.c = 128
|
||||
self.assertEqual(0, self.proto.ByteSize())
|
||||
|
||||
if api_implementation.Type() == 'python':
|
||||
# This is only possible in pure-Python implementation of the API.
|
||||
child = self.proto.optional_foreign_message
|
||||
self.proto.ClearField('optional_foreign_message')
|
||||
child.c = 128
|
||||
self.assertEqual(0, self.proto.ByteSize())
|
||||
|
||||
# Test within extension.
|
||||
extension = more_extensions_pb2.optional_message_extension
|
||||
@ -1737,7 +1880,6 @@ class ByteSizeTest(unittest.TestCase):
|
||||
self.assertEqual(19, self.packed_extended_proto.ByteSize())
|
||||
|
||||
|
||||
# TODO(robinson): We need cross-language serialization consistency tests.
|
||||
# Issues to be sure to cover include:
|
||||
# * Handling of unrecognized tags ("uninterpreted_bytes").
|
||||
# * Handling of MessageSets.
|
||||
@ -1792,6 +1934,10 @@ class SerializationTest(unittest.TestCase):
|
||||
self.assertEqual(first_proto, second_proto)
|
||||
|
||||
def testParseTruncated(self):
|
||||
# This test is only applicable for the Python implementation of the API.
|
||||
if api_implementation.Type() != 'python':
|
||||
return
|
||||
|
||||
first_proto = unittest_pb2.TestAllTypes()
|
||||
test_util.SetAllFields(first_proto)
|
||||
serialized = first_proto.SerializeToString()
|
||||
|
@ -35,6 +35,7 @@
|
||||
__author__ = 'kenton@google.com (Kenton Varda)'
|
||||
|
||||
import difflib
|
||||
import re
|
||||
|
||||
import unittest
|
||||
from google.protobuf import text_format
|
||||
@ -95,12 +96,13 @@ class TextFormatTest(unittest.TestCase):
|
||||
|
||||
def testPrintExotic(self):
|
||||
message = unittest_pb2.TestAllTypes()
|
||||
message.repeated_int64.append(-9223372036854775808);
|
||||
message.repeated_uint64.append(18446744073709551615);
|
||||
message.repeated_double.append(123.456);
|
||||
message.repeated_double.append(1.23e22);
|
||||
message.repeated_double.append(1.23e-18);
|
||||
message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'\"');
|
||||
message.repeated_int64.append(-9223372036854775808)
|
||||
message.repeated_uint64.append(18446744073709551615)
|
||||
message.repeated_double.append(123.456)
|
||||
message.repeated_double.append(1.23e22)
|
||||
message.repeated_double.append(1.23e-18)
|
||||
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)),
|
||||
'repeated_int64: -9223372036854775808\n'
|
||||
@ -109,7 +111,95 @@ class TextFormatTest(unittest.TestCase):
|
||||
'repeated_double: 1.23e+22\n'
|
||||
'repeated_double: 1.23e-18\n'
|
||||
'repeated_string: '
|
||||
'\"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\\"\"\n')
|
||||
'"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n'
|
||||
'repeated_string: "\\303\\274\\352\\234\\237"\n')
|
||||
|
||||
def testPrintNestedMessageAsOneLine(self):
|
||||
message = unittest_pb2.TestAllTypes()
|
||||
msg = message.repeated_nested_message.add()
|
||||
msg.bb = 42;
|
||||
self.CompareToGoldenText(
|
||||
text_format.MessageToString(message, as_one_line=True),
|
||||
'repeated_nested_message { bb: 42 }')
|
||||
|
||||
def testPrintRepeatedFieldsAsOneLine(self):
|
||||
message = unittest_pb2.TestAllTypes()
|
||||
message.repeated_int32.append(1)
|
||||
message.repeated_int32.append(1)
|
||||
message.repeated_int32.append(3)
|
||||
message.repeated_string.append("Google")
|
||||
message.repeated_string.append("Zurich")
|
||||
self.CompareToGoldenText(
|
||||
text_format.MessageToString(message, as_one_line=True),
|
||||
'repeated_int32: 1 repeated_int32: 1 repeated_int32: 3 '
|
||||
'repeated_string: "Google" repeated_string: "Zurich"')
|
||||
|
||||
def testPrintNestedNewLineInStringAsOneLine(self):
|
||||
message = unittest_pb2.TestAllTypes()
|
||||
message.optional_string = "a\nnew\nline"
|
||||
self.CompareToGoldenText(
|
||||
text_format.MessageToString(message, as_one_line=True),
|
||||
'optional_string: "a\\nnew\\nline"')
|
||||
|
||||
def testPrintMessageSetAsOneLine(self):
|
||||
message = unittest_mset_pb2.TestMessageSetContainer()
|
||||
ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
|
||||
ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
|
||||
message.message_set.Extensions[ext1].i = 23
|
||||
message.message_set.Extensions[ext2].str = 'foo'
|
||||
self.CompareToGoldenText(
|
||||
text_format.MessageToString(message, as_one_line=True),
|
||||
'message_set {'
|
||||
' [protobuf_unittest.TestMessageSetExtension1] {'
|
||||
' i: 23'
|
||||
' }'
|
||||
' [protobuf_unittest.TestMessageSetExtension2] {'
|
||||
' str: \"foo\"'
|
||||
' }'
|
||||
' }')
|
||||
|
||||
def testPrintExoticAsOneLine(self):
|
||||
message = unittest_pb2.TestAllTypes()
|
||||
message.repeated_int64.append(-9223372036854775808)
|
||||
message.repeated_uint64.append(18446744073709551615)
|
||||
message.repeated_double.append(123.456)
|
||||
message.repeated_double.append(1.23e22)
|
||||
message.repeated_double.append(1.23e-18)
|
||||
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)),
|
||||
'repeated_int64: -9223372036854775808'
|
||||
' repeated_uint64: 18446744073709551615'
|
||||
' repeated_double: 123.456'
|
||||
' repeated_double: 1.23e+22'
|
||||
' repeated_double: 1.23e-18'
|
||||
' repeated_string: '
|
||||
'"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""'
|
||||
' repeated_string: "\\303\\274\\352\\234\\237"')
|
||||
|
||||
def testRoundTripExoticAsOneLine(self):
|
||||
message = unittest_pb2.TestAllTypes()
|
||||
message.repeated_int64.append(-9223372036854775808)
|
||||
message.repeated_uint64.append(18446744073709551615)
|
||||
message.repeated_double.append(123.456)
|
||||
message.repeated_double.append(1.23e22)
|
||||
message.repeated_double.append(1.23e-18)
|
||||
message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
|
||||
message.repeated_string.append(u'\u00fc\ua71f')
|
||||
|
||||
wire_text = text_format.MessageToString(message, as_one_line=True)
|
||||
parsed_message = unittest_pb2.TestAllTypes()
|
||||
text_format.Merge(wire_text, parsed_message)
|
||||
self.assertEquals(message, parsed_message)
|
||||
|
||||
def testPrintRawUtf8String(self):
|
||||
message = unittest_pb2.TestAllTypes()
|
||||
message.repeated_string.append(u'\u00fc\ua71f')
|
||||
self.CompareToGoldenText(
|
||||
text_format.MessageToString(message, as_utf8 = True),
|
||||
'repeated_string: "\303\274\352\234\237"\n')
|
||||
|
||||
def testMessageToString(self):
|
||||
message = unittest_pb2.ForeignMessage()
|
||||
@ -119,8 +209,12 @@ class TextFormatTest(unittest.TestCase):
|
||||
def RemoveRedundantZeros(self, text):
|
||||
# Some platforms print 1e+5 as 1e+005. This is fine, but we need to remove
|
||||
# these zeros in order to match the golden file.
|
||||
return text.replace('e+0','e+').replace('e+0','e+') \
|
||||
text = text.replace('e+0','e+').replace('e+0','e+') \
|
||||
.replace('e-0','e-').replace('e-0','e-')
|
||||
# Floating point fields are printed with .0 suffix even if they are
|
||||
# actualy integer numbers.
|
||||
text = re.compile('\.0$', re.MULTILINE).sub('', text)
|
||||
return text
|
||||
|
||||
def testMergeGolden(self):
|
||||
golden_text = '\n'.join(self.ReadGolden('text_format_unittest_data.txt'))
|
||||
@ -191,8 +285,11 @@ class TextFormatTest(unittest.TestCase):
|
||||
'repeated_double: 1.23e+22\n'
|
||||
'repeated_double: 1.23e-18\n'
|
||||
'repeated_string: \n'
|
||||
'\"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\\"\"\n'
|
||||
'repeated_string: "foo" \'corge\' "grault"')
|
||||
'"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n'
|
||||
'repeated_string: "foo" \'corge\' "grault"\n'
|
||||
'repeated_string: "\\303\\274\\352\\234\\237"\n'
|
||||
'repeated_string: "\\xc3\\xbc"\n'
|
||||
'repeated_string: "\xc3\xbc"\n')
|
||||
text_format.Merge(text, message)
|
||||
|
||||
self.assertEqual(-9223372036854775808, message.repeated_int64[0])
|
||||
@ -201,8 +298,30 @@ class TextFormatTest(unittest.TestCase):
|
||||
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])
|
||||
'\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])
|
||||
|
||||
def testMergeEmptyText(self):
|
||||
message = unittest_pb2.TestAllTypes()
|
||||
text = ''
|
||||
text_format.Merge(text, message)
|
||||
self.assertEquals(unittest_pb2.TestAllTypes(), message)
|
||||
|
||||
def testMergeInvalidUtf8(self):
|
||||
message = unittest_pb2.TestAllTypes()
|
||||
text = 'repeated_string: "\\xc3\\xc3"'
|
||||
self.assertRaises(text_format.ParseError, text_format.Merge, text, message)
|
||||
|
||||
def testMergeSingleWord(self):
|
||||
message = unittest_pb2.TestAllTypes()
|
||||
text = 'foo'
|
||||
self.assertRaisesWithMessage(
|
||||
text_format.ParseError,
|
||||
('1:1 : Message type "protobuf_unittest.TestAllTypes" has no field named '
|
||||
'"foo".'),
|
||||
text_format.Merge, text, message)
|
||||
|
||||
def testMergeUnknownField(self):
|
||||
message = unittest_pb2.TestAllTypes()
|
||||
@ -297,7 +416,8 @@ class TokenizerTest(unittest.TestCase):
|
||||
'identifiER_4 : 1.1e+2 ID5:-0.23 ID6:\'aaaa\\\'bbbb\'\n'
|
||||
'ID7 : "aa\\"bb"\n\n\n\n ID8: {A:inf B:-inf C:true D:false}\n'
|
||||
'ID9: 22 ID10: -111111111111111111 ID11: -22\n'
|
||||
'ID12: 2222222222222222222')
|
||||
'ID12: 2222222222222222222 '
|
||||
'false_bool: 0 true_BOOL:t \n true_bool1: 1 false_BOOL1:f ' )
|
||||
tokenizer = text_format._Tokenizer(text)
|
||||
methods = [(tokenizer.ConsumeIdentifier, 'identifier1'),
|
||||
':',
|
||||
@ -347,7 +467,19 @@ class TokenizerTest(unittest.TestCase):
|
||||
(tokenizer.ConsumeInt32, -22),
|
||||
(tokenizer.ConsumeIdentifier, 'ID12'),
|
||||
':',
|
||||
(tokenizer.ConsumeUint64, 2222222222222222222)]
|
||||
(tokenizer.ConsumeUint64, 2222222222222222222),
|
||||
(tokenizer.ConsumeIdentifier, 'false_bool'),
|
||||
':',
|
||||
(tokenizer.ConsumeBool, False),
|
||||
(tokenizer.ConsumeIdentifier, 'true_BOOL'),
|
||||
':',
|
||||
(tokenizer.ConsumeBool, True),
|
||||
(tokenizer.ConsumeIdentifier, 'true_bool1'),
|
||||
':',
|
||||
(tokenizer.ConsumeBool, True),
|
||||
(tokenizer.ConsumeIdentifier, 'false_BOOL1'),
|
||||
':',
|
||||
(tokenizer.ConsumeBool, False)]
|
||||
|
||||
i = 0
|
||||
while not tokenizer.AtEnd():
|
||||
|
@ -67,6 +67,11 @@ class Message(object):
|
||||
|
||||
DESCRIPTOR = None
|
||||
|
||||
def __deepcopy__(self, memo=None):
|
||||
clone = type(self)()
|
||||
clone.MergeFrom(self)
|
||||
return clone
|
||||
|
||||
def __eq__(self, other_msg):
|
||||
raise NotImplementedError
|
||||
|
||||
@ -74,9 +79,15 @@ class Message(object):
|
||||
# Can't just say self != other_msg, since that would infinitely recurse. :)
|
||||
return not self == other_msg
|
||||
|
||||
def __hash__(self):
|
||||
raise TypeError('unhashable object')
|
||||
|
||||
def __str__(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def __unicode__(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def MergeFrom(self, other_msg):
|
||||
"""Merges the contents of the specified message into current message.
|
||||
|
||||
|
1658
python/google/protobuf/pyext/python-proto2.cc
Normal file
1658
python/google/protobuf/pyext/python-proto2.cc
Normal file
File diff suppressed because it is too large
Load Diff
334
python/google/protobuf/pyext/python_descriptor.cc
Normal file
334
python/google/protobuf/pyext/python_descriptor.cc
Normal file
@ -0,0 +1,334 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Author: petar@google.com (Petar Petrov)
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include <google/protobuf/pyext/python_descriptor.h>
|
||||
#include <google/protobuf/descriptor.pb.h>
|
||||
|
||||
#define C(str) const_cast<char*>(str)
|
||||
|
||||
namespace google {
|
||||
namespace protobuf {
|
||||
namespace python {
|
||||
|
||||
static void CFieldDescriptorDealloc(CFieldDescriptor* self);
|
||||
|
||||
static google::protobuf::DescriptorPool* g_descriptor_pool = NULL;
|
||||
|
||||
static PyObject* CFieldDescriptor_GetFullName(
|
||||
CFieldDescriptor* self, void *closure) {
|
||||
Py_XINCREF(self->full_name);
|
||||
return self->full_name;
|
||||
}
|
||||
|
||||
static PyObject* CFieldDescriptor_GetName(
|
||||
CFieldDescriptor *self, void *closure) {
|
||||
Py_XINCREF(self->name);
|
||||
return self->name;
|
||||
}
|
||||
|
||||
static PyObject* CFieldDescriptor_GetCppType(
|
||||
CFieldDescriptor *self, void *closure) {
|
||||
Py_XINCREF(self->cpp_type);
|
||||
return self->cpp_type;
|
||||
}
|
||||
|
||||
static PyObject* CFieldDescriptor_GetLabel(
|
||||
CFieldDescriptor *self, void *closure) {
|
||||
Py_XINCREF(self->label);
|
||||
return self->label;
|
||||
}
|
||||
|
||||
static PyObject* CFieldDescriptor_GetID(
|
||||
CFieldDescriptor *self, void *closure) {
|
||||
Py_XINCREF(self->id);
|
||||
return self->id;
|
||||
}
|
||||
|
||||
|
||||
static PyGetSetDef CFieldDescriptorGetters[] = {
|
||||
{ C("full_name"),
|
||||
(getter)CFieldDescriptor_GetFullName, NULL, "Full name", NULL},
|
||||
{ C("name"),
|
||||
(getter)CFieldDescriptor_GetName, NULL, "last name", NULL},
|
||||
{ C("cpp_type"),
|
||||
(getter)CFieldDescriptor_GetCppType, NULL, "C++ Type", NULL},
|
||||
{ C("label"),
|
||||
(getter)CFieldDescriptor_GetLabel, NULL, "Label", NULL},
|
||||
{ C("id"),
|
||||
(getter)CFieldDescriptor_GetID, NULL, "ID", NULL},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
PyTypeObject CFieldDescriptor_Type = {
|
||||
PyObject_HEAD_INIT(&PyType_Type)
|
||||
0,
|
||||
C("google3.net.google.protobuf.python.internal."
|
||||
"_net_proto2___python."
|
||||
"CFieldDescriptor"), // tp_name
|
||||
sizeof(CFieldDescriptor), // tp_basicsize
|
||||
0, // tp_itemsize
|
||||
(destructor)CFieldDescriptorDealloc, // tp_dealloc
|
||||
0, // tp_print
|
||||
0, // tp_getattr
|
||||
0, // tp_setattr
|
||||
0, // tp_compare
|
||||
0, // tp_repr
|
||||
0, // tp_as_number
|
||||
0, // tp_as_sequence
|
||||
0, // tp_as_mapping
|
||||
0, // tp_hash
|
||||
0, // tp_call
|
||||
0, // tp_str
|
||||
0, // tp_getattro
|
||||
0, // tp_setattro
|
||||
0, // tp_as_buffer
|
||||
Py_TPFLAGS_DEFAULT, // tp_flags
|
||||
C("A Field Descriptor"), // tp_doc
|
||||
0, // tp_traverse
|
||||
0, // tp_clear
|
||||
0, // tp_richcompare
|
||||
0, // tp_weaklistoffset
|
||||
0, // tp_iter
|
||||
0, // tp_iternext
|
||||
0, // tp_methods
|
||||
0, // tp_members
|
||||
CFieldDescriptorGetters, // tp_getset
|
||||
0, // tp_base
|
||||
0, // tp_dict
|
||||
0, // tp_descr_get
|
||||
0, // tp_descr_set
|
||||
0, // tp_dictoffset
|
||||
0, // tp_init
|
||||
PyType_GenericAlloc, // tp_alloc
|
||||
PyType_GenericNew, // tp_new
|
||||
PyObject_Del, // tp_free
|
||||
};
|
||||
|
||||
static void CFieldDescriptorDealloc(CFieldDescriptor* self) {
|
||||
Py_DECREF(self->full_name);
|
||||
Py_DECREF(self->name);
|
||||
Py_DECREF(self->cpp_type);
|
||||
Py_DECREF(self->label);
|
||||
Py_DECREF(self->id);
|
||||
self->ob_type->tp_free(reinterpret_cast<PyObject*>(self));
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
|
||||
const google::protobuf::DescriptorPool* pool;
|
||||
} CDescriptorPool;
|
||||
|
||||
static void CDescriptorPoolDealloc(CDescriptorPool* self);
|
||||
|
||||
static PyObject* CDescriptorPool_NewCDescriptor(
|
||||
const google::protobuf::FieldDescriptor* field_descriptor) {
|
||||
CFieldDescriptor* cfield_descriptor = PyObject_New(
|
||||
CFieldDescriptor, &CFieldDescriptor_Type);
|
||||
if (cfield_descriptor == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
cfield_descriptor->descriptor = field_descriptor;
|
||||
|
||||
cfield_descriptor->full_name = PyString_FromString(
|
||||
field_descriptor->full_name().c_str());
|
||||
cfield_descriptor->name = PyString_FromString(
|
||||
field_descriptor->name().c_str());
|
||||
cfield_descriptor->cpp_type = PyLong_FromLong(field_descriptor->cpp_type());
|
||||
cfield_descriptor->label = PyLong_FromLong(field_descriptor->label());
|
||||
cfield_descriptor->id = PyLong_FromVoidPtr(cfield_descriptor);
|
||||
return reinterpret_cast<PyObject*>(cfield_descriptor);
|
||||
}
|
||||
|
||||
static PyObject* CDescriptorPool_FindFieldByName(
|
||||
CDescriptorPool* self, PyObject* arg) {
|
||||
const char* full_field_name = PyString_AsString(arg);
|
||||
if (full_field_name == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const google::protobuf::FieldDescriptor* field_descriptor = NULL;
|
||||
|
||||
field_descriptor = self->pool->FindFieldByName(full_field_name);
|
||||
if (field_descriptor == NULL) {
|
||||
PyErr_Format(PyExc_TypeError, "Couldn't find field %.200s",
|
||||
full_field_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return CDescriptorPool_NewCDescriptor(field_descriptor);
|
||||
}
|
||||
|
||||
static PyObject* CDescriptorPool_FindExtensionByName(
|
||||
CDescriptorPool* self, PyObject* arg) {
|
||||
const char* full_field_name = PyString_AsString(arg);
|
||||
if (full_field_name == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const google::protobuf::FieldDescriptor* field_descriptor =
|
||||
self->pool->FindExtensionByName(full_field_name);
|
||||
if (field_descriptor == NULL) {
|
||||
PyErr_Format(PyExc_TypeError, "Couldn't find field %.200s",
|
||||
full_field_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return CDescriptorPool_NewCDescriptor(field_descriptor);
|
||||
}
|
||||
|
||||
static PyMethodDef CDescriptorPoolMethods[] = {
|
||||
{ C("FindFieldByName"),
|
||||
(PyCFunction)CDescriptorPool_FindFieldByName,
|
||||
METH_O,
|
||||
C("Searches for a field descriptor by full name.") },
|
||||
{ C("FindExtensionByName"),
|
||||
(PyCFunction)CDescriptorPool_FindExtensionByName,
|
||||
METH_O,
|
||||
C("Searches for extension descriptor by full name.") },
|
||||
{NULL}
|
||||
};
|
||||
|
||||
PyTypeObject CDescriptorPool_Type = {
|
||||
PyObject_HEAD_INIT(&PyType_Type)
|
||||
0,
|
||||
C("google3.net.google.protobuf.python.internal."
|
||||
"_net_proto2___python."
|
||||
"CFieldDescriptor"), // tp_name
|
||||
sizeof(CDescriptorPool), // tp_basicsize
|
||||
0, // tp_itemsize
|
||||
(destructor)CDescriptorPoolDealloc, // tp_dealloc
|
||||
0, // tp_print
|
||||
0, // tp_getattr
|
||||
0, // tp_setattr
|
||||
0, // tp_compare
|
||||
0, // tp_repr
|
||||
0, // tp_as_number
|
||||
0, // tp_as_sequence
|
||||
0, // tp_as_mapping
|
||||
0, // tp_hash
|
||||
0, // tp_call
|
||||
0, // tp_str
|
||||
0, // tp_getattro
|
||||
0, // tp_setattro
|
||||
0, // tp_as_buffer
|
||||
Py_TPFLAGS_DEFAULT, // tp_flags
|
||||
C("A Descriptor Pool"), // tp_doc
|
||||
0, // tp_traverse
|
||||
0, // tp_clear
|
||||
0, // tp_richcompare
|
||||
0, // tp_weaklistoffset
|
||||
0, // tp_iter
|
||||
0, // tp_iternext
|
||||
CDescriptorPoolMethods, // tp_methods
|
||||
0, // tp_members
|
||||
0, // tp_getset
|
||||
0, // tp_base
|
||||
0, // tp_dict
|
||||
0, // tp_descr_get
|
||||
0, // tp_descr_set
|
||||
0, // tp_dictoffset
|
||||
0, // tp_init
|
||||
PyType_GenericAlloc, // tp_alloc
|
||||
PyType_GenericNew, // tp_new
|
||||
PyObject_Del, // tp_free
|
||||
};
|
||||
|
||||
static void CDescriptorPoolDealloc(CDescriptorPool* self) {
|
||||
self->ob_type->tp_free(reinterpret_cast<PyObject*>(self));
|
||||
}
|
||||
|
||||
google::protobuf::DescriptorPool* GetDescriptorPool() {
|
||||
if (g_descriptor_pool == NULL) {
|
||||
g_descriptor_pool = new google::protobuf::DescriptorPool(
|
||||
google::protobuf::DescriptorPool::generated_pool());
|
||||
}
|
||||
return g_descriptor_pool;
|
||||
}
|
||||
|
||||
PyObject* Python_NewCDescriptorPool(PyObject* ignored, PyObject* args) {
|
||||
CDescriptorPool* cdescriptor_pool = PyObject_New(
|
||||
CDescriptorPool, &CDescriptorPool_Type);
|
||||
if (cdescriptor_pool == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
cdescriptor_pool->pool = GetDescriptorPool();
|
||||
return reinterpret_cast<PyObject*>(cdescriptor_pool);
|
||||
}
|
||||
|
||||
PyObject* Python_BuildFile(PyObject* ignored, PyObject* arg) {
|
||||
char* message_type;
|
||||
Py_ssize_t message_len;
|
||||
|
||||
if (PyString_AsStringAndSize(arg, &message_type, &message_len) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
google::protobuf::FileDescriptorProto file_proto;
|
||||
if (!file_proto.ParseFromArray(message_type, message_len)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Couldn't parse file content!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// If this file is already in the generated pool, don't add it again.
|
||||
if (google::protobuf::DescriptorPool::generated_pool()->FindFileByName(
|
||||
file_proto.name()) != NULL) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
const google::protobuf::FileDescriptor* descriptor = GetDescriptorPool()->BuildFile(
|
||||
file_proto);
|
||||
if (descriptor == NULL) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"Couldn't build proto file into descriptor pool!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
bool InitDescriptor() {
|
||||
CFieldDescriptor_Type.tp_new = PyType_GenericNew;
|
||||
if (PyType_Ready(&CFieldDescriptor_Type) < 0)
|
||||
return false;
|
||||
|
||||
CDescriptorPool_Type.tp_new = PyType_GenericNew;
|
||||
if (PyType_Ready(&CDescriptorPool_Type) < 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace python
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
87
python/google/protobuf/pyext/python_descriptor.h
Normal file
87
python/google/protobuf/pyext/python_descriptor.h
Normal file
@ -0,0 +1,87 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Author: petar@google.com (Petar Petrov)
|
||||
|
||||
#ifndef GOOGLE_PROTOBUF_PYTHON_DESCRIPTOR_H__
|
||||
#define GOOGLE_PROTOBUF_PYTHON_DESCRIPTOR_H__
|
||||
|
||||
#include <Python.h>
|
||||
#include <structmember.h>
|
||||
|
||||
#include <google/protobuf/descriptor.h>
|
||||
|
||||
#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
|
||||
typedef int Py_ssize_t;
|
||||
#define PY_SSIZE_T_MAX INT_MAX
|
||||
#define PY_SSIZE_T_MIN INT_MIN
|
||||
#endif
|
||||
|
||||
namespace google {
|
||||
namespace protobuf {
|
||||
namespace python {
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
|
||||
// The proto2 descriptor that this object represents.
|
||||
const google::protobuf::FieldDescriptor* descriptor;
|
||||
|
||||
// Full name of the field (PyString).
|
||||
PyObject* full_name;
|
||||
|
||||
// Name of the field (PyString).
|
||||
PyObject* name;
|
||||
|
||||
// C++ type of the field (PyLong).
|
||||
PyObject* cpp_type;
|
||||
|
||||
// Name of the field (PyLong).
|
||||
PyObject* label;
|
||||
|
||||
// Identity of the descriptor (PyLong used as a poiner).
|
||||
PyObject* id;
|
||||
} CFieldDescriptor;
|
||||
|
||||
extern PyTypeObject CFieldDescriptor_Type;
|
||||
|
||||
extern PyTypeObject CDescriptorPool_Type;
|
||||
|
||||
|
||||
PyObject* Python_NewCDescriptorPool(PyObject* ignored, PyObject* args);
|
||||
PyObject* Python_BuildFile(PyObject* ignored, PyObject* args);
|
||||
bool InitDescriptor();
|
||||
google::protobuf::DescriptorPool* GetDescriptorPool();
|
||||
|
||||
} // namespace python
|
||||
} // namespace protobuf
|
||||
|
||||
} // namespace google
|
||||
#endif // GOOGLE_PROTOBUF_PYTHON_DESCRIPTOR_H__
|
63
python/google/protobuf/pyext/python_protobuf.cc
Normal file
63
python/google/protobuf/pyext/python_protobuf.cc
Normal file
@ -0,0 +1,63 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Author: qrczak@google.com (Marcin Kowalczyk)
|
||||
|
||||
#include <google/protobuf/pyext/python_protobuf.h>
|
||||
|
||||
namespace google {
|
||||
namespace protobuf {
|
||||
namespace python {
|
||||
|
||||
static const Message* GetCProtoInsidePyProtoStub(PyObject* msg) {
|
||||
return NULL;
|
||||
}
|
||||
static Message* MutableCProtoInsidePyProtoStub(PyObject* msg) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// This is initialized with a default, stub implementation.
|
||||
// If python-google.protobuf.cc is loaded, the function pointer is overridden
|
||||
// with a full implementation.
|
||||
const Message* (*GetCProtoInsidePyProtoPtr)(PyObject* msg) =
|
||||
GetCProtoInsidePyProtoStub;
|
||||
Message* (*MutableCProtoInsidePyProtoPtr)(PyObject* msg) =
|
||||
MutableCProtoInsidePyProtoStub;
|
||||
|
||||
const Message* GetCProtoInsidePyProto(PyObject* msg) {
|
||||
return GetCProtoInsidePyProtoPtr(msg);
|
||||
}
|
||||
Message* MutableCProtoInsidePyProto(PyObject* msg) {
|
||||
return MutableCProtoInsidePyProtoPtr(msg);
|
||||
}
|
||||
|
||||
} // namespace python
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
57
python/google/protobuf/pyext/python_protobuf.h
Normal file
57
python/google/protobuf/pyext/python_protobuf.h
Normal file
@ -0,0 +1,57 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Author: qrczak@google.com (Marcin Kowalczyk)
|
||||
//
|
||||
// This module exposes the C proto inside the given Python proto, in
|
||||
// case the Python proto is implemented with a C proto.
|
||||
|
||||
#ifndef GOOGLE_PROTOBUF_PYTHON_PYTHON_PROTOBUF_H__
|
||||
#define GOOGLE_PROTOBUF_PYTHON_PYTHON_PROTOBUF_H__
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace google {
|
||||
namespace protobuf {
|
||||
|
||||
class Message;
|
||||
|
||||
namespace python {
|
||||
|
||||
// Return the pointer to the C proto inside the given Python proto,
|
||||
// or NULL when this is not a Python proto implemented with a C proto.
|
||||
const Message* GetCProtoInsidePyProto(PyObject* msg);
|
||||
Message* MutableCProtoInsidePyProto(PyObject* msg);
|
||||
|
||||
} // namespace python
|
||||
} // namespace protobuf
|
||||
|
||||
} // namespace google
|
||||
#endif // GOOGLE_PROTOBUF_PYTHON_PYTHON_PROTOBUF_H__
|
File diff suppressed because it is too large
Load Diff
@ -53,24 +53,26 @@ class ParseError(Exception):
|
||||
"""Thrown in case of ASCII parsing error."""
|
||||
|
||||
|
||||
def MessageToString(message):
|
||||
def MessageToString(message, as_utf8=False, as_one_line=False):
|
||||
out = cStringIO.StringIO()
|
||||
PrintMessage(message, out)
|
||||
PrintMessage(message, out, as_utf8=as_utf8, as_one_line=as_one_line)
|
||||
result = out.getvalue()
|
||||
out.close()
|
||||
if as_one_line:
|
||||
return result.rstrip()
|
||||
return result
|
||||
|
||||
|
||||
def PrintMessage(message, out, indent = 0):
|
||||
def PrintMessage(message, out, indent=0, as_utf8=False, as_one_line=False):
|
||||
for field, value in message.ListFields():
|
||||
if field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
|
||||
for element in value:
|
||||
PrintField(field, element, out, indent)
|
||||
PrintField(field, element, out, indent, as_utf8, as_one_line)
|
||||
else:
|
||||
PrintField(field, value, out, indent)
|
||||
PrintField(field, value, out, indent, as_utf8, as_one_line)
|
||||
|
||||
|
||||
def PrintField(field, value, out, indent = 0):
|
||||
def PrintField(field, value, out, indent=0, as_utf8=False, as_one_line=False):
|
||||
"""Print a single field name/value pair. For repeated fields, the value
|
||||
should be a single element."""
|
||||
|
||||
@ -96,23 +98,35 @@ def PrintField(field, value, out, indent = 0):
|
||||
# don't include it.
|
||||
out.write(': ')
|
||||
|
||||
PrintFieldValue(field, value, out, indent)
|
||||
out.write('\n')
|
||||
PrintFieldValue(field, value, out, indent, as_utf8, as_one_line)
|
||||
if as_one_line:
|
||||
out.write(' ')
|
||||
else:
|
||||
out.write('\n')
|
||||
|
||||
|
||||
def PrintFieldValue(field, value, out, indent = 0):
|
||||
def PrintFieldValue(field, value, out, indent=0,
|
||||
as_utf8=False, as_one_line=False):
|
||||
"""Print a single field value (not including name). For repeated fields,
|
||||
the value should be a single element."""
|
||||
|
||||
if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
|
||||
out.write(' {\n')
|
||||
PrintMessage(value, out, indent + 2)
|
||||
out.write(' ' * indent + '}')
|
||||
if as_one_line:
|
||||
out.write(' { ')
|
||||
PrintMessage(value, out, indent, as_utf8, as_one_line)
|
||||
out.write('}')
|
||||
else:
|
||||
out.write(' {\n')
|
||||
PrintMessage(value, out, indent + 2, as_utf8, as_one_line)
|
||||
out.write(' ' * indent + '}')
|
||||
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM:
|
||||
out.write(field.enum_type.values_by_number[value].name)
|
||||
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING:
|
||||
out.write('\"')
|
||||
out.write(_CEscape(value))
|
||||
if type(value) is unicode:
|
||||
out.write(_CEscape(value.encode('utf-8'), as_utf8))
|
||||
else:
|
||||
out.write(_CEscape(value, as_utf8))
|
||||
out.write('\"')
|
||||
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL:
|
||||
if value:
|
||||
@ -334,10 +348,10 @@ class _Tokenizer(object):
|
||||
Returns:
|
||||
True iff the end was reached.
|
||||
"""
|
||||
return not self._lines and not self._current_line
|
||||
return self.token == ''
|
||||
|
||||
def _PopLine(self):
|
||||
while not self._current_line:
|
||||
while len(self._current_line) <= self._column:
|
||||
if not self._lines:
|
||||
self._current_line = ''
|
||||
return
|
||||
@ -348,11 +362,10 @@ class _Tokenizer(object):
|
||||
def _SkipWhitespace(self):
|
||||
while True:
|
||||
self._PopLine()
|
||||
match = re.match(self._WHITESPACE, self._current_line)
|
||||
match = self._WHITESPACE.match(self._current_line, self._column)
|
||||
if not match:
|
||||
break
|
||||
length = len(match.group(0))
|
||||
self._current_line = self._current_line[length:]
|
||||
self._column += length
|
||||
|
||||
def TryConsume(self, token):
|
||||
@ -402,7 +415,7 @@ class _Tokenizer(object):
|
||||
ParseError: If an identifier couldn't be consumed.
|
||||
"""
|
||||
result = self.token
|
||||
if not re.match(self._IDENTIFIER, result):
|
||||
if not self._IDENTIFIER.match(result):
|
||||
raise self._ParseError('Expected identifier.')
|
||||
self.NextToken()
|
||||
return result
|
||||
@ -481,13 +494,13 @@ class _Tokenizer(object):
|
||||
ParseError: If a floating point number couldn't be consumed.
|
||||
"""
|
||||
text = self.token
|
||||
if re.match(self._FLOAT_INFINITY, text):
|
||||
if self._FLOAT_INFINITY.match(text):
|
||||
self.NextToken()
|
||||
if text.startswith('-'):
|
||||
return -_INFINITY
|
||||
return _INFINITY
|
||||
|
||||
if re.match(self._FLOAT_NAN, text):
|
||||
if self._FLOAT_NAN.match(text):
|
||||
self.NextToken()
|
||||
return _NAN
|
||||
|
||||
@ -507,10 +520,10 @@ class _Tokenizer(object):
|
||||
Raises:
|
||||
ParseError: If a boolean value couldn't be consumed.
|
||||
"""
|
||||
if self.token == 'true':
|
||||
if self.token in ('true', 't', '1'):
|
||||
self.NextToken()
|
||||
return True
|
||||
elif self.token == 'false':
|
||||
elif self.token in ('false', 'f', '0'):
|
||||
self.NextToken()
|
||||
return False
|
||||
else:
|
||||
@ -525,7 +538,11 @@ class _Tokenizer(object):
|
||||
Raises:
|
||||
ParseError: If a string value couldn't be consumed.
|
||||
"""
|
||||
return unicode(self.ConsumeByteString(), 'utf-8')
|
||||
bytes = self.ConsumeByteString()
|
||||
try:
|
||||
return unicode(bytes, 'utf-8')
|
||||
except UnicodeDecodeError, e:
|
||||
raise self._StringParseError(e)
|
||||
|
||||
def ConsumeByteString(self):
|
||||
"""Consumes a byte array value.
|
||||
@ -609,7 +626,7 @@ class _Tokenizer(object):
|
||||
def _ParseError(self, message):
|
||||
"""Creates and *returns* a ParseError for the current token."""
|
||||
return ParseError('%d:%d : %s' % (
|
||||
self._line + 1, self._column + 1, message))
|
||||
self._line + 1, self._column - len(self.token) + 1, message))
|
||||
|
||||
def _IntegerParseError(self, e):
|
||||
return self._ParseError('Couldn\'t parse integer: ' + str(e))
|
||||
@ -617,27 +634,27 @@ class _Tokenizer(object):
|
||||
def _FloatParseError(self, e):
|
||||
return self._ParseError('Couldn\'t parse number: ' + str(e))
|
||||
|
||||
def _StringParseError(self, e):
|
||||
return self._ParseError('Couldn\'t parse string: ' + str(e))
|
||||
|
||||
def NextToken(self):
|
||||
"""Reads the next meaningful token."""
|
||||
self._previous_line = self._line
|
||||
self._previous_column = self._column
|
||||
if self.AtEnd():
|
||||
|
||||
self._column += len(self.token)
|
||||
self._SkipWhitespace()
|
||||
|
||||
if not self._lines and len(self._current_line) <= self._column:
|
||||
self.token = ''
|
||||
return
|
||||
self._column += len(self.token)
|
||||
|
||||
# Make sure there is data to work on.
|
||||
self._PopLine()
|
||||
|
||||
match = re.match(self._TOKEN, self._current_line)
|
||||
match = self._TOKEN.match(self._current_line, self._column)
|
||||
if match:
|
||||
token = match.group(0)
|
||||
self._current_line = self._current_line[len(token):]
|
||||
self.token = token
|
||||
else:
|
||||
self.token = self._current_line[0]
|
||||
self._current_line = self._current_line[1:]
|
||||
self._SkipWhitespace()
|
||||
self.token = self._current_line[self._column]
|
||||
|
||||
|
||||
# text.encode('string_escape') does not seem to satisfy our needs as it
|
||||
@ -645,7 +662,7 @@ class _Tokenizer(object):
|
||||
# C++ unescaping function allows hex escapes to be any length. So,
|
||||
# "\0011".encode('string_escape') ends up being "\\x011", which will be
|
||||
# decoded in C++ as a single-character string with char code 0x11.
|
||||
def _CEscape(text):
|
||||
def _CEscape(text, as_utf8):
|
||||
def escape(c):
|
||||
o = ord(c)
|
||||
if o == 10: return r"\n" # optional escape
|
||||
@ -656,12 +673,13 @@ def _CEscape(text):
|
||||
if o == 34: return r'\"' # necessary escape
|
||||
if o == 92: return r"\\" # necessary escape
|
||||
|
||||
if o >= 127 or o < 32: return "\\%03o" % o # necessary escapes
|
||||
# necessary escapes
|
||||
if not as_utf8 and (o >= 127 or o < 32): return "\\%03o" % o
|
||||
return c
|
||||
return "".join([escape(c) for c in text])
|
||||
|
||||
|
||||
_CUNESCAPE_HEX = re.compile('\\\\x([0-9a-fA-F]{2}|[0-9a-f-A-F])')
|
||||
_CUNESCAPE_HEX = re.compile('\\\\x([0-9a-fA-F]{2}|[0-9a-fA-F])')
|
||||
|
||||
|
||||
def _CUnescape(text):
|
||||
|
@ -7,7 +7,7 @@
|
||||
from ez_setup import use_setuptools
|
||||
use_setuptools()
|
||||
|
||||
from setuptools import setup
|
||||
from setuptools import setup, Extension
|
||||
from distutils.spawn import find_executable
|
||||
import sys
|
||||
import os
|
||||
@ -60,6 +60,7 @@ def MakeTestSuite():
|
||||
del sys.modules['google']
|
||||
|
||||
generate_proto("../src/google/protobuf/unittest.proto")
|
||||
generate_proto("../src/google/protobuf/unittest_custom_options.proto")
|
||||
generate_proto("../src/google/protobuf/unittest_import.proto")
|
||||
generate_proto("../src/google/protobuf/unittest_mset.proto")
|
||||
generate_proto("../src/google/protobuf/unittest_no_generic_services.proto")
|
||||
@ -94,24 +95,39 @@ if __name__ == '__main__':
|
||||
for (dirpath, dirnames, filenames) in os.walk("."):
|
||||
for filename in filenames:
|
||||
filepath = os.path.join(dirpath, filename)
|
||||
if filepath.endswith("_pb2.py") or filepath.endswith(".pyc"):
|
||||
if filepath.endswith("_pb2.py") or filepath.endswith(".pyc") or \
|
||||
filepath.endswith(".so") or filepath.endswith(".o"):
|
||||
os.remove(filepath)
|
||||
else:
|
||||
# Generate necessary .proto file if it doesn't exist.
|
||||
# TODO(kenton): Maybe we should hook this into a distutils command?
|
||||
generate_proto("../src/google/protobuf/descriptor.proto")
|
||||
|
||||
python_c_extension = Extension("google.protobuf.internal._net_proto2___python",
|
||||
[ "google/protobuf/pyext/python_descriptor.cc",
|
||||
"google/protobuf/pyext/python_protobuf.cc",
|
||||
"google/protobuf/pyext/python-proto2.cc",
|
||||
],
|
||||
include_dirs = [ "../src", ".", ],
|
||||
libraries = [ "protobuf" ],
|
||||
runtime_library_dirs = [ "../src/.libs" ],
|
||||
library_dirs = [ "../src/.libs" ],
|
||||
)
|
||||
|
||||
setup(name = 'protobuf',
|
||||
version = '2.3.1-pre',
|
||||
version = '2.4.0-pre',
|
||||
packages = [ 'google' ],
|
||||
namespace_packages = [ 'google' ],
|
||||
test_suite = 'setup.MakeTestSuite',
|
||||
# Must list modules explicitly so that we don't install tests.
|
||||
py_modules = [
|
||||
'google.protobuf.internal.api_implementation',
|
||||
'google.protobuf.internal.containers',
|
||||
'google.protobuf.internal.cpp_message',
|
||||
'google.protobuf.internal.decoder',
|
||||
'google.protobuf.internal.encoder',
|
||||
'google.protobuf.internal.message_listener',
|
||||
'google.protobuf.internal.python_message',
|
||||
'google.protobuf.internal.type_checkers',
|
||||
'google.protobuf.internal.wire_format',
|
||||
'google.protobuf.descriptor',
|
||||
@ -121,6 +137,7 @@ if __name__ == '__main__':
|
||||
'google.protobuf.service',
|
||||
'google.protobuf.service_reflection',
|
||||
'google.protobuf.text_format' ],
|
||||
ext_modules = [ python_c_extension ],
|
||||
url = 'http://code.google.com/p/protobuf/',
|
||||
maintainer = maintainer_email,
|
||||
maintainer_email = 'protobuf@googlegroups.com',
|
||||
|
@ -178,6 +178,8 @@ libprotoc_la_SOURCES = \
|
||||
google/protobuf/compiler/java/java_primitive_field.h \
|
||||
google/protobuf/compiler/java/java_service.cc \
|
||||
google/protobuf/compiler/java/java_service.h \
|
||||
google/protobuf/compiler/java/java_string_field.cc \
|
||||
google/protobuf/compiler/java/java_string_field.h \
|
||||
google/protobuf/compiler/python/python_generator.cc
|
||||
|
||||
bin_PROGRAMS = protoc
|
||||
|
@ -42,14 +42,19 @@ namespace protobuf {
|
||||
namespace compiler {
|
||||
|
||||
CodeGenerator::~CodeGenerator() {}
|
||||
OutputDirectory::~OutputDirectory() {}
|
||||
GeneratorContext::~GeneratorContext() {}
|
||||
|
||||
io::ZeroCopyOutputStream* OutputDirectory::OpenForInsert(
|
||||
io::ZeroCopyOutputStream* GeneratorContext::OpenForInsert(
|
||||
const string& filename, const string& insertion_point) {
|
||||
GOOGLE_LOG(FATAL) << "This OutputDirectory does not support insertion.";
|
||||
GOOGLE_LOG(FATAL) << "This GeneratorContext does not support insertion.";
|
||||
return NULL; // make compiler happy
|
||||
}
|
||||
|
||||
void GeneratorContext::ListParsedFiles(
|
||||
vector<const FileDescriptor*>* output) {
|
||||
GOOGLE_LOG(FATAL) << "This GeneratorContext does not support ListParsedFiles";
|
||||
}
|
||||
|
||||
// Parses a set of comma-delimited name/value pairs.
|
||||
void ParseGeneratorParameter(const string& text,
|
||||
vector<pair<string, string> >* output) {
|
||||
|
@ -53,7 +53,7 @@ namespace compiler {
|
||||
|
||||
// Defined in this file.
|
||||
class CodeGenerator;
|
||||
class OutputDirectory;
|
||||
class GeneratorContext;
|
||||
|
||||
// The abstract interface to a class which generates code implementing a
|
||||
// particular proto file in a particular language. A number of these may
|
||||
@ -76,7 +76,7 @@ class LIBPROTOC_EXPORT CodeGenerator {
|
||||
// the problem (e.g. "invalid parameter") and returns false.
|
||||
virtual bool Generate(const FileDescriptor* file,
|
||||
const string& parameter,
|
||||
OutputDirectory* output_directory,
|
||||
GeneratorContext* generator_context,
|
||||
string* error) const = 0;
|
||||
|
||||
private:
|
||||
@ -85,11 +85,12 @@ class LIBPROTOC_EXPORT CodeGenerator {
|
||||
|
||||
// CodeGenerators generate one or more files in a given directory. This
|
||||
// abstract interface represents the directory to which the CodeGenerator is
|
||||
// to write.
|
||||
class LIBPROTOC_EXPORT OutputDirectory {
|
||||
// to write and other information about the context in which the Generator
|
||||
// runs.
|
||||
class LIBPROTOC_EXPORT GeneratorContext {
|
||||
public:
|
||||
inline OutputDirectory() {}
|
||||
virtual ~OutputDirectory();
|
||||
inline GeneratorContext() {}
|
||||
virtual ~GeneratorContext();
|
||||
|
||||
// Opens the given file, truncating it if it exists, and returns a
|
||||
// ZeroCopyOutputStream that writes to the file. The caller takes ownership
|
||||
@ -112,10 +113,19 @@ class LIBPROTOC_EXPORT OutputDirectory {
|
||||
virtual io::ZeroCopyOutputStream* OpenForInsert(
|
||||
const string& filename, const string& insertion_point);
|
||||
|
||||
// Returns a vector of FileDescriptors for all the files being compiled
|
||||
// in this run. Useful for languages, such as Go, that treat files
|
||||
// differently when compiled as a set rather than individually.
|
||||
virtual void ListParsedFiles(vector<const FileDescriptor*>* output);
|
||||
|
||||
private:
|
||||
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(OutputDirectory);
|
||||
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(GeneratorContext);
|
||||
};
|
||||
|
||||
// The type GeneratorContext was once called OutputDirectory. This typedef
|
||||
// provides backward compatibility.
|
||||
typedef GeneratorContext OutputDirectory;
|
||||
|
||||
// Several code generators treat the parameter argument as holding a
|
||||
// list of options separated by commas. This helper function parses
|
||||
// a set of comma-delimited name/value pairs: e.g.,
|
||||
|
@ -48,6 +48,9 @@
|
||||
#include <iostream>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <google/protobuf/stubs/hash.h>
|
||||
|
||||
#include <google/protobuf/stubs/common.h>
|
||||
#include <google/protobuf/compiler/importer.h>
|
||||
#include <google/protobuf/compiler/code_generator.h>
|
||||
#include <google/protobuf/compiler/plugin.pb.h>
|
||||
@ -58,12 +61,10 @@
|
||||
#include <google/protobuf/dynamic_message.h>
|
||||
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
||||
#include <google/protobuf/io/printer.h>
|
||||
#include <google/protobuf/stubs/common.h>
|
||||
#include <google/protobuf/stubs/strutil.h>
|
||||
#include <google/protobuf/stubs/substitute.h>
|
||||
#include <google/protobuf/stubs/map-util.h>
|
||||
#include <google/protobuf/stubs/stl_util-inl.h>
|
||||
#include <google/protobuf/stubs/hash.h>
|
||||
|
||||
|
||||
namespace google {
|
||||
@ -182,7 +183,7 @@ bool TryCreateParentDirectory(const string& prefix, const string& filename) {
|
||||
class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector,
|
||||
public io::ErrorCollector {
|
||||
public:
|
||||
ErrorPrinter(ErrorFormat format, DiskSourceTree *tree = NULL)
|
||||
ErrorPrinter(ErrorFormat format, DiskSourceTree *tree = NULL)
|
||||
: format_(format), tree_(tree) {}
|
||||
~ErrorPrinter() {}
|
||||
|
||||
@ -191,8 +192,8 @@ class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector,
|
||||
const string& message) {
|
||||
|
||||
// Print full path when running under MSVS
|
||||
std::string dfile;
|
||||
if (format_ == CommandLineInterface::ERROR_FORMAT_MSVS &&
|
||||
string dfile;
|
||||
if (format_ == CommandLineInterface::ERROR_FORMAT_MSVS &&
|
||||
tree_ != NULL &&
|
||||
tree_->VirtualFileToDiskFile(filename, &dfile)) {
|
||||
cerr << dfile;
|
||||
@ -229,12 +230,12 @@ class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector,
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
// An OutputDirectory implementation that buffers files in memory, then dumps
|
||||
// A GeneratorContext implementation that buffers files in memory, then dumps
|
||||
// them all to disk on demand.
|
||||
class CommandLineInterface::MemoryOutputDirectory : public OutputDirectory {
|
||||
class CommandLineInterface::GeneratorContextImpl : public GeneratorContext {
|
||||
public:
|
||||
MemoryOutputDirectory();
|
||||
~MemoryOutputDirectory();
|
||||
GeneratorContextImpl(const vector<const FileDescriptor*>& parsed_files);
|
||||
~GeneratorContextImpl();
|
||||
|
||||
// Write all files in the directory to disk at the given output location,
|
||||
// which must end in a '/'.
|
||||
@ -248,10 +249,13 @@ class CommandLineInterface::MemoryOutputDirectory : public OutputDirectory {
|
||||
// format, unless one has already been written.
|
||||
void AddJarManifest();
|
||||
|
||||
// implements OutputDirectory --------------------------------------
|
||||
// implements GeneratorContext --------------------------------------
|
||||
io::ZeroCopyOutputStream* Open(const string& filename);
|
||||
io::ZeroCopyOutputStream* OpenForInsert(
|
||||
const string& filename, const string& insertion_point);
|
||||
void ListParsedFiles(vector<const FileDescriptor*>* output) {
|
||||
*output = parsed_files_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class MemoryOutputStream;
|
||||
@ -259,14 +263,15 @@ class CommandLineInterface::MemoryOutputDirectory : public OutputDirectory {
|
||||
// map instead of hash_map so that files are written in order (good when
|
||||
// writing zips).
|
||||
map<string, string*> files_;
|
||||
const vector<const FileDescriptor*>& parsed_files_;
|
||||
bool had_error_;
|
||||
};
|
||||
|
||||
class CommandLineInterface::MemoryOutputStream
|
||||
: public io::ZeroCopyOutputStream {
|
||||
public:
|
||||
MemoryOutputStream(MemoryOutputDirectory* directory, const string& filename);
|
||||
MemoryOutputStream(MemoryOutputDirectory* directory, const string& filename,
|
||||
MemoryOutputStream(GeneratorContextImpl* directory, const string& filename);
|
||||
MemoryOutputStream(GeneratorContextImpl* directory, const string& filename,
|
||||
const string& insertion_point);
|
||||
virtual ~MemoryOutputStream();
|
||||
|
||||
@ -277,7 +282,7 @@ class CommandLineInterface::MemoryOutputStream
|
||||
|
||||
private:
|
||||
// Where to insert the string when it's done.
|
||||
MemoryOutputDirectory* directory_;
|
||||
GeneratorContextImpl* directory_;
|
||||
string filename_;
|
||||
string insertion_point_;
|
||||
|
||||
@ -290,14 +295,17 @@ class CommandLineInterface::MemoryOutputStream
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
CommandLineInterface::MemoryOutputDirectory::MemoryOutputDirectory()
|
||||
: had_error_(false) {}
|
||||
CommandLineInterface::GeneratorContextImpl::GeneratorContextImpl(
|
||||
const vector<const FileDescriptor*>& parsed_files)
|
||||
: parsed_files_(parsed_files),
|
||||
had_error_(false) {
|
||||
}
|
||||
|
||||
CommandLineInterface::MemoryOutputDirectory::~MemoryOutputDirectory() {
|
||||
CommandLineInterface::GeneratorContextImpl::~GeneratorContextImpl() {
|
||||
STLDeleteValues(&files_);
|
||||
}
|
||||
|
||||
bool CommandLineInterface::MemoryOutputDirectory::WriteAllToDisk(
|
||||
bool CommandLineInterface::GeneratorContextImpl::WriteAllToDisk(
|
||||
const string& prefix) {
|
||||
if (had_error_) {
|
||||
return false;
|
||||
@ -372,7 +380,7 @@ bool CommandLineInterface::MemoryOutputDirectory::WriteAllToDisk(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CommandLineInterface::MemoryOutputDirectory::WriteAllToZip(
|
||||
bool CommandLineInterface::GeneratorContextImpl::WriteAllToZip(
|
||||
const string& filename) {
|
||||
if (had_error_) {
|
||||
return false;
|
||||
@ -413,7 +421,7 @@ bool CommandLineInterface::MemoryOutputDirectory::WriteAllToZip(
|
||||
return true;
|
||||
}
|
||||
|
||||
void CommandLineInterface::MemoryOutputDirectory::AddJarManifest() {
|
||||
void CommandLineInterface::GeneratorContextImpl::AddJarManifest() {
|
||||
string** map_slot = &files_["META-INF/MANIFEST.MF"];
|
||||
if (*map_slot == NULL) {
|
||||
*map_slot = new string(
|
||||
@ -423,13 +431,13 @@ void CommandLineInterface::MemoryOutputDirectory::AddJarManifest() {
|
||||
}
|
||||
}
|
||||
|
||||
io::ZeroCopyOutputStream* CommandLineInterface::MemoryOutputDirectory::Open(
|
||||
io::ZeroCopyOutputStream* CommandLineInterface::GeneratorContextImpl::Open(
|
||||
const string& filename) {
|
||||
return new MemoryOutputStream(this, filename);
|
||||
}
|
||||
|
||||
io::ZeroCopyOutputStream*
|
||||
CommandLineInterface::MemoryOutputDirectory::OpenForInsert(
|
||||
CommandLineInterface::GeneratorContextImpl::OpenForInsert(
|
||||
const string& filename, const string& insertion_point) {
|
||||
return new MemoryOutputStream(this, filename, insertion_point);
|
||||
}
|
||||
@ -437,14 +445,14 @@ CommandLineInterface::MemoryOutputDirectory::OpenForInsert(
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
CommandLineInterface::MemoryOutputStream::MemoryOutputStream(
|
||||
MemoryOutputDirectory* directory, const string& filename)
|
||||
GeneratorContextImpl* directory, const string& filename)
|
||||
: directory_(directory),
|
||||
filename_(filename),
|
||||
inner_(new io::StringOutputStream(&data_)) {
|
||||
}
|
||||
|
||||
CommandLineInterface::MemoryOutputStream::MemoryOutputStream(
|
||||
MemoryOutputDirectory* directory, const string& filename,
|
||||
GeneratorContextImpl* directory, const string& filename,
|
||||
const string& insertion_point)
|
||||
: directory_(directory),
|
||||
filename_(filename),
|
||||
@ -613,11 +621,11 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
// We construct a separate OutputDirectory for each output location. Note
|
||||
// We construct a separate GeneratorContext for each output location. Note
|
||||
// that two code generators may output to the same location, in which case
|
||||
// they should share a single OutputDirectory (so that OpenForInsert() works).
|
||||
typedef hash_map<string, MemoryOutputDirectory*> OutputDirectoryMap;
|
||||
OutputDirectoryMap output_directories;
|
||||
// they should share a single GeneratorContext so that OpenForInsert() works.
|
||||
typedef hash_map<string, GeneratorContextImpl*> GeneratorContextMap;
|
||||
GeneratorContextMap output_directories;
|
||||
|
||||
// Generate output.
|
||||
if (mode_ == MODE_COMPILE) {
|
||||
@ -627,11 +635,11 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) {
|
||||
!HasSuffixString(output_location, ".jar")) {
|
||||
AddTrailingSlash(&output_location);
|
||||
}
|
||||
MemoryOutputDirectory** map_slot = &output_directories[output_location];
|
||||
GeneratorContextImpl** map_slot = &output_directories[output_location];
|
||||
|
||||
if (*map_slot == NULL) {
|
||||
// First time we've seen this output location.
|
||||
*map_slot = new MemoryOutputDirectory;
|
||||
*map_slot = new GeneratorContextImpl(parsed_files);
|
||||
}
|
||||
|
||||
if (!GenerateOutput(parsed_files, output_directives_[i], *map_slot)) {
|
||||
@ -642,10 +650,10 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) {
|
||||
}
|
||||
|
||||
// Write all output to disk.
|
||||
for (OutputDirectoryMap::iterator iter = output_directories.begin();
|
||||
for (GeneratorContextMap::iterator iter = output_directories.begin();
|
||||
iter != output_directories.end(); ++iter) {
|
||||
const string& location = iter->first;
|
||||
MemoryOutputDirectory* directory = iter->second;
|
||||
GeneratorContextImpl* directory = iter->second;
|
||||
if (HasSuffixString(location, "/")) {
|
||||
if (!directory->WriteAllToDisk(location)) {
|
||||
STLDeleteValues(&output_directories);
|
||||
@ -1107,7 +1115,7 @@ void CommandLineInterface::PrintHelpText() {
|
||||
bool CommandLineInterface::GenerateOutput(
|
||||
const vector<const FileDescriptor*>& parsed_files,
|
||||
const OutputDirective& output_directive,
|
||||
OutputDirectory* output_directory) {
|
||||
GeneratorContext* generator_context) {
|
||||
// Call the generator.
|
||||
string error;
|
||||
if (output_directive.generator == NULL) {
|
||||
@ -1122,7 +1130,7 @@ bool CommandLineInterface::GenerateOutput(
|
||||
|
||||
if (!GeneratePluginOutput(parsed_files, plugin_name,
|
||||
output_directive.parameter,
|
||||
output_directory, &error)) {
|
||||
generator_context, &error)) {
|
||||
cerr << output_directive.name << ": " << error << endl;
|
||||
return false;
|
||||
}
|
||||
@ -1131,7 +1139,7 @@ bool CommandLineInterface::GenerateOutput(
|
||||
for (int i = 0; i < parsed_files.size(); i++) {
|
||||
if (!output_directive.generator->Generate(
|
||||
parsed_files[i], output_directive.parameter,
|
||||
output_directory, &error)) {
|
||||
generator_context, &error)) {
|
||||
// Generator returned an error.
|
||||
cerr << output_directive.name << ": " << parsed_files[i]->name() << ": "
|
||||
<< error << endl;
|
||||
@ -1147,7 +1155,7 @@ bool CommandLineInterface::GeneratePluginOutput(
|
||||
const vector<const FileDescriptor*>& parsed_files,
|
||||
const string& plugin_name,
|
||||
const string& parameter,
|
||||
OutputDirectory* output_directory,
|
||||
GeneratorContext* generator_context,
|
||||
string* error) {
|
||||
CodeGeneratorRequest request;
|
||||
CodeGeneratorResponse response;
|
||||
@ -1190,14 +1198,14 @@ bool CommandLineInterface::GeneratePluginOutput(
|
||||
// We reset current_output to NULL first so that the old file is closed
|
||||
// before the new one is opened.
|
||||
current_output.reset();
|
||||
current_output.reset(output_directory->OpenForInsert(
|
||||
current_output.reset(generator_context->OpenForInsert(
|
||||
output_file.name(), output_file.insertion_point()));
|
||||
} else if (!output_file.name().empty()) {
|
||||
// Starting a new file. Open it.
|
||||
// We reset current_output to NULL first so that the old file is closed
|
||||
// before the new one is opened.
|
||||
current_output.reset();
|
||||
current_output.reset(output_directory->Open(output_file.name()));
|
||||
current_output.reset(generator_context->Open(output_file.name()));
|
||||
} else if (current_output == NULL) {
|
||||
*error = strings::Substitute(
|
||||
"$0: First file chunk returned by plugin did not specify a file name.",
|
||||
|
@ -56,7 +56,7 @@ template<typename T> class RepeatedPtrField; // repeated_field.h
|
||||
namespace compiler {
|
||||
|
||||
class CodeGenerator; // code_generator.h
|
||||
class OutputDirectory; // code_generator.h
|
||||
class GeneratorContext; // code_generator.h
|
||||
class DiskSourceTree; // importer.h
|
||||
|
||||
// This class implements the command-line interface to the protocol compiler.
|
||||
@ -174,7 +174,7 @@ class LIBPROTOC_EXPORT CommandLineInterface {
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
class ErrorPrinter;
|
||||
class MemoryOutputDirectory;
|
||||
class GeneratorContextImpl;
|
||||
class MemoryOutputStream;
|
||||
|
||||
// Clear state from previous Run().
|
||||
@ -212,11 +212,11 @@ class LIBPROTOC_EXPORT CommandLineInterface {
|
||||
struct OutputDirective; // see below
|
||||
bool GenerateOutput(const vector<const FileDescriptor*>& parsed_files,
|
||||
const OutputDirective& output_directive,
|
||||
OutputDirectory* output_directory);
|
||||
GeneratorContext* generator_context);
|
||||
bool GeneratePluginOutput(const vector<const FileDescriptor*>& parsed_files,
|
||||
const string& plugin_name,
|
||||
const string& parameter,
|
||||
OutputDirectory* output_directory,
|
||||
GeneratorContext* generator_context,
|
||||
string* error);
|
||||
|
||||
// Implements --encode and --decode.
|
||||
|
@ -143,6 +143,10 @@ class CommandLineInterfaceTest : public testing::Test {
|
||||
const string& proto_name,
|
||||
const string& message_name,
|
||||
const string& output_directory);
|
||||
void ExpectGeneratedWithMultipleInputs(const string& generator_name,
|
||||
const string& all_proto_names,
|
||||
const string& proto_name,
|
||||
const string& message_name);
|
||||
void ExpectGeneratedWithInsertions(const string& generator_name,
|
||||
const string& parameter,
|
||||
const string& insertions,
|
||||
@ -190,7 +194,7 @@ class CommandLineInterfaceTest::NullCodeGenerator : public CodeGenerator {
|
||||
// implements CodeGenerator ----------------------------------------
|
||||
bool Generate(const FileDescriptor* file,
|
||||
const string& parameter,
|
||||
OutputDirectory* output_directory,
|
||||
GeneratorContext* context,
|
||||
string* error) const {
|
||||
called_ = true;
|
||||
parameter_ = parameter;
|
||||
@ -251,7 +255,6 @@ void CommandLineInterfaceTest::Run(const string& command) {
|
||||
|
||||
if (!disallow_plugins_) {
|
||||
cli_.AllowPlugins("prefix-");
|
||||
|
||||
const char* possible_paths[] = {
|
||||
// When building with shared libraries, libtool hides the real executable
|
||||
// in .libs and puts a fake wrapper in the current directory.
|
||||
@ -353,7 +356,8 @@ void CommandLineInterfaceTest::ExpectGenerated(
|
||||
const string& proto_name,
|
||||
const string& message_name) {
|
||||
MockCodeGenerator::ExpectGenerated(
|
||||
generator_name, parameter, "", proto_name, message_name, temp_directory_);
|
||||
generator_name, parameter, "", proto_name, message_name, proto_name,
|
||||
temp_directory_);
|
||||
}
|
||||
|
||||
void CommandLineInterfaceTest::ExpectGenerated(
|
||||
@ -363,10 +367,21 @@ void CommandLineInterfaceTest::ExpectGenerated(
|
||||
const string& message_name,
|
||||
const string& output_directory) {
|
||||
MockCodeGenerator::ExpectGenerated(
|
||||
generator_name, parameter, "", proto_name, message_name,
|
||||
generator_name, parameter, "", proto_name, message_name, proto_name,
|
||||
temp_directory_ + "/" + output_directory);
|
||||
}
|
||||
|
||||
void CommandLineInterfaceTest::ExpectGeneratedWithMultipleInputs(
|
||||
const string& generator_name,
|
||||
const string& all_proto_names,
|
||||
const string& proto_name,
|
||||
const string& message_name) {
|
||||
MockCodeGenerator::ExpectGenerated(
|
||||
generator_name, "", "", proto_name, message_name,
|
||||
all_proto_names,
|
||||
temp_directory_);
|
||||
}
|
||||
|
||||
void CommandLineInterfaceTest::ExpectGeneratedWithInsertions(
|
||||
const string& generator_name,
|
||||
const string& parameter,
|
||||
@ -375,7 +390,7 @@ void CommandLineInterfaceTest::ExpectGeneratedWithInsertions(
|
||||
const string& message_name) {
|
||||
MockCodeGenerator::ExpectGenerated(
|
||||
generator_name, parameter, insertions, proto_name, message_name,
|
||||
temp_directory_);
|
||||
proto_name, temp_directory_);
|
||||
}
|
||||
|
||||
void CommandLineInterfaceTest::ExpectNullCodeGeneratorCalled(
|
||||
@ -455,8 +470,44 @@ TEST_F(CommandLineInterfaceTest, MultipleInputs) {
|
||||
"--proto_path=$tmpdir foo.proto bar.proto");
|
||||
|
||||
ExpectNoErrors();
|
||||
ExpectGenerated("test_generator", "", "foo.proto", "Foo");
|
||||
ExpectGenerated("test_generator", "", "bar.proto", "Bar");
|
||||
ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
|
||||
"foo.proto", "Foo");
|
||||
ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
|
||||
"bar.proto", "Bar");
|
||||
ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
|
||||
"foo.proto", "Foo");
|
||||
ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
|
||||
"bar.proto", "Bar");
|
||||
}
|
||||
|
||||
TEST_F(CommandLineInterfaceTest, MultipleInputsWithImport) {
|
||||
// Test parsing multiple input files with an import of a separate file.
|
||||
|
||||
CreateTempFile("foo.proto",
|
||||
"syntax = \"proto2\";\n"
|
||||
"message Foo {}\n");
|
||||
CreateTempFile("bar.proto",
|
||||
"syntax = \"proto2\";\n"
|
||||
"import \"baz.proto\";\n"
|
||||
"message Bar {\n"
|
||||
" optional Baz a = 1;\n"
|
||||
"}\n");
|
||||
CreateTempFile("baz.proto",
|
||||
"syntax = \"proto2\";\n"
|
||||
"message Baz {}\n");
|
||||
|
||||
Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
|
||||
"--proto_path=$tmpdir foo.proto bar.proto");
|
||||
|
||||
ExpectNoErrors();
|
||||
ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
|
||||
"foo.proto", "Foo");
|
||||
ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
|
||||
"bar.proto", "Bar");
|
||||
ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
|
||||
"foo.proto", "Foo");
|
||||
ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
|
||||
"bar.proto", "Bar");
|
||||
}
|
||||
|
||||
TEST_F(CommandLineInterfaceTest, CreateDirectory) {
|
||||
|
@ -79,10 +79,10 @@ class MockErrorCollector : public MultiFileErrorCollector {
|
||||
}
|
||||
};
|
||||
|
||||
class MockOutputDirectory : public OutputDirectory {
|
||||
class MockGeneratorContext : public GeneratorContext {
|
||||
public:
|
||||
MockOutputDirectory() {}
|
||||
~MockOutputDirectory() {
|
||||
MockGeneratorContext() {}
|
||||
~MockGeneratorContext() {
|
||||
STLDeleteValues(&files_);
|
||||
}
|
||||
|
||||
@ -102,7 +102,7 @@ class MockOutputDirectory : public OutputDirectory {
|
||||
"to your CL.";
|
||||
}
|
||||
|
||||
// implements OutputDirectory --------------------------------------
|
||||
// implements GeneratorContext --------------------------------------
|
||||
|
||||
virtual io::ZeroCopyOutputStream* Open(const string& filename) {
|
||||
string** map_slot = &files_[filename];
|
||||
@ -130,24 +130,24 @@ TEST(BootstrapTest, GeneratedDescriptorMatches) {
|
||||
ASSERT_TRUE(plugin_proto_file != NULL);
|
||||
|
||||
CppGenerator generator;
|
||||
MockOutputDirectory output_directory;
|
||||
MockGeneratorContext context;
|
||||
string error;
|
||||
string parameter;
|
||||
parameter = "dllexport_decl=LIBPROTOBUF_EXPORT";
|
||||
ASSERT_TRUE(generator.Generate(proto_file, parameter,
|
||||
&output_directory, &error));
|
||||
&context, &error));
|
||||
parameter = "dllexport_decl=LIBPROTOC_EXPORT";
|
||||
ASSERT_TRUE(generator.Generate(plugin_proto_file, parameter,
|
||||
&output_directory, &error));
|
||||
&context, &error));
|
||||
|
||||
output_directory.ExpectFileMatches("google/protobuf/descriptor.pb.h",
|
||||
"google/protobuf/descriptor.pb.h");
|
||||
output_directory.ExpectFileMatches("google/protobuf/descriptor.pb.cc",
|
||||
"google/protobuf/descriptor.pb.cc");
|
||||
output_directory.ExpectFileMatches("google/protobuf/compiler/plugin.pb.h",
|
||||
"google/protobuf/compiler/plugin.pb.h");
|
||||
output_directory.ExpectFileMatches("google/protobuf/compiler/plugin.pb.cc",
|
||||
"google/protobuf/compiler/plugin.pb.cc");
|
||||
context.ExpectFileMatches("google/protobuf/descriptor.pb.h",
|
||||
"google/protobuf/descriptor.pb.h");
|
||||
context.ExpectFileMatches("google/protobuf/descriptor.pb.cc",
|
||||
"google/protobuf/descriptor.pb.cc");
|
||||
context.ExpectFileMatches("google/protobuf/compiler/plugin.pb.h",
|
||||
"google/protobuf/compiler/plugin.pb.h");
|
||||
context.ExpectFileMatches("google/protobuf/compiler/plugin.pb.cc",
|
||||
"google/protobuf/compiler/plugin.pb.cc");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -85,7 +85,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const {
|
||||
"}\n"
|
||||
"inline void $classname$::set_$name$($type$ value) {\n"
|
||||
" GOOGLE_DCHECK($type$_IsValid(value));\n"
|
||||
" _set_bit($index$);\n"
|
||||
" set_has_$name$();\n"
|
||||
" $name$_ = value;\n"
|
||||
"}\n");
|
||||
}
|
||||
|
@ -283,6 +283,7 @@ void FileGenerator::GenerateSource(io::Printer* printer) {
|
||||
printer->Print(
|
||||
"// Generated by the protocol buffer compiler. DO NOT EDIT!\n"
|
||||
"\n"
|
||||
|
||||
// The generated code calls accessors that might be deprecated. We don't
|
||||
// want the compiler to warn in generated code.
|
||||
"#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION\n"
|
||||
|
@ -53,7 +53,7 @@ CppGenerator::~CppGenerator() {}
|
||||
|
||||
bool CppGenerator::Generate(const FileDescriptor* file,
|
||||
const string& parameter,
|
||||
OutputDirectory* output_directory,
|
||||
GeneratorContext* generator_context,
|
||||
string* error) const {
|
||||
vector<pair<string, string> > options;
|
||||
ParseGeneratorParameter(parameter, &options);
|
||||
@ -100,7 +100,7 @@ bool CppGenerator::Generate(const FileDescriptor* file,
|
||||
// Generate header.
|
||||
{
|
||||
scoped_ptr<io::ZeroCopyOutputStream> output(
|
||||
output_directory->Open(basename + ".h"));
|
||||
generator_context->Open(basename + ".h"));
|
||||
io::Printer printer(output.get(), '$');
|
||||
file_generator.GenerateHeader(&printer);
|
||||
}
|
||||
@ -108,7 +108,7 @@ bool CppGenerator::Generate(const FileDescriptor* file,
|
||||
// Generate cc file.
|
||||
{
|
||||
scoped_ptr<io::ZeroCopyOutputStream> output(
|
||||
output_directory->Open(basename + ".cc"));
|
||||
generator_context->Open(basename + ".cc"));
|
||||
io::Printer printer(output.get(), '$');
|
||||
file_generator.GenerateSource(&printer);
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ class LIBPROTOC_EXPORT CppGenerator : public CodeGenerator {
|
||||
// implements CodeGenerator ----------------------------------------
|
||||
bool Generate(const FileDescriptor* file,
|
||||
const string& parameter,
|
||||
OutputDirectory* output_directory,
|
||||
GeneratorContext* generator_context,
|
||||
string* error) const;
|
||||
|
||||
private:
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include <algorithm>
|
||||
#include <google/protobuf/stubs/hash.h>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <google/protobuf/compiler/cpp/cpp_message.h>
|
||||
#include <google/protobuf/compiler/cpp/cpp_field.h>
|
||||
@ -143,6 +144,137 @@ static bool HasRequiredFields(const Descriptor* type) {
|
||||
return HasRequiredFields(type, &already_seen);
|
||||
}
|
||||
|
||||
// This returns an estimate of the compiler's alignment for the field. This
|
||||
// can't guarantee to be correct because the generated code could be compiled on
|
||||
// different systems with different alignment rules. The estimates below assume
|
||||
// 64-bit pointers.
|
||||
int EstimateAlignmentSize(const FieldDescriptor* field) {
|
||||
if (field == NULL) return 0;
|
||||
if (field->is_repeated()) return 8;
|
||||
switch (field->cpp_type()) {
|
||||
case FieldDescriptor::CPPTYPE_BOOL:
|
||||
return 1;
|
||||
|
||||
case FieldDescriptor::CPPTYPE_INT32:
|
||||
case FieldDescriptor::CPPTYPE_UINT32:
|
||||
case FieldDescriptor::CPPTYPE_ENUM:
|
||||
case FieldDescriptor::CPPTYPE_FLOAT:
|
||||
return 4;
|
||||
|
||||
case FieldDescriptor::CPPTYPE_INT64:
|
||||
case FieldDescriptor::CPPTYPE_UINT64:
|
||||
case FieldDescriptor::CPPTYPE_DOUBLE:
|
||||
case FieldDescriptor::CPPTYPE_STRING:
|
||||
case FieldDescriptor::CPPTYPE_MESSAGE:
|
||||
return 8;
|
||||
}
|
||||
GOOGLE_LOG(FATAL) << "Can't get here.";
|
||||
return -1; // Make compiler happy.
|
||||
}
|
||||
|
||||
// FieldGroup is just a helper for OptimizePadding below. It holds a vector of
|
||||
// fields that are grouped together because they have compatible alignment, and
|
||||
// a preferred location in the final field ordering.
|
||||
class FieldGroup {
|
||||
public:
|
||||
FieldGroup()
|
||||
: preferred_location_(0) {}
|
||||
|
||||
// A group with a single field.
|
||||
FieldGroup(float preferred_location, const FieldDescriptor* field)
|
||||
: preferred_location_(preferred_location),
|
||||
fields_(1, field) {}
|
||||
|
||||
// Append the fields in 'other' to this group.
|
||||
void Append(const FieldGroup& other) {
|
||||
if (other.fields_.empty()) {
|
||||
return;
|
||||
}
|
||||
// Preferred location is the average among all the fields, so we weight by
|
||||
// the number of fields on each FieldGroup object.
|
||||
preferred_location_ =
|
||||
(preferred_location_ * fields_.size() +
|
||||
(other.preferred_location_ * other.fields_.size())) /
|
||||
(fields_.size() + other.fields_.size());
|
||||
fields_.insert(fields_.end(), other.fields_.begin(), other.fields_.end());
|
||||
}
|
||||
|
||||
void SetPreferredLocation(float location) { preferred_location_ = location; }
|
||||
const vector<const FieldDescriptor*>& fields() const { return fields_; }
|
||||
|
||||
// FieldGroup objects sort by their preferred location.
|
||||
bool operator<(const FieldGroup& other) const {
|
||||
return preferred_location_ < other.preferred_location_;
|
||||
}
|
||||
|
||||
private:
|
||||
// "preferred_location_" is an estimate of where this group should go in the
|
||||
// final list of fields. We compute this by taking the average index of each
|
||||
// field in this group in the original ordering of fields. This is very
|
||||
// approximate, but should put this group close to where its member fields
|
||||
// originally went.
|
||||
float preferred_location_;
|
||||
vector<const FieldDescriptor*> fields_;
|
||||
// We rely on the default copy constructor and operator= so this type can be
|
||||
// used in a vector.
|
||||
};
|
||||
|
||||
// Reorder 'fields' so that if the fields are output into a c++ class in the new
|
||||
// order, the alignment padding is minimized. We try to do this while keeping
|
||||
// each field as close as possible to its original position so that we don't
|
||||
// reduce cache locality much for function that access each field in order.
|
||||
void OptimizePadding(vector<const FieldDescriptor*>* fields) {
|
||||
// First divide fields into those that align to 1 byte, 4 bytes or 8 bytes.
|
||||
vector<FieldGroup> aligned_to_1, aligned_to_4, aligned_to_8;
|
||||
for (int i = 0; i < fields->size(); ++i) {
|
||||
switch (EstimateAlignmentSize((*fields)[i])) {
|
||||
case 1: aligned_to_1.push_back(FieldGroup(i, (*fields)[i])); break;
|
||||
case 4: aligned_to_4.push_back(FieldGroup(i, (*fields)[i])); break;
|
||||
case 8: aligned_to_8.push_back(FieldGroup(i, (*fields)[i])); break;
|
||||
default:
|
||||
GOOGLE_LOG(FATAL) << "Unknown alignment size.";
|
||||
}
|
||||
}
|
||||
|
||||
// Now group fields aligned to 1 byte into sets of 4, and treat those like a
|
||||
// single field aligned to 4 bytes.
|
||||
for (int i = 0; i < aligned_to_1.size(); i += 4) {
|
||||
FieldGroup field_group;
|
||||
for (int j = i; j < aligned_to_1.size() && j < i + 4; ++j) {
|
||||
field_group.Append(aligned_to_1[j]);
|
||||
}
|
||||
aligned_to_4.push_back(field_group);
|
||||
}
|
||||
// Sort by preferred location to keep fields as close to their original
|
||||
// location as possible.
|
||||
sort(aligned_to_4.begin(), aligned_to_4.end());
|
||||
|
||||
// Now group fields aligned to 4 bytes (or the 4-field groups created above)
|
||||
// into pairs, and treat those like a single field aligned to 8 bytes.
|
||||
for (int i = 0; i < aligned_to_4.size(); i += 2) {
|
||||
FieldGroup field_group;
|
||||
for (int j = i; j < aligned_to_4.size() && j < i + 2; ++j) {
|
||||
field_group.Append(aligned_to_4[j]);
|
||||
}
|
||||
if (i == aligned_to_4.size() - 1) {
|
||||
// Move incomplete 4-byte block to the end.
|
||||
field_group.SetPreferredLocation(fields->size() + 1);
|
||||
}
|
||||
aligned_to_8.push_back(field_group);
|
||||
}
|
||||
// Sort by preferred location to keep fields as close to their original
|
||||
// location as possible.
|
||||
sort(aligned_to_8.begin(), aligned_to_8.end());
|
||||
|
||||
// Now pull out all the FieldDescriptors in order.
|
||||
fields->clear();
|
||||
for (int i = 0; i < aligned_to_8.size(); ++i) {
|
||||
fields->insert(fields->end(),
|
||||
aligned_to_8[i].fields().begin(),
|
||||
aligned_to_8[i].fields().end());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
@ -264,10 +396,20 @@ GenerateFieldAccessorDefinitions(io::Printer* printer) {
|
||||
"}\n");
|
||||
} else {
|
||||
// Singular field.
|
||||
char buffer[kFastToBufferSize];
|
||||
vars["has_array_index"] = SimpleItoa(field->index() / 32);
|
||||
vars["has_mask"] = FastHex32ToBuffer(1u << (field->index() % 32), buffer);
|
||||
printer->Print(vars,
|
||||
"inline bool $classname$::has_$name$() const {\n"
|
||||
" return _has_bit($index$);\n"
|
||||
"}\n");
|
||||
" return (_has_bits_[$has_array_index$] & 0x$has_mask$u) != 0;\n"
|
||||
"}\n"
|
||||
"inline void $classname$::set_has_$name$() {\n"
|
||||
" _has_bits_[$has_array_index$] |= 0x$has_mask$u;\n"
|
||||
"}\n"
|
||||
"inline void $classname$::clear_has_$name$() {\n"
|
||||
" _has_bits_[$has_array_index$] &= ~0x$has_mask$u;\n"
|
||||
"}\n"
|
||||
);
|
||||
}
|
||||
|
||||
// Generate clear_$name$()
|
||||
@ -279,7 +421,8 @@ GenerateFieldAccessorDefinitions(io::Printer* printer) {
|
||||
printer->Outdent();
|
||||
|
||||
if (!field->is_repeated()) {
|
||||
printer->Print(vars, " _clear_bit($index$);\n");
|
||||
printer->Print(vars,
|
||||
" clear_has_$name$();\n");
|
||||
}
|
||||
|
||||
printer->Print("}\n");
|
||||
@ -444,28 +587,74 @@ GenerateClassDefinition(io::Printer* printer) {
|
||||
"// @@protoc_insertion_point(class_scope:$full_name$)\n",
|
||||
"full_name", descriptor_->full_name());
|
||||
|
||||
// Generate private members for fields.
|
||||
// Generate private members.
|
||||
printer->Outdent();
|
||||
printer->Print(" private:\n");
|
||||
printer->Indent();
|
||||
|
||||
for (int i = 0; i < descriptor_->field_count(); i++) {
|
||||
if (!descriptor_->field(i)->is_repeated()) {
|
||||
printer->Print(
|
||||
"inline void set_has_$name$();\n",
|
||||
"name", FieldName(descriptor_->field(i)));
|
||||
printer->Print(
|
||||
"inline void clear_has_$name$();\n",
|
||||
"name", FieldName(descriptor_->field(i)));
|
||||
}
|
||||
}
|
||||
printer->Print("\n");
|
||||
|
||||
// To minimize padding, data members are divided into three sections:
|
||||
// (1) members assumed to align to 8 bytes
|
||||
// (2) members corresponding to message fields, re-ordered to optimize
|
||||
// alignment.
|
||||
// (3) members assumed to align to 4 bytes.
|
||||
|
||||
// Members assumed to align to 8 bytes:
|
||||
|
||||
if (descriptor_->extension_range_count() > 0) {
|
||||
printer->Print(
|
||||
"::google::protobuf::internal::ExtensionSet _extensions_;\n");
|
||||
"::google::protobuf::internal::ExtensionSet _extensions_;\n"
|
||||
"\n");
|
||||
}
|
||||
|
||||
if (HasUnknownFields(descriptor_->file())) {
|
||||
printer->Print(
|
||||
"::google::protobuf::UnknownFieldSet _unknown_fields_;\n");
|
||||
"::google::protobuf::UnknownFieldSet _unknown_fields_;\n"
|
||||
"\n");
|
||||
}
|
||||
|
||||
// Field members:
|
||||
|
||||
vector<const FieldDescriptor*> fields;
|
||||
for (int i = 0; i < descriptor_->field_count(); i++) {
|
||||
fields.push_back(descriptor_->field(i));
|
||||
}
|
||||
OptimizePadding(&fields);
|
||||
for (int i = 0; i < fields.size(); ++i) {
|
||||
field_generators_.get(fields[i]).GeneratePrivateMembers(printer);
|
||||
}
|
||||
|
||||
// Members assumed to align to 4 bytes:
|
||||
|
||||
// TODO(kenton): Make _cached_size_ an atomic<int> when C++ supports it.
|
||||
printer->Print(
|
||||
"mutable int _cached_size_;\n"
|
||||
"\n");
|
||||
for (int i = 0; i < descriptor_->field_count(); i++) {
|
||||
field_generators_.get(descriptor_->field(i))
|
||||
.GeneratePrivateMembers(printer);
|
||||
"\n"
|
||||
"mutable int _cached_size_;\n");
|
||||
|
||||
// Generate _has_bits_.
|
||||
if (descriptor_->field_count() > 0) {
|
||||
printer->Print(vars,
|
||||
"::google::protobuf::uint32 _has_bits_[($field_count$ + 31) / 32];\n"
|
||||
"\n");
|
||||
} else {
|
||||
// Zero-size arrays aren't technically allowed, and MSVC in particular
|
||||
// doesn't like them. We still need to declare these arrays to make
|
||||
// other code compile. Since this is an uncommon case, we'll just declare
|
||||
// them with size 1 and waste some space. Oh well.
|
||||
printer->Print(
|
||||
"::google::protobuf::uint32 _has_bits_[1];\n"
|
||||
"\n");
|
||||
}
|
||||
|
||||
// Declare AddDescriptors(), BuildDescriptors(), and ShutdownFile() as
|
||||
@ -484,32 +673,7 @@ GenerateClassDefinition(io::Printer* printer) {
|
||||
GlobalAssignDescriptorsName(descriptor_->file()->name()),
|
||||
"shutdownfilename", GlobalShutdownFileName(descriptor_->file()->name()));
|
||||
|
||||
// Generate offsets and _has_bits_ boilerplate.
|
||||
if (descriptor_->field_count() > 0) {
|
||||
printer->Print(vars,
|
||||
"::google::protobuf::uint32 _has_bits_[($field_count$ + 31) / 32];\n");
|
||||
} else {
|
||||
// Zero-size arrays aren't technically allowed, and MSVC in particular
|
||||
// doesn't like them. We still need to declare these arrays to make
|
||||
// other code compile. Since this is an uncommon case, we'll just declare
|
||||
// them with size 1 and waste some space. Oh well.
|
||||
printer->Print(
|
||||
"::google::protobuf::uint32 _has_bits_[1];\n");
|
||||
}
|
||||
|
||||
printer->Print(
|
||||
"\n"
|
||||
"// WHY DOES & HAVE LOWER PRECEDENCE THAN != !?\n"
|
||||
"inline bool _has_bit(int index) const {\n"
|
||||
" return (_has_bits_[index / 32] & (1u << (index % 32))) != 0;\n"
|
||||
"}\n"
|
||||
"inline void _set_bit(int index) {\n"
|
||||
" _has_bits_[index / 32] |= (1u << (index % 32));\n"
|
||||
"}\n"
|
||||
"inline void _clear_bit(int index) {\n"
|
||||
" _has_bits_[index / 32] &= ~(1u << (index % 32));\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"void InitAsDefaultInstance();\n"
|
||||
"static $classname$* default_instance_;\n",
|
||||
"classname", classname_);
|
||||
@ -961,9 +1125,6 @@ GenerateClear(io::Printer* printer) {
|
||||
const FieldDescriptor* field = descriptor_->field(i);
|
||||
|
||||
if (!field->is_repeated()) {
|
||||
map<string, string> vars;
|
||||
vars["index"] = SimpleItoa(field->index());
|
||||
|
||||
// We can use the fact that _has_bits_ is a giant bitfield to our
|
||||
// advantage: We can check up to 32 bits at a time for equality to
|
||||
// zero, and skip the whole range if so. This can improve the speed
|
||||
@ -975,8 +1136,9 @@ GenerateClear(io::Printer* printer) {
|
||||
printer->Outdent();
|
||||
printer->Print("}\n");
|
||||
}
|
||||
printer->Print(vars,
|
||||
"if (_has_bits_[$index$ / 32] & (0xffu << ($index$ % 32))) {\n");
|
||||
printer->Print(
|
||||
"if (_has_bits_[$index$ / 32] & (0xffu << ($index$ % 32))) {\n",
|
||||
"index", SimpleItoa(field->index()));
|
||||
printer->Indent();
|
||||
}
|
||||
last_index = i;
|
||||
@ -989,7 +1151,9 @@ GenerateClear(io::Printer* printer) {
|
||||
field->cpp_type() == FieldDescriptor::CPPTYPE_STRING;
|
||||
|
||||
if (should_check_bit) {
|
||||
printer->Print(vars, "if (_has_bit($index$)) {\n");
|
||||
printer->Print(
|
||||
"if (has_$name$()) {\n",
|
||||
"name", FieldName(field));
|
||||
printer->Indent();
|
||||
}
|
||||
|
||||
@ -1129,24 +1293,23 @@ GenerateMergeFrom(io::Printer* printer) {
|
||||
const FieldDescriptor* field = descriptor_->field(i);
|
||||
|
||||
if (!field->is_repeated()) {
|
||||
map<string, string> vars;
|
||||
vars["index"] = SimpleItoa(field->index());
|
||||
|
||||
// See above in GenerateClear for an explanation of this.
|
||||
if (i / 8 != last_index / 8 || last_index < 0) {
|
||||
if (last_index >= 0) {
|
||||
printer->Outdent();
|
||||
printer->Print("}\n");
|
||||
}
|
||||
printer->Print(vars,
|
||||
"if (from._has_bits_[$index$ / 32] & (0xffu << ($index$ % 32))) {\n");
|
||||
printer->Print(
|
||||
"if (from._has_bits_[$index$ / 32] & (0xffu << ($index$ % 32))) {\n",
|
||||
"index", SimpleItoa(field->index()));
|
||||
printer->Indent();
|
||||
}
|
||||
|
||||
last_index = i;
|
||||
|
||||
printer->Print(vars,
|
||||
"if (from._has_bit($index$)) {\n");
|
||||
printer->Print(
|
||||
"if (from.has_$name$()) {\n",
|
||||
"name", FieldName(field));
|
||||
printer->Indent();
|
||||
|
||||
field_generators_.get(field).GenerateMergingCode(printer);
|
||||
@ -1423,8 +1586,8 @@ void MessageGenerator::GenerateSerializeOneField(
|
||||
|
||||
if (!field->is_repeated()) {
|
||||
printer->Print(
|
||||
"if (_has_bit($index$)) {\n",
|
||||
"index", SimpleItoa(field->index()));
|
||||
"if (has_$name$()) {\n",
|
||||
"name", FieldName(field));
|
||||
printer->Indent();
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,8 @@ void MessageFieldGenerator::
|
||||
GenerateAccessorDeclarations(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
"inline const $type$& $name$() const$deprecation$;\n"
|
||||
"inline $type$* mutable_$name$()$deprecation$;\n");
|
||||
"inline $type$* mutable_$name$()$deprecation$;\n"
|
||||
"inline $type$* release_$name$()$deprecation$;\n");
|
||||
}
|
||||
|
||||
void MessageFieldGenerator::
|
||||
@ -85,9 +86,15 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const {
|
||||
" return $name$_ != NULL ? *$name$_ : *default_instance_->$name$_;\n"
|
||||
"}\n"
|
||||
"inline $type$* $classname$::mutable_$name$() {\n"
|
||||
" _set_bit($index$);\n"
|
||||
" set_has_$name$();\n"
|
||||
" if ($name$_ == NULL) $name$_ = new $type$;\n"
|
||||
" return $name$_;\n"
|
||||
"}\n"
|
||||
"inline $type$* $classname$::release_$name$() {\n"
|
||||
" clear_has_$name$();\n"
|
||||
" $type$* temp = $name$_;\n"
|
||||
" $name$_ = NULL;\n"
|
||||
" return temp;\n"
|
||||
"}\n");
|
||||
}
|
||||
|
||||
|
@ -56,24 +56,24 @@ class TestGenerator : public CodeGenerator {
|
||||
|
||||
virtual bool Generate(const FileDescriptor* file,
|
||||
const string& parameter,
|
||||
OutputDirectory* output_directory,
|
||||
GeneratorContext* context,
|
||||
string* error) const {
|
||||
TryInsert("test.pb.h", "includes", output_directory);
|
||||
TryInsert("test.pb.h", "namespace_scope", output_directory);
|
||||
TryInsert("test.pb.h", "global_scope", output_directory);
|
||||
TryInsert("test.pb.h", "class_scope:foo.Bar", output_directory);
|
||||
TryInsert("test.pb.h", "class_scope:foo.Bar.Baz", output_directory);
|
||||
TryInsert("test.pb.h", "includes", context);
|
||||
TryInsert("test.pb.h", "namespace_scope", context);
|
||||
TryInsert("test.pb.h", "global_scope", context);
|
||||
TryInsert("test.pb.h", "class_scope:foo.Bar", context);
|
||||
TryInsert("test.pb.h", "class_scope:foo.Bar.Baz", context);
|
||||
|
||||
TryInsert("test.pb.cc", "includes", output_directory);
|
||||
TryInsert("test.pb.cc", "namespace_scope", output_directory);
|
||||
TryInsert("test.pb.cc", "global_scope", output_directory);
|
||||
TryInsert("test.pb.cc", "includes", context);
|
||||
TryInsert("test.pb.cc", "namespace_scope", context);
|
||||
TryInsert("test.pb.cc", "global_scope", context);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TryInsert(const string& filename, const string& insertion_point,
|
||||
OutputDirectory* output_directory) const {
|
||||
GeneratorContext* context) const {
|
||||
scoped_ptr<io::ZeroCopyOutputStream> output(
|
||||
output_directory->OpenForInsert(filename, insertion_point));
|
||||
context->OpenForInsert(filename, insertion_point));
|
||||
io::Printer printer(output.get(), '$');
|
||||
printer.Print("// inserted $name$\n", "name", insertion_point);
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const {
|
||||
" return $name$_;\n"
|
||||
"}\n"
|
||||
"inline void $classname$::set_$name$($type$ value) {\n"
|
||||
" _set_bit($index$);\n"
|
||||
" set_has_$name$();\n"
|
||||
" $name$_ = value;\n"
|
||||
"}\n");
|
||||
}
|
||||
@ -156,7 +156,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) const {
|
||||
"DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<\n"
|
||||
" $type$, $wire_format_field_type$>(\n"
|
||||
" input, &$name$_)));\n"
|
||||
"_set_bit($index$);\n");
|
||||
"set_has_$name$();\n");
|
||||
}
|
||||
|
||||
void PrimitiveFieldGenerator::
|
||||
|
@ -50,6 +50,9 @@ void SetStringVariables(const FieldDescriptor* descriptor,
|
||||
SetCommonFieldVariables(descriptor, variables);
|
||||
(*variables)["default"] =
|
||||
"\"" + CEscape(descriptor->default_value_string()) + "\"";
|
||||
(*variables)["default_variable"] = descriptor->default_value_string().empty()
|
||||
? "::google::protobuf::internal::kEmptyString"
|
||||
: "_default_" + FieldName(descriptor) + "_";
|
||||
(*variables)["pointer_type"] =
|
||||
descriptor->type() == FieldDescriptor::TYPE_BYTES ? "void" : "char";
|
||||
}
|
||||
@ -68,9 +71,10 @@ StringFieldGenerator::~StringFieldGenerator() {}
|
||||
|
||||
void StringFieldGenerator::
|
||||
GeneratePrivateMembers(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
"::std::string* $name$_;\n"
|
||||
"static const ::std::string _default_$name$_;\n");
|
||||
printer->Print(variables_, "::std::string* $name$_;\n");
|
||||
if (!descriptor_->default_value_string().empty()) {
|
||||
printer->Print(variables_, "static const ::std::string $default_variable$;\n");
|
||||
}
|
||||
}
|
||||
|
||||
void StringFieldGenerator::
|
||||
@ -105,7 +109,8 @@ GenerateAccessorDeclarations(io::Printer* printer) const {
|
||||
"inline void set_$name$(const char* value)$deprecation$;\n"
|
||||
"inline void set_$name$(const $pointer_type$* value, size_t size)"
|
||||
"$deprecation$;\n"
|
||||
"inline ::std::string* mutable_$name$()$deprecation$;\n");
|
||||
"inline ::std::string* mutable_$name$()$deprecation$;\n"
|
||||
"inline ::std::string* release_$name$()$deprecation$;\n");
|
||||
|
||||
if (descriptor_->options().ctype() != FieldOptions::STRING) {
|
||||
printer->Outdent();
|
||||
@ -121,51 +126,58 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const {
|
||||
" return *$name$_;\n"
|
||||
"}\n"
|
||||
"inline void $classname$::set_$name$(const ::std::string& value) {\n"
|
||||
" _set_bit($index$);\n"
|
||||
" if ($name$_ == &_default_$name$_) {\n"
|
||||
" set_has_$name$();\n"
|
||||
" if ($name$_ == &$default_variable$) {\n"
|
||||
" $name$_ = new ::std::string;\n"
|
||||
" }\n"
|
||||
" $name$_->assign(value);\n"
|
||||
"}\n"
|
||||
"inline void $classname$::set_$name$(const char* value) {\n"
|
||||
" _set_bit($index$);\n"
|
||||
" if ($name$_ == &_default_$name$_) {\n"
|
||||
" set_has_$name$();\n"
|
||||
" if ($name$_ == &$default_variable$) {\n"
|
||||
" $name$_ = new ::std::string;\n"
|
||||
" }\n"
|
||||
" $name$_->assign(value);\n"
|
||||
"}\n"
|
||||
"inline "
|
||||
"void $classname$::set_$name$(const $pointer_type$* value, size_t size) {\n"
|
||||
" _set_bit($index$);\n"
|
||||
" if ($name$_ == &_default_$name$_) {\n"
|
||||
" set_has_$name$();\n"
|
||||
" if ($name$_ == &$default_variable$) {\n"
|
||||
" $name$_ = new ::std::string;\n"
|
||||
" }\n"
|
||||
" $name$_->assign(reinterpret_cast<const char*>(value), size);\n"
|
||||
"}\n"
|
||||
"inline ::std::string* $classname$::mutable_$name$() {\n"
|
||||
" _set_bit($index$);\n"
|
||||
" if ($name$_ == &_default_$name$_) {\n");
|
||||
" set_has_$name$();\n"
|
||||
" if ($name$_ == &$default_variable$) {\n");
|
||||
if (descriptor_->default_value_string().empty()) {
|
||||
printer->Print(variables_,
|
||||
" $name$_ = new ::std::string;\n");
|
||||
} else {
|
||||
printer->Print(variables_,
|
||||
" $name$_ = new ::std::string(_default_$name$_);\n");
|
||||
" $name$_ = new ::std::string($default_variable$);\n");
|
||||
}
|
||||
printer->Print(variables_,
|
||||
" }\n"
|
||||
" return $name$_;\n"
|
||||
"}\n"
|
||||
"inline ::std::string* $classname$::release_$name$() {\n"
|
||||
" clear_has_$name$();\n"
|
||||
" if ($name$_ == &$default_variable$) {\n"
|
||||
" return NULL;\n"
|
||||
" } else {\n"
|
||||
" ::std::string* temp = $name$_;\n"
|
||||
" $name$_ = const_cast< ::std::string*>(&$default_variable$);\n"
|
||||
" return temp;\n"
|
||||
" }\n"
|
||||
"}\n");
|
||||
}
|
||||
|
||||
void StringFieldGenerator::
|
||||
GenerateNonInlineAccessorDefinitions(io::Printer* printer) const {
|
||||
if (descriptor_->default_value_string().empty()) {
|
||||
if (!descriptor_->default_value_string().empty()) {
|
||||
printer->Print(variables_,
|
||||
"const ::std::string $classname$::_default_$name$_;\n");
|
||||
} else {
|
||||
printer->Print(variables_,
|
||||
"const ::std::string $classname$::_default_$name$_($default$);\n");
|
||||
"const ::std::string $classname$::$default_variable$($default$);\n");
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,13 +185,13 @@ void StringFieldGenerator::
|
||||
GenerateClearingCode(io::Printer* printer) const {
|
||||
if (descriptor_->default_value_string().empty()) {
|
||||
printer->Print(variables_,
|
||||
"if ($name$_ != &_default_$name$_) {\n"
|
||||
"if ($name$_ != &$default_variable$) {\n"
|
||||
" $name$_->clear();\n"
|
||||
"}\n");
|
||||
} else {
|
||||
printer->Print(variables_,
|
||||
"if ($name$_ != &_default_$name$_) {\n"
|
||||
" $name$_->assign(_default_$name$_);\n"
|
||||
"if ($name$_ != &$default_variable$) {\n"
|
||||
" $name$_->assign($default_variable$);\n"
|
||||
"}\n");
|
||||
}
|
||||
}
|
||||
@ -197,13 +209,13 @@ GenerateSwappingCode(io::Printer* printer) const {
|
||||
void StringFieldGenerator::
|
||||
GenerateConstructorCode(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
"$name$_ = const_cast< ::std::string*>(&_default_$name$_);\n");
|
||||
"$name$_ = const_cast< ::std::string*>(&$default_variable$);\n");
|
||||
}
|
||||
|
||||
void StringFieldGenerator::
|
||||
GenerateDestructorCode(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
"if ($name$_ != &_default_$name$_) {\n"
|
||||
"if ($name$_ != &$default_variable$) {\n"
|
||||
" delete $name$_;\n"
|
||||
"}\n");
|
||||
}
|
||||
|
@ -36,6 +36,10 @@
|
||||
// though the same identifiers are used internally by the C++ code generator.
|
||||
|
||||
|
||||
// Some generic_services option(s) added automatically.
|
||||
// See: http://go/proto2-generic-services-default
|
||||
option cc_generic_services = true; // auto-added
|
||||
|
||||
// We don't put this in a package within proto2 because we need to make sure
|
||||
// that the generated code doesn't depend on being in the proto2 namespace.
|
||||
package protobuf_unittest;
|
||||
|
@ -195,6 +195,48 @@ TEST(GeneratedMessageTest, MutableStringDefault) {
|
||||
EXPECT_EQ("hello", *message.mutable_default_string());
|
||||
}
|
||||
|
||||
TEST(GeneratedMessageTest, ReleaseString) {
|
||||
// Check that release_foo() starts out NULL, and gives us a value
|
||||
// that we can delete after it's been set.
|
||||
unittest::TestAllTypes message;
|
||||
|
||||
EXPECT_EQ(NULL, message.release_default_string());
|
||||
EXPECT_FALSE(message.has_default_string());
|
||||
EXPECT_EQ("hello", message.default_string());
|
||||
|
||||
message.set_default_string("blah");
|
||||
EXPECT_TRUE(message.has_default_string());
|
||||
string* str = message.release_default_string();
|
||||
EXPECT_FALSE(message.has_default_string());
|
||||
ASSERT_TRUE(str != NULL);
|
||||
EXPECT_EQ("blah", *str);
|
||||
delete str;
|
||||
|
||||
EXPECT_EQ(NULL, message.release_default_string());
|
||||
EXPECT_FALSE(message.has_default_string());
|
||||
EXPECT_EQ("hello", message.default_string());
|
||||
}
|
||||
|
||||
TEST(GeneratedMessageTest, ReleaseMessage) {
|
||||
// Check that release_foo() starts out NULL, and gives us a value
|
||||
// that we can delete after it's been set.
|
||||
unittest::TestAllTypes message;
|
||||
|
||||
EXPECT_EQ(NULL, message.release_optional_nested_message());
|
||||
EXPECT_FALSE(message.has_optional_nested_message());
|
||||
|
||||
message.mutable_optional_nested_message()->set_bb(1);
|
||||
unittest::TestAllTypes::NestedMessage* nest =
|
||||
message.release_optional_nested_message();
|
||||
EXPECT_FALSE(message.has_optional_nested_message());
|
||||
ASSERT_TRUE(nest != NULL);
|
||||
EXPECT_EQ(1, nest->bb());
|
||||
delete nest;
|
||||
|
||||
EXPECT_EQ(NULL, message.release_optional_nested_message());
|
||||
EXPECT_FALSE(message.has_optional_nested_message());
|
||||
}
|
||||
|
||||
TEST(GeneratedMessageTest, Clear) {
|
||||
// Set every field to a unique value, clear the message, then check that
|
||||
// it is cleared.
|
||||
@ -282,6 +324,7 @@ TEST(GeneratedMessageTest, CopyFrom) {
|
||||
TestUtil::ExpectAllFieldsSet(message2);
|
||||
}
|
||||
|
||||
|
||||
TEST(GeneratedMessageTest, SwapWithEmpty) {
|
||||
unittest::TestAllTypes message1, message2;
|
||||
TestUtil::SetAllFields(&message1);
|
||||
@ -376,7 +419,7 @@ TEST(GeneratedMessageTest, CopyAssignmentOperator) {
|
||||
TestUtil::ExpectAllFieldsSet(message2);
|
||||
|
||||
// Make sure that self-assignment does something sane.
|
||||
message2 = message2;
|
||||
message2.operator=(message2);
|
||||
TestUtil::ExpectAllFieldsSet(message2);
|
||||
}
|
||||
|
||||
@ -718,6 +761,7 @@ TEST(GeneratedMessageTest, TestSpaceUsed) {
|
||||
|
||||
#endif // !PROTOBUF_TEST_NO_DESCRIPTORS
|
||||
|
||||
|
||||
TEST(GeneratedMessageTest, FieldConstantValues) {
|
||||
unittest::TestRequired message;
|
||||
EXPECT_EQ(unittest::TestAllTypes_NestedMessage::kBbFieldNumber, 1);
|
||||
|
@ -104,6 +104,15 @@ void EnumGenerator::Generate(io::Printer* printer) {
|
||||
"public static final $classname$ $name$ = $canonical_name$;\n");
|
||||
}
|
||||
|
||||
for (int i = 0; i < descriptor_->value_count(); i++) {
|
||||
map<string, string> vars;
|
||||
vars["name"] = descriptor_->value(i)->name();
|
||||
vars["number"] = SimpleItoa(descriptor_->value(i)->number());
|
||||
printer->Print(vars,
|
||||
"public static final int $name$_VALUE = $number$;\n");
|
||||
}
|
||||
printer->Print("\n");
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
printer->Print(
|
||||
@ -219,17 +228,6 @@ void EnumGenerator::Generate(io::Printer* printer) {
|
||||
" this.value = value;\n"
|
||||
"}\n");
|
||||
|
||||
if (HasDescriptorMethods(descriptor_)) {
|
||||
// Force the static initialization code for the file to run, since it may
|
||||
// initialize static variables declared in this class.
|
||||
printer->Print(
|
||||
"\n"
|
||||
"static {\n"
|
||||
" $file$.getDescriptor();\n"
|
||||
"}\n",
|
||||
"file", ClassName(descriptor_->file()));
|
||||
}
|
||||
|
||||
printer->Print(
|
||||
"\n"
|
||||
"// @@protoc_insertion_point(enum_scope:$full_name$)\n",
|
||||
|
@ -52,17 +52,44 @@ namespace {
|
||||
// TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of
|
||||
// repeat code between this and the other field types.
|
||||
void SetEnumVariables(const FieldDescriptor* descriptor,
|
||||
int messageBitIndex,
|
||||
int builderBitIndex,
|
||||
map<string, string>* variables) {
|
||||
(*variables)["name"] =
|
||||
UnderscoresToCamelCase(descriptor);
|
||||
(*variables)["capitalized_name"] =
|
||||
UnderscoresToCapitalizedCamelCase(descriptor);
|
||||
(*variables)["constant_name"] = FieldConstantName(descriptor);
|
||||
(*variables)["number"] = SimpleItoa(descriptor->number());
|
||||
(*variables)["type"] = ClassName(descriptor->enum_type());
|
||||
(*variables)["default"] = DefaultValue(descriptor);
|
||||
(*variables)["tag"] = SimpleItoa(internal::WireFormat::MakeTag(descriptor));
|
||||
(*variables)["tag_size"] = SimpleItoa(
|
||||
internal::WireFormat::TagSize(descriptor->number(), GetType(descriptor)));
|
||||
// TODO(birdo): Add @deprecated javadoc when generating javadoc is supported
|
||||
// by the proto compiler
|
||||
(*variables)["deprecation"] = descriptor->options().deprecated()
|
||||
? "@java.lang.Deprecated " : "";
|
||||
(*variables)["on_changed"] =
|
||||
HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : "";
|
||||
|
||||
// For singular messages and builders, one bit is used for the hasField bit.
|
||||
(*variables)["get_has_field_bit_message"] = GenerateGetBit(messageBitIndex);
|
||||
|
||||
(*variables)["get_has_field_bit_builder"] = GenerateGetBit(builderBitIndex);
|
||||
(*variables)["set_has_field_bit_builder"] = GenerateSetBit(builderBitIndex);
|
||||
(*variables)["clear_has_field_bit_builder"] =
|
||||
GenerateClearBit(builderBitIndex);
|
||||
|
||||
// For repated builders, one bit is used for whether the array is immutable.
|
||||
(*variables)["get_mutable_bit_builder"] = GenerateGetBit(builderBitIndex);
|
||||
(*variables)["set_mutable_bit_builder"] = GenerateSetBit(builderBitIndex);
|
||||
(*variables)["clear_mutable_bit_builder"] = GenerateClearBit(builderBitIndex);
|
||||
|
||||
(*variables)["get_has_field_bit_from_local"] =
|
||||
GenerateGetBitFromLocal(builderBitIndex);
|
||||
(*variables)["set_has_field_bit_to_local"] =
|
||||
GenerateSetBitToLocal(messageBitIndex);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -70,51 +97,87 @@ void SetEnumVariables(const FieldDescriptor* descriptor,
|
||||
// ===================================================================
|
||||
|
||||
EnumFieldGenerator::
|
||||
EnumFieldGenerator(const FieldDescriptor* descriptor)
|
||||
: descriptor_(descriptor) {
|
||||
SetEnumVariables(descriptor, &variables_);
|
||||
EnumFieldGenerator(const FieldDescriptor* descriptor,
|
||||
int messageBitIndex,
|
||||
int builderBitIndex)
|
||||
: descriptor_(descriptor), messageBitIndex_(messageBitIndex),
|
||||
builderBitIndex_(builderBitIndex) {
|
||||
SetEnumVariables(descriptor, messageBitIndex, builderBitIndex, &variables_);
|
||||
}
|
||||
|
||||
EnumFieldGenerator::~EnumFieldGenerator() {}
|
||||
|
||||
int EnumFieldGenerator::GetNumBitsForMessage() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int EnumFieldGenerator::GetNumBitsForBuilder() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void EnumFieldGenerator::
|
||||
GenerateInterfaceMembers(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
"$deprecation$boolean has$capitalized_name$();\n"
|
||||
"$deprecation$$type$ get$capitalized_name$();\n");
|
||||
}
|
||||
|
||||
void EnumFieldGenerator::
|
||||
GenerateMembers(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
"private boolean has$capitalized_name$;\n"
|
||||
"private $type$ $name$_;\n"
|
||||
"public boolean has$capitalized_name$() { return has$capitalized_name$; }\n"
|
||||
"public $type$ get$capitalized_name$() { return $name$_; }\n");
|
||||
"$deprecation$public boolean has$capitalized_name$() {\n"
|
||||
" return $get_has_field_bit_message$;\n"
|
||||
"}\n"
|
||||
"$deprecation$public $type$ get$capitalized_name$() {\n"
|
||||
" return $name$_;\n"
|
||||
"}\n");
|
||||
}
|
||||
|
||||
void EnumFieldGenerator::
|
||||
GenerateBuilderMembers(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
"public boolean has$capitalized_name$() {\n"
|
||||
" return result.has$capitalized_name$();\n"
|
||||
"private $type$ $name$_ = $default$;\n"
|
||||
"$deprecation$public boolean has$capitalized_name$() {\n"
|
||||
" return $get_has_field_bit_builder$;\n"
|
||||
"}\n"
|
||||
"public $type$ get$capitalized_name$() {\n"
|
||||
" return result.get$capitalized_name$();\n"
|
||||
"$deprecation$public $type$ get$capitalized_name$() {\n"
|
||||
" return $name$_;\n"
|
||||
"}\n"
|
||||
"public Builder set$capitalized_name$($type$ value) {\n"
|
||||
"$deprecation$public Builder set$capitalized_name$($type$ value) {\n"
|
||||
" if (value == null) {\n"
|
||||
" throw new NullPointerException();\n"
|
||||
" }\n"
|
||||
" result.has$capitalized_name$ = true;\n"
|
||||
" result.$name$_ = value;\n"
|
||||
" $set_has_field_bit_builder$;\n"
|
||||
" $name$_ = value;\n"
|
||||
" $on_changed$\n"
|
||||
" return this;\n"
|
||||
"}\n"
|
||||
"public Builder clear$capitalized_name$() {\n"
|
||||
" result.has$capitalized_name$ = false;\n"
|
||||
" result.$name$_ = $default$;\n"
|
||||
"$deprecation$public Builder clear$capitalized_name$() {\n"
|
||||
" $clear_has_field_bit_builder$;\n"
|
||||
" $name$_ = $default$;\n"
|
||||
" $on_changed$\n"
|
||||
" return this;\n"
|
||||
"}\n");
|
||||
}
|
||||
|
||||
void EnumFieldGenerator::
|
||||
GenerateFieldBuilderInitializationCode(io::Printer* printer) const {
|
||||
// noop for enums
|
||||
}
|
||||
|
||||
void EnumFieldGenerator::
|
||||
GenerateInitializationCode(io::Printer* printer) const {
|
||||
printer->Print(variables_, "$name$_ = $default$;\n");
|
||||
}
|
||||
|
||||
void EnumFieldGenerator::
|
||||
GenerateBuilderClearCode(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
"$name$_ = $default$;\n"
|
||||
"$clear_has_field_bit_builder$;\n");
|
||||
}
|
||||
|
||||
void EnumFieldGenerator::
|
||||
GenerateMergingCode(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
@ -125,7 +188,11 @@ GenerateMergingCode(io::Printer* printer) const {
|
||||
|
||||
void EnumFieldGenerator::
|
||||
GenerateBuildingCode(io::Printer* printer) const {
|
||||
// Nothing to do here for enum types.
|
||||
printer->Print(variables_,
|
||||
"if ($get_has_field_bit_from_local$) {\n"
|
||||
" $set_has_field_bit_to_local$;\n"
|
||||
"}\n"
|
||||
"result.$name$_ = $name$_;\n");
|
||||
}
|
||||
|
||||
void EnumFieldGenerator::
|
||||
@ -143,27 +210,42 @@ GenerateParsingCode(io::Printer* printer) const {
|
||||
"if (value != null) {\n");
|
||||
}
|
||||
printer->Print(variables_,
|
||||
" set$capitalized_name$(value);\n"
|
||||
" $set_has_field_bit_builder$;\n"
|
||||
" $name$_ = value;\n"
|
||||
"}\n");
|
||||
}
|
||||
|
||||
void EnumFieldGenerator::
|
||||
GenerateSerializationCode(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
"if (has$capitalized_name$()) {\n"
|
||||
" output.writeEnum($number$, get$capitalized_name$().getNumber());\n"
|
||||
"if ($get_has_field_bit_message$) {\n"
|
||||
" output.writeEnum($number$, $name$_.getNumber());\n"
|
||||
"}\n");
|
||||
}
|
||||
|
||||
void EnumFieldGenerator::
|
||||
GenerateSerializedSizeCode(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
"if (has$capitalized_name$()) {\n"
|
||||
"if ($get_has_field_bit_message$) {\n"
|
||||
" size += com.google.protobuf.CodedOutputStream\n"
|
||||
" .computeEnumSize($number$, get$capitalized_name$().getNumber());\n"
|
||||
" .computeEnumSize($number$, $name$_.getNumber());\n"
|
||||
"}\n");
|
||||
}
|
||||
|
||||
void EnumFieldGenerator::
|
||||
GenerateEqualsCode(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
"result = result &&\n"
|
||||
" (get$capitalized_name$() == other.get$capitalized_name$());\n");
|
||||
}
|
||||
|
||||
void EnumFieldGenerator::
|
||||
GenerateHashCode(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
"hash = (37 * hash) + $constant_name$;\n"
|
||||
"hash = (53 * hash) + hashEnum(get$capitalized_name$());\n");
|
||||
}
|
||||
|
||||
string EnumFieldGenerator::GetBoxedType() const {
|
||||
return ClassName(descriptor_->enum_type());
|
||||
}
|
||||
@ -171,23 +253,43 @@ string EnumFieldGenerator::GetBoxedType() const {
|
||||
// ===================================================================
|
||||
|
||||
RepeatedEnumFieldGenerator::
|
||||
RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor)
|
||||
: descriptor_(descriptor) {
|
||||
SetEnumVariables(descriptor, &variables_);
|
||||
RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor,
|
||||
int messageBitIndex,
|
||||
int builderBitIndex)
|
||||
: descriptor_(descriptor), messageBitIndex_(messageBitIndex),
|
||||
builderBitIndex_(builderBitIndex) {
|
||||
SetEnumVariables(descriptor, messageBitIndex, builderBitIndex, &variables_);
|
||||
}
|
||||
|
||||
RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {}
|
||||
|
||||
int RepeatedEnumFieldGenerator::GetNumBitsForMessage() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RepeatedEnumFieldGenerator::GetNumBitsForBuilder() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void RepeatedEnumFieldGenerator::
|
||||
GenerateInterfaceMembers(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
"$deprecation$java.util.List<$type$> get$capitalized_name$List();\n"
|
||||
"$deprecation$int get$capitalized_name$Count();\n"
|
||||
"$deprecation$$type$ get$capitalized_name$(int index);\n");
|
||||
}
|
||||
|
||||
void RepeatedEnumFieldGenerator::
|
||||
GenerateMembers(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
"private java.util.List<$type$> $name$_ =\n"
|
||||
" java.util.Collections.emptyList();\n"
|
||||
"public java.util.List<$type$> get$capitalized_name$List() {\n"
|
||||
"private java.util.List<$type$> $name$_;\n"
|
||||
"$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n"
|
||||
" return $name$_;\n" // note: unmodifiable list
|
||||
"}\n"
|
||||
"public int get$capitalized_name$Count() { return $name$_.size(); }\n"
|
||||
"public $type$ get$capitalized_name$(int index) {\n"
|
||||
"$deprecation$public int get$capitalized_name$Count() {\n"
|
||||
" return $name$_.size();\n"
|
||||
"}\n"
|
||||
"$deprecation$public $type$ get$capitalized_name$(int index) {\n"
|
||||
" return $name$_.get(index);\n"
|
||||
"}\n");
|
||||
|
||||
@ -201,73 +303,119 @@ GenerateMembers(io::Printer* printer) const {
|
||||
void RepeatedEnumFieldGenerator::
|
||||
GenerateBuilderMembers(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
// One field is the list and the other field keeps track of whether the
|
||||
// list is immutable. If it's immutable, the invariant is that it must
|
||||
// either an instance of Collections.emptyList() or it's an ArrayList
|
||||
// wrapped in a Collections.unmodifiableList() wrapper and nobody else has
|
||||
// a refererence to the underlying ArrayList. This invariant allows us to
|
||||
// share instances of lists between protocol buffers avoiding expensive
|
||||
// memory allocations. Note, immutable is a strong guarantee here -- not
|
||||
// just that the list cannot be modified via the reference but that the
|
||||
// list can never be modified.
|
||||
"private java.util.List<$type$> $name$_ =\n"
|
||||
" java.util.Collections.emptyList();\n"
|
||||
|
||||
"private void ensure$capitalized_name$IsMutable() {\n"
|
||||
" if (!$get_mutable_bit_builder$) {\n"
|
||||
" $name$_ = new java.util.ArrayList<$type$>($name$_);\n"
|
||||
" $set_mutable_bit_builder$;\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
|
||||
// Note: We return an unmodifiable list because otherwise the caller
|
||||
// could hold on to the returned list and modify it after the message
|
||||
// has been built, thus mutating the message which is supposed to be
|
||||
// immutable.
|
||||
"public java.util.List<$type$> get$capitalized_name$List() {\n"
|
||||
" return java.util.Collections.unmodifiableList(result.$name$_);\n"
|
||||
"$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n"
|
||||
" return java.util.Collections.unmodifiableList($name$_);\n"
|
||||
"}\n"
|
||||
"public int get$capitalized_name$Count() {\n"
|
||||
" return result.get$capitalized_name$Count();\n"
|
||||
"$deprecation$public int get$capitalized_name$Count() {\n"
|
||||
" return $name$_.size();\n"
|
||||
"}\n"
|
||||
"public $type$ get$capitalized_name$(int index) {\n"
|
||||
" return result.get$capitalized_name$(index);\n"
|
||||
"$deprecation$public $type$ get$capitalized_name$(int index) {\n"
|
||||
" return $name$_.get(index);\n"
|
||||
"}\n"
|
||||
"public Builder set$capitalized_name$(int index, $type$ value) {\n"
|
||||
"$deprecation$public Builder set$capitalized_name$(\n"
|
||||
" int index, $type$ value) {\n"
|
||||
" if (value == null) {\n"
|
||||
" throw new NullPointerException();\n"
|
||||
" }\n"
|
||||
" result.$name$_.set(index, value);\n"
|
||||
" ensure$capitalized_name$IsMutable();\n"
|
||||
" $name$_.set(index, value);\n"
|
||||
" $on_changed$\n"
|
||||
" return this;\n"
|
||||
"}\n"
|
||||
"public Builder add$capitalized_name$($type$ value) {\n"
|
||||
"$deprecation$public Builder add$capitalized_name$($type$ value) {\n"
|
||||
" if (value == null) {\n"
|
||||
" throw new NullPointerException();\n"
|
||||
" }\n"
|
||||
" if (result.$name$_.isEmpty()) {\n"
|
||||
" result.$name$_ = new java.util.ArrayList<$type$>();\n"
|
||||
" }\n"
|
||||
" result.$name$_.add(value);\n"
|
||||
" ensure$capitalized_name$IsMutable();\n"
|
||||
" $name$_.add(value);\n"
|
||||
" $on_changed$\n"
|
||||
" return this;\n"
|
||||
"}\n"
|
||||
"public Builder addAll$capitalized_name$(\n"
|
||||
"$deprecation$public Builder addAll$capitalized_name$(\n"
|
||||
" java.lang.Iterable<? extends $type$> values) {\n"
|
||||
" if (result.$name$_.isEmpty()) {\n"
|
||||
" result.$name$_ = new java.util.ArrayList<$type$>();\n"
|
||||
" }\n"
|
||||
" super.addAll(values, result.$name$_);\n"
|
||||
" ensure$capitalized_name$IsMutable();\n"
|
||||
" super.addAll(values, $name$_);\n"
|
||||
" $on_changed$\n"
|
||||
" return this;\n"
|
||||
"}\n"
|
||||
"public Builder clear$capitalized_name$() {\n"
|
||||
" result.$name$_ = java.util.Collections.emptyList();\n"
|
||||
"$deprecation$public Builder clear$capitalized_name$() {\n"
|
||||
" $name$_ = java.util.Collections.emptyList();\n"
|
||||
" $clear_mutable_bit_builder$;\n"
|
||||
" $on_changed$\n"
|
||||
" return this;\n"
|
||||
"}\n");
|
||||
}
|
||||
|
||||
void RepeatedEnumFieldGenerator::
|
||||
GenerateFieldBuilderInitializationCode(io::Printer* printer) const {
|
||||
// noop for enums
|
||||
}
|
||||
|
||||
void RepeatedEnumFieldGenerator::
|
||||
GenerateInitializationCode(io::Printer* printer) const {
|
||||
// Initialized inline.
|
||||
printer->Print(variables_, "$name$_ = java.util.Collections.emptyList();\n");
|
||||
}
|
||||
|
||||
void RepeatedEnumFieldGenerator::
|
||||
GenerateBuilderClearCode(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
"$name$_ = java.util.Collections.emptyList();\n"
|
||||
"$clear_mutable_bit_builder$;\n");
|
||||
}
|
||||
|
||||
void RepeatedEnumFieldGenerator::
|
||||
GenerateMergingCode(io::Printer* printer) const {
|
||||
// The code below does two optimizations:
|
||||
// 1. If the other list is empty, there's nothing to do. This ensures we
|
||||
// don't allocate a new array if we already have an immutable one.
|
||||
// 2. If the other list is non-empty and our current list is empty, we can
|
||||
// reuse the other list which is guaranteed to be immutable.
|
||||
printer->Print(variables_,
|
||||
"if (!other.$name$_.isEmpty()) {\n"
|
||||
" if (result.$name$_.isEmpty()) {\n"
|
||||
" result.$name$_ = new java.util.ArrayList<$type$>();\n"
|
||||
" if ($name$_.isEmpty()) {\n"
|
||||
" $name$_ = other.$name$_;\n"
|
||||
" $clear_mutable_bit_builder$;\n"
|
||||
" } else {\n"
|
||||
" ensure$capitalized_name$IsMutable();\n"
|
||||
" $name$_.addAll(other.$name$_);\n"
|
||||
" }\n"
|
||||
" result.$name$_.addAll(other.$name$_);\n"
|
||||
" $on_changed$\n"
|
||||
"}\n");
|
||||
}
|
||||
|
||||
void RepeatedEnumFieldGenerator::
|
||||
GenerateBuildingCode(io::Printer* printer) const {
|
||||
// The code below ensures that the result has an immutable list. If our
|
||||
// list is immutable, we can just reuse it. If not, we make it immutable.
|
||||
printer->Print(variables_,
|
||||
"if (result.$name$_ != java.util.Collections.EMPTY_LIST) {\n"
|
||||
" result.$name$_ =\n"
|
||||
" java.util.Collections.unmodifiableList(result.$name$_);\n"
|
||||
"}\n");
|
||||
"if ($get_mutable_bit_builder$) {\n"
|
||||
" $name$_ = java.util.Collections.unmodifiableList($name$_);\n"
|
||||
" $clear_mutable_bit_builder$;\n"
|
||||
"}\n"
|
||||
"result.$name$_ = $name$_;\n");
|
||||
}
|
||||
|
||||
void RepeatedEnumFieldGenerator::
|
||||
@ -316,13 +464,13 @@ GenerateSerializationCode(io::Printer* printer) const {
|
||||
" output.writeRawVarint32($tag$);\n"
|
||||
" output.writeRawVarint32($name$MemoizedSerializedSize);\n"
|
||||
"}\n"
|
||||
"for ($type$ element : get$capitalized_name$List()) {\n"
|
||||
" output.writeEnumNoTag(element.getNumber());\n"
|
||||
"for (int i = 0; i < $name$_.size(); i++) {\n"
|
||||
" output.writeEnumNoTag($name$_.get(i).getNumber());\n"
|
||||
"}\n");
|
||||
} else {
|
||||
printer->Print(variables_,
|
||||
"for ($type$ element : get$capitalized_name$List()) {\n"
|
||||
" output.writeEnum($number$, element.getNumber());\n"
|
||||
"for (int i = 0; i < $name$_.size(); i++) {\n"
|
||||
" output.writeEnum($number$, $name$_.get(i).getNumber());\n"
|
||||
"}\n");
|
||||
}
|
||||
}
|
||||
@ -335,9 +483,9 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
|
||||
printer->Indent();
|
||||
|
||||
printer->Print(variables_,
|
||||
"for ($type$ element : get$capitalized_name$List()) {\n"
|
||||
"for (int i = 0; i < $name$_.size(); i++) {\n"
|
||||
" dataSize += com.google.protobuf.CodedOutputStream\n"
|
||||
" .computeEnumSizeNoTag(element.getNumber());\n"
|
||||
" .computeEnumSizeNoTag($name$_.get(i).getNumber());\n"
|
||||
"}\n");
|
||||
printer->Print(
|
||||
"size += dataSize;\n");
|
||||
@ -350,7 +498,7 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
|
||||
"}");
|
||||
} else {
|
||||
printer->Print(variables_,
|
||||
"size += $tag_size$ * get$capitalized_name$List().size();\n");
|
||||
"size += $tag_size$ * $name$_.size();\n");
|
||||
}
|
||||
|
||||
// cache the data size for packed fields.
|
||||
@ -363,6 +511,22 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
|
||||
printer->Print("}\n");
|
||||
}
|
||||
|
||||
void RepeatedEnumFieldGenerator::
|
||||
GenerateEqualsCode(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
"result = result && get$capitalized_name$List()\n"
|
||||
" .equals(other.get$capitalized_name$List());\n");
|
||||
}
|
||||
|
||||
void RepeatedEnumFieldGenerator::
|
||||
GenerateHashCode(io::Printer* printer) const {
|
||||
printer->Print(variables_,
|
||||
"if (get$capitalized_name$Count() > 0) {\n"
|
||||
" hash = (37 * hash) + $constant_name$;\n"
|
||||
" hash = (53 * hash) + hashEnumList(get$capitalized_name$List());\n"
|
||||
"}\n");
|
||||
}
|
||||
|
||||
string RepeatedEnumFieldGenerator::GetBoxedType() const {
|
||||
return ClassName(descriptor_->enum_type());
|
||||
}
|
||||
|
@ -46,49 +46,69 @@ namespace java {
|
||||
|
||||
class EnumFieldGenerator : public FieldGenerator {
|
||||
public:
|
||||
explicit EnumFieldGenerator(const FieldDescriptor* descriptor);
|
||||
explicit EnumFieldGenerator(const FieldDescriptor* descriptor,
|
||||
int messageBitIndex, int builderBitIndex);
|
||||
~EnumFieldGenerator();
|
||||
|
||||
// implements FieldGenerator ---------------------------------------
|
||||
int GetNumBitsForMessage() const;
|
||||
int GetNumBitsForBuilder() const;
|
||||
void GenerateInterfaceMembers(io::Printer* printer) const;
|
||||
void GenerateMembers(io::Printer* printer) const;
|
||||
void GenerateBuilderMembers(io::Printer* printer) const;
|
||||
void GenerateInitializationCode(io::Printer* printer) const;
|
||||
void GenerateBuilderClearCode(io::Printer* printer) const;
|
||||
void GenerateMergingCode(io::Printer* printer) const;
|
||||
void GenerateBuildingCode(io::Printer* printer) const;
|
||||
void GenerateParsingCode(io::Printer* printer) const;
|
||||
void GenerateSerializationCode(io::Printer* printer) const;
|
||||
void GenerateSerializedSizeCode(io::Printer* printer) const;
|
||||
void GenerateFieldBuilderInitializationCode(io::Printer* printer) const;
|
||||
void GenerateEqualsCode(io::Printer* printer) const;
|
||||
void GenerateHashCode(io::Printer* printer) const;
|
||||
|
||||
string GetBoxedType() const;
|
||||
|
||||
private:
|
||||
const FieldDescriptor* descriptor_;
|
||||
map<string, string> variables_;
|
||||
const int messageBitIndex_;
|
||||
const int builderBitIndex_;
|
||||
|
||||
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumFieldGenerator);
|
||||
};
|
||||
|
||||
class RepeatedEnumFieldGenerator : public FieldGenerator {
|
||||
public:
|
||||
explicit RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor);
|
||||
explicit RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor,
|
||||
int messageBitIndex, int builderBitIndex);
|
||||
~RepeatedEnumFieldGenerator();
|
||||
|
||||
// implements FieldGenerator ---------------------------------------
|
||||
int GetNumBitsForMessage() const;
|
||||
int GetNumBitsForBuilder() const;
|
||||
void GenerateInterfaceMembers(io::Printer* printer) const;
|
||||
void GenerateMembers(io::Printer* printer) const;
|
||||
void GenerateBuilderMembers(io::Printer* printer) const;
|
||||
void GenerateInitializationCode(io::Printer* printer) const;
|
||||
void GenerateBuilderClearCode(io::Printer* printer) const;
|
||||
void GenerateMergingCode(io::Printer* printer) const;
|
||||
void GenerateBuildingCode(io::Printer* printer) const;
|
||||
void GenerateParsingCode(io::Printer* printer) const;
|
||||
void GenerateParsingCodeFromPacked(io::Printer* printer) const;
|
||||
void GenerateSerializationCode(io::Printer* printer) const;
|
||||
void GenerateSerializedSizeCode(io::Printer* printer) const;
|
||||
void GenerateFieldBuilderInitializationCode(io::Printer* printer) const;
|
||||
void GenerateEqualsCode(io::Printer* printer) const;
|
||||
void GenerateHashCode(io::Printer* printer) const;
|
||||
|
||||
string GetBoxedType() const;
|
||||
|
||||
private:
|
||||
const FieldDescriptor* descriptor_;
|
||||
map<string, string> variables_;
|
||||
const int messageBitIndex_;
|
||||
const int builderBitIndex_;
|
||||
|
||||
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedEnumFieldGenerator);
|
||||
};
|
||||
|
@ -86,105 +86,120 @@ ExtensionGenerator::ExtensionGenerator(const FieldDescriptor* descriptor)
|
||||
|
||||
ExtensionGenerator::~ExtensionGenerator() {}
|
||||
|
||||
void ExtensionGenerator::Generate(io::Printer* printer) {
|
||||
map<string, string> vars;
|
||||
vars["name"] = UnderscoresToCamelCase(descriptor_);
|
||||
vars["containing_type"] = ClassName(descriptor_->containing_type());
|
||||
vars["number"] = SimpleItoa(descriptor_->number());
|
||||
vars["constant_name"] = FieldConstantName(descriptor_);
|
||||
vars["lite"] = HasDescriptorMethods(descriptor_->file()) ? "" : "Lite";
|
||||
|
||||
JavaType java_type = GetJavaType(descriptor_);
|
||||
string singular_type;
|
||||
switch (java_type) {
|
||||
case JAVATYPE_MESSAGE:
|
||||
vars["type"] = ClassName(descriptor_->message_type());
|
||||
break;
|
||||
case JAVATYPE_ENUM:
|
||||
vars["type"] = ClassName(descriptor_->enum_type());
|
||||
break;
|
||||
default:
|
||||
vars["type"] = BoxedPrimitiveTypeName(java_type);
|
||||
break;
|
||||
}
|
||||
|
||||
printer->Print(vars,
|
||||
"public static final int $constant_name$ = $number$;\n");
|
||||
if (descriptor_->is_repeated()) {
|
||||
printer->Print(vars,
|
||||
"public static final\n"
|
||||
" com.google.protobuf.GeneratedMessage$lite$.GeneratedExtension<\n"
|
||||
" $containing_type$,\n"
|
||||
" java.util.List<$type$>> $name$ =\n"
|
||||
" com.google.protobuf.GeneratedMessage$lite$\n"
|
||||
" .newGeneratedExtension();\n");
|
||||
} else {
|
||||
printer->Print(vars,
|
||||
"public static final\n"
|
||||
" com.google.protobuf.GeneratedMessage$lite$.GeneratedExtension<\n"
|
||||
" $containing_type$,\n"
|
||||
" $type$> $name$ =\n"
|
||||
" com.google.protobuf.GeneratedMessage$lite$\n"
|
||||
" .newGeneratedExtension();\n");
|
||||
}
|
||||
}
|
||||
|
||||
void ExtensionGenerator::GenerateInitializationCode(io::Printer* printer) {
|
||||
map<string, string> vars;
|
||||
vars["name"] = UnderscoresToCamelCase(descriptor_);
|
||||
vars["scope"] = scope_;
|
||||
vars["index"] = SimpleItoa(descriptor_->index());
|
||||
vars["extendee"] = ClassName(descriptor_->containing_type());
|
||||
vars["default"] = descriptor_->is_repeated() ? "" : DefaultValue(descriptor_);
|
||||
vars["number"] = SimpleItoa(descriptor_->number());
|
||||
vars["type_constant"] = TypeName(GetType(descriptor_));
|
||||
vars["packed"] = descriptor_->options().packed() ? "true" : "false";
|
||||
// Initializes the vars referenced in the generated code templates.
|
||||
void InitTemplateVars(const FieldDescriptor* descriptor,
|
||||
const string& scope,
|
||||
map<string, string>* vars_pointer) {
|
||||
map<string, string> &vars = *vars_pointer;
|
||||
vars["scope"] = scope;
|
||||
vars["name"] = UnderscoresToCamelCase(descriptor);
|
||||
vars["containing_type"] = ClassName(descriptor->containing_type());
|
||||
vars["number"] = SimpleItoa(descriptor->number());
|
||||
vars["constant_name"] = FieldConstantName(descriptor);
|
||||
vars["index"] = SimpleItoa(descriptor->index());
|
||||
vars["default"] =
|
||||
descriptor->is_repeated() ? "" : DefaultValue(descriptor);
|
||||
vars["type_constant"] = TypeName(GetType(descriptor));
|
||||
vars["packed"] = descriptor->options().packed() ? "true" : "false";
|
||||
vars["enum_map"] = "null";
|
||||
vars["prototype"] = "null";
|
||||
|
||||
JavaType java_type = GetJavaType(descriptor_);
|
||||
JavaType java_type = GetJavaType(descriptor);
|
||||
string singular_type;
|
||||
switch (java_type) {
|
||||
case JAVATYPE_MESSAGE:
|
||||
vars["type"] = ClassName(descriptor_->message_type());
|
||||
vars["prototype"] = ClassName(descriptor_->message_type()) +
|
||||
".getDefaultInstance()";
|
||||
singular_type = ClassName(descriptor->message_type());
|
||||
vars["prototype"] = singular_type + ".getDefaultInstance()";
|
||||
break;
|
||||
case JAVATYPE_ENUM:
|
||||
vars["type"] = ClassName(descriptor_->enum_type());
|
||||
vars["enum_map"] = ClassName(descriptor_->enum_type()) +
|
||||
".internalGetValueMap()";
|
||||
singular_type = ClassName(descriptor->enum_type());
|
||||
vars["enum_map"] = singular_type + ".internalGetValueMap()";
|
||||
break;
|
||||
default:
|
||||
vars["type"] = BoxedPrimitiveTypeName(java_type);
|
||||
singular_type = BoxedPrimitiveTypeName(java_type);
|
||||
break;
|
||||
}
|
||||
vars["type"] = descriptor->is_repeated() ?
|
||||
"java.util.List<" + singular_type + ">" : singular_type;
|
||||
vars["singular_type"] = singular_type;
|
||||
}
|
||||
|
||||
void ExtensionGenerator::Generate(io::Printer* printer) {
|
||||
map<string, string> vars;
|
||||
InitTemplateVars(descriptor_, scope_, &vars);
|
||||
printer->Print(vars,
|
||||
"public static final int $constant_name$ = $number$;\n");
|
||||
|
||||
if (HasDescriptorMethods(descriptor_->file())) {
|
||||
printer->Print(vars,
|
||||
"$scope$.$name$.internalInit(\n"
|
||||
" $scope$.getDescriptor().getExtensions().get($index$),\n"
|
||||
" $type$.class);\n");
|
||||
} else {
|
||||
if (descriptor_->is_repeated()) {
|
||||
printer->Print(vars,
|
||||
"$scope$.$name$.internalInitRepeated(\n"
|
||||
" $extendee$.getDefaultInstance(),\n"
|
||||
" $prototype$,\n"
|
||||
" $enum_map$,\n"
|
||||
" $number$,\n"
|
||||
" com.google.protobuf.WireFormat.FieldType.$type_constant$,\n"
|
||||
" $packed$);\n");
|
||||
// Non-lite extensions
|
||||
if (descriptor_->extension_scope() == NULL) {
|
||||
// Non-nested
|
||||
printer->Print(
|
||||
vars,
|
||||
"public static final\n"
|
||||
" com.google.protobuf.GeneratedMessage.GeneratedExtension<\n"
|
||||
" $containing_type$,\n"
|
||||
" $type$> $name$ = com.google.protobuf.GeneratedMessage\n"
|
||||
" .newFileScopedGeneratedExtension(\n"
|
||||
" $singular_type$.class,\n"
|
||||
" $prototype$);\n");
|
||||
} else {
|
||||
printer->Print(vars,
|
||||
"$scope$.$name$.internalInitSingular(\n"
|
||||
" $extendee$.getDefaultInstance(),\n"
|
||||
" $default$,\n"
|
||||
" $prototype$,\n"
|
||||
" $enum_map$,\n"
|
||||
" $number$,\n"
|
||||
" com.google.protobuf.WireFormat.FieldType.$type_constant$);\n");
|
||||
// Nested
|
||||
printer->Print(
|
||||
vars,
|
||||
"public static final\n"
|
||||
" com.google.protobuf.GeneratedMessage.GeneratedExtension<\n"
|
||||
" $containing_type$,\n"
|
||||
" $type$> $name$ = com.google.protobuf.GeneratedMessage\n"
|
||||
" .newMessageScopedGeneratedExtension(\n"
|
||||
" $scope$.getDefaultInstance(),\n"
|
||||
" $index$,\n"
|
||||
" $singular_type$.class,\n"
|
||||
" $prototype$);\n");
|
||||
}
|
||||
} else {
|
||||
// Lite extensions
|
||||
if (descriptor_->is_repeated()) {
|
||||
printer->Print(
|
||||
vars,
|
||||
"public static final\n"
|
||||
" com.google.protobuf.GeneratedMessageLite.GeneratedExtension<\n"
|
||||
" $containing_type$,\n"
|
||||
" $type$> $name$ = com.google.protobuf.GeneratedMessageLite\n"
|
||||
" .newRepeatedGeneratedExtension(\n"
|
||||
" $containing_type$.getDefaultInstance(),\n"
|
||||
" $prototype$,\n"
|
||||
" $enum_map$,\n"
|
||||
" $number$,\n"
|
||||
" com.google.protobuf.WireFormat.FieldType.$type_constant$,\n"
|
||||
" $packed$);\n");
|
||||
} else {
|
||||
printer->Print(
|
||||
vars,
|
||||
"public static final\n"
|
||||
" com.google.protobuf.GeneratedMessageLite.GeneratedExtension<\n"
|
||||
" $containing_type$,\n"
|
||||
" $type$> $name$ = com.google.protobuf.GeneratedMessageLite\n"
|
||||
" .newSingularGeneratedExtension(\n"
|
||||
" $containing_type$.getDefaultInstance(),\n"
|
||||
" $default$,\n"
|
||||
" $prototype$,\n"
|
||||
" $enum_map$,\n"
|
||||
" $number$,\n"
|
||||
" com.google.protobuf.WireFormat.FieldType.$type_constant$);\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExtensionGenerator::GenerateNonNestedInitializationCode(
|
||||
io::Printer* printer) {
|
||||
if (descriptor_->extension_scope() == NULL &&
|
||||
HasDescriptorMethods(descriptor_->file())) {
|
||||
// Only applies to non-nested, non-lite extensions.
|
||||
printer->Print(
|
||||
"$name$.internalInit(descriptor.getExtensions().get($index$));\n",
|
||||
"name", UnderscoresToCamelCase(descriptor_),
|
||||
"index", SimpleItoa(descriptor_->index()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ class ExtensionGenerator {
|
||||
~ExtensionGenerator();
|
||||
|
||||
void Generate(io::Printer* printer);
|
||||
void GenerateInitializationCode(io::Printer* printer);
|
||||
void GenerateNonNestedInitializationCode(io::Printer* printer);
|
||||
void GenerateRegistrationCode(io::Printer* printer);
|
||||
|
||||
private:
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include <google/protobuf/compiler/java/java_primitive_field.h>
|
||||
#include <google/protobuf/compiler/java/java_enum_field.h>
|
||||
#include <google/protobuf/compiler/java/java_message_field.h>
|
||||
#include <google/protobuf/compiler/java/java_string_field.h>
|
||||
#include <google/protobuf/stubs/common.h>
|
||||
|
||||
namespace google {
|
||||
@ -63,33 +64,57 @@ FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor)
|
||||
extension_generators_(
|
||||
new scoped_ptr<FieldGenerator>[descriptor->extension_count()]) {
|
||||
|
||||
// Construct all the FieldGenerators.
|
||||
// Construct all the FieldGenerators and assign them bit indices for their
|
||||
// bit fields.
|
||||
int messageBitIndex = 0;
|
||||
int builderBitIndex = 0;
|
||||
for (int i = 0; i < descriptor->field_count(); i++) {
|
||||
field_generators_[i].reset(MakeGenerator(descriptor->field(i)));
|
||||
FieldGenerator* generator = MakeGenerator(descriptor->field(i),
|
||||
messageBitIndex, builderBitIndex);
|
||||
field_generators_[i].reset(generator);
|
||||
messageBitIndex += generator->GetNumBitsForMessage();
|
||||
builderBitIndex += generator->GetNumBitsForBuilder();
|
||||
}
|
||||
for (int i = 0; i < descriptor->extension_count(); i++) {
|
||||
extension_generators_[i].reset(MakeGenerator(descriptor->extension(i)));
|
||||
FieldGenerator* generator = MakeGenerator(descriptor->extension(i),
|
||||
messageBitIndex, builderBitIndex);
|
||||
extension_generators_[i].reset(generator);
|
||||
messageBitIndex += generator->GetNumBitsForMessage();
|
||||
builderBitIndex += generator->GetNumBitsForBuilder();
|
||||
}
|
||||
}
|
||||
|
||||
FieldGenerator* FieldGeneratorMap::MakeGenerator(const FieldDescriptor* field) {
|
||||
FieldGenerator* FieldGeneratorMap::MakeGenerator(
|
||||
const FieldDescriptor* field, int messageBitIndex, int builderBitIndex) {
|
||||
if (field->is_repeated()) {
|
||||
switch (GetJavaType(field)) {
|
||||
case JAVATYPE_MESSAGE:
|
||||
return new RepeatedMessageFieldGenerator(field);
|
||||
return new RepeatedMessageFieldGenerator(
|
||||
field, messageBitIndex, builderBitIndex);
|
||||
case JAVATYPE_ENUM:
|
||||
return new RepeatedEnumFieldGenerator(field);
|
||||
return new RepeatedEnumFieldGenerator(
|
||||
field, messageBitIndex, builderBitIndex);
|
||||
case JAVATYPE_STRING:
|
||||
return new RepeatedStringFieldGenerator(
|
||||
field, messageBitIndex, builderBitIndex);
|
||||
default:
|
||||
return new RepeatedPrimitiveFieldGenerator(field);
|
||||
return new RepeatedPrimitiveFieldGenerator(
|
||||
field, messageBitIndex, builderBitIndex);
|
||||
}
|
||||
} else {
|
||||
switch (GetJavaType(field)) {
|
||||
case JAVATYPE_MESSAGE:
|
||||
return new MessageFieldGenerator(field);
|
||||
return new MessageFieldGenerator(
|
||||
field, messageBitIndex, builderBitIndex);
|
||||
case JAVATYPE_ENUM:
|
||||
return new EnumFieldGenerator(field);
|
||||
return new EnumFieldGenerator(
|
||||
field, messageBitIndex, builderBitIndex);
|
||||
case JAVATYPE_STRING:
|
||||
return new StringFieldGenerator(
|
||||
field, messageBitIndex, builderBitIndex);
|
||||
default:
|
||||
return new PrimitiveFieldGenerator(field);
|
||||
return new PrimitiveFieldGenerator(
|
||||
field, messageBitIndex, builderBitIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,15 +55,24 @@ class FieldGenerator {
|
||||
FieldGenerator() {}
|
||||
virtual ~FieldGenerator();
|
||||
|
||||
virtual int GetNumBitsForMessage() const = 0;
|
||||
virtual int GetNumBitsForBuilder() const = 0;
|
||||
virtual void GenerateInterfaceMembers(io::Printer* printer) const = 0;
|
||||
virtual void GenerateMembers(io::Printer* printer) const = 0;
|
||||
virtual void GenerateBuilderMembers(io::Printer* printer) const = 0;
|
||||
virtual void GenerateInitializationCode(io::Printer* printer) const = 0;
|
||||
virtual void GenerateBuilderClearCode(io::Printer* printer) const = 0;
|
||||
virtual void GenerateMergingCode(io::Printer* printer) const = 0;
|
||||
virtual void GenerateBuildingCode(io::Printer* printer) const = 0;
|
||||
virtual void GenerateParsingCode(io::Printer* printer) const = 0;
|
||||
virtual void GenerateParsingCodeFromPacked(io::Printer* printer) const;
|
||||
virtual void GenerateSerializationCode(io::Printer* printer) const = 0;
|
||||
virtual void GenerateSerializedSizeCode(io::Printer* printer) const = 0;
|
||||
virtual void GenerateFieldBuilderInitializationCode(io::Printer* printer)
|
||||
const = 0;
|
||||
|
||||
virtual void GenerateEqualsCode(io::Printer* printer) const = 0;
|
||||
virtual void GenerateHashCode(io::Printer* printer) const = 0;
|
||||
|
||||
virtual string GetBoxedType() const = 0;
|
||||
|
||||
@ -85,7 +94,8 @@ class FieldGeneratorMap {
|
||||
scoped_array<scoped_ptr<FieldGenerator> > field_generators_;
|
||||
scoped_array<scoped_ptr<FieldGenerator> > extension_generators_;
|
||||
|
||||
static FieldGenerator* MakeGenerator(const FieldDescriptor* field);
|
||||
static FieldGenerator* MakeGenerator(const FieldDescriptor* field,
|
||||
int messageBitIndex, int builderBitIndex);
|
||||
|
||||
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGeneratorMap);
|
||||
};
|
||||
|
@ -88,7 +88,8 @@ bool UsesExtensions(const Message& message) {
|
||||
FileGenerator::FileGenerator(const FileDescriptor* file)
|
||||
: file_(file),
|
||||
java_package_(FileJavaPackage(file)),
|
||||
classname_(FileClassName(file)) {}
|
||||
classname_(FileClassName(file)) {
|
||||
}
|
||||
|
||||
FileGenerator::~FileGenerator() {}
|
||||
|
||||
@ -179,7 +180,9 @@ void FileGenerator::Generate(io::Printer* printer) {
|
||||
EnumGenerator(file_->enum_type(i)).Generate(printer);
|
||||
}
|
||||
for (int i = 0; i < file_->message_type_count(); i++) {
|
||||
MessageGenerator(file_->message_type(i)).Generate(printer);
|
||||
MessageGenerator messageGenerator(file_->message_type(i));
|
||||
messageGenerator.GenerateInterface(printer);
|
||||
messageGenerator.Generate(printer);
|
||||
}
|
||||
if (HasGenericServices(file_)) {
|
||||
for (int i = 0; i < file_->service_count(); i++) {
|
||||
@ -215,24 +218,11 @@ void FileGenerator::Generate(io::Printer* printer) {
|
||||
.GenerateStaticVariableInitializers(printer);
|
||||
}
|
||||
|
||||
for (int i = 0; i < file_->extension_count(); i++) {
|
||||
// TODO(kenton): Reuse ExtensionGenerator objects?
|
||||
ExtensionGenerator(file_->extension(i))
|
||||
.GenerateInitializationCode(printer);
|
||||
}
|
||||
|
||||
printer->Outdent();
|
||||
printer->Print(
|
||||
"}\n");
|
||||
}
|
||||
|
||||
// Dummy function we can use to force the static initialization block to
|
||||
// run. Needed by inner classes. Cannot be private due to
|
||||
// java_multiple_files option.
|
||||
printer->Print(
|
||||
"\n"
|
||||
"public static void internalForceInit() {}\n");
|
||||
|
||||
printer->Print(
|
||||
"\n"
|
||||
"// @@protoc_insertion_point(outer_class_scope)\n");
|
||||
@ -310,11 +300,10 @@ void FileGenerator::GenerateEmbeddedDescriptor(io::Printer* printer) {
|
||||
MessageGenerator(file_->message_type(i))
|
||||
.GenerateStaticVariableInitializers(printer);
|
||||
}
|
||||
|
||||
for (int i = 0; i < file_->extension_count(); i++) {
|
||||
// TODO(kenton): Reuse ExtensionGenerator objects?
|
||||
ExtensionGenerator(file_->extension(i))
|
||||
.GenerateInitializationCode(printer);
|
||||
.GenerateNonNestedInitializationCode(printer);
|
||||
}
|
||||
|
||||
if (UsesExtensions(file_proto)) {
|
||||
@ -325,9 +314,11 @@ void FileGenerator::GenerateEmbeddedDescriptor(io::Printer* printer) {
|
||||
" com.google.protobuf.ExtensionRegistry.newInstance();\n"
|
||||
"registerAllExtensions(registry);\n");
|
||||
for (int i = 0; i < file_->dependency_count(); i++) {
|
||||
printer->Print(
|
||||
"$dependency$.registerAllExtensions(registry);\n",
|
||||
"dependency", ClassName(file_->dependency(i)));
|
||||
if (ShouldIncludeDependency(file_->dependency(i))) {
|
||||
printer->Print(
|
||||
"$dependency$.registerAllExtensions(registry);\n",
|
||||
"dependency", ClassName(file_->dependency(i)));
|
||||
}
|
||||
}
|
||||
printer->Print(
|
||||
"return registry;\n");
|
||||
@ -372,13 +363,14 @@ template<typename GeneratorClass, typename DescriptorClass>
|
||||
static void GenerateSibling(const string& package_dir,
|
||||
const string& java_package,
|
||||
const DescriptorClass* descriptor,
|
||||
OutputDirectory* output_directory,
|
||||
vector<string>* file_list) {
|
||||
string filename = package_dir + descriptor->name() + ".java";
|
||||
GeneratorContext* context,
|
||||
vector<string>* file_list,
|
||||
const string& name_suffix,
|
||||
void (GeneratorClass::*pfn)(io::Printer* printer)) {
|
||||
string filename = package_dir + descriptor->name() + name_suffix + ".java";
|
||||
file_list->push_back(filename);
|
||||
|
||||
scoped_ptr<io::ZeroCopyOutputStream> output(
|
||||
output_directory->Open(filename));
|
||||
scoped_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
|
||||
io::Printer printer(output.get(), '$');
|
||||
|
||||
printer.Print(
|
||||
@ -391,28 +383,36 @@ static void GenerateSibling(const string& package_dir,
|
||||
"package", java_package);
|
||||
}
|
||||
|
||||
GeneratorClass(descriptor).Generate(&printer);
|
||||
GeneratorClass generator(descriptor);
|
||||
(generator.*pfn)(&printer);
|
||||
}
|
||||
|
||||
void FileGenerator::GenerateSiblings(const string& package_dir,
|
||||
OutputDirectory* output_directory,
|
||||
GeneratorContext* context,
|
||||
vector<string>* file_list) {
|
||||
if (file_->options().java_multiple_files()) {
|
||||
for (int i = 0; i < file_->enum_type_count(); i++) {
|
||||
GenerateSibling<EnumGenerator>(package_dir, java_package_,
|
||||
file_->enum_type(i),
|
||||
output_directory, file_list);
|
||||
context, file_list, "",
|
||||
&EnumGenerator::Generate);
|
||||
}
|
||||
for (int i = 0; i < file_->message_type_count(); i++) {
|
||||
GenerateSibling<MessageGenerator>(package_dir, java_package_,
|
||||
file_->message_type(i),
|
||||
output_directory, file_list);
|
||||
context, file_list, "OrBuilder",
|
||||
&MessageGenerator::GenerateInterface);
|
||||
GenerateSibling<MessageGenerator>(package_dir, java_package_,
|
||||
file_->message_type(i),
|
||||
context, file_list, "",
|
||||
&MessageGenerator::Generate);
|
||||
}
|
||||
if (HasGenericServices(file_)) {
|
||||
for (int i = 0; i < file_->service_count(); i++) {
|
||||
GenerateSibling<ServiceGenerator>(package_dir, java_package_,
|
||||
file_->service(i),
|
||||
output_directory, file_list);
|
||||
context, file_list, "",
|
||||
&ServiceGenerator::Generate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ namespace protobuf {
|
||||
class Printer; // printer.h
|
||||
}
|
||||
namespace compiler {
|
||||
class OutputDirectory; // code_generator.h
|
||||
class GeneratorContext; // code_generator.h
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,12 +70,13 @@ class FileGenerator {
|
||||
// files other than the outer file (i.e. one for each message, enum, and
|
||||
// service type).
|
||||
void GenerateSiblings(const string& package_dir,
|
||||
OutputDirectory* output_directory,
|
||||
GeneratorContext* generator_context,
|
||||
vector<string>* file_list);
|
||||
|
||||
const string& java_package() { return java_package_; }
|
||||
const string& classname() { return classname_; }
|
||||
|
||||
|
||||
private:
|
||||
// Returns whether the dependency should be included in the output file.
|
||||
// Always returns true for opensource, but used internally at Google to help
|
||||
@ -86,6 +87,7 @@ class FileGenerator {
|
||||
string java_package_;
|
||||
string classname_;
|
||||
|
||||
|
||||
void GenerateEmbeddedDescriptor(io::Printer* printer);
|
||||
|
||||
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileGenerator);
|
||||
|
@ -51,11 +51,8 @@ JavaGenerator::~JavaGenerator() {}
|
||||
|
||||
bool JavaGenerator::Generate(const FileDescriptor* file,
|
||||
const string& parameter,
|
||||
OutputDirectory* output_directory,
|
||||
GeneratorContext* context,
|
||||
string* error) const {
|
||||
vector<pair<string, string> > options;
|
||||
ParseGeneratorParameter(parameter, &options);
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// parse generator options
|
||||
|
||||
@ -63,6 +60,10 @@ bool JavaGenerator::Generate(const FileDescriptor* file,
|
||||
// per line.
|
||||
string output_list_file;
|
||||
|
||||
|
||||
vector<pair<string, string> > options;
|
||||
ParseGeneratorParameter(parameter, &options);
|
||||
|
||||
for (int i = 0; i < options.size(); i++) {
|
||||
if (options[i].first == "output_list_file") {
|
||||
output_list_file = options[i].second;
|
||||
@ -72,18 +73,23 @@ bool JavaGenerator::Generate(const FileDescriptor* file,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
|
||||
if (file->options().optimize_for() == FileOptions::LITE_RUNTIME &&
|
||||
file->options().java_generate_equals_and_hash()) {
|
||||
*error = "The \"java_generate_equals_and_hash\" option is incompatible "
|
||||
"with \"optimize_for = LITE_RUNTIME\". You must optimize for "
|
||||
"SPEED or CODE_SIZE if you want to use this option.";
|
||||
return false;
|
||||
}
|
||||
|
||||
FileGenerator file_generator(file);
|
||||
if (!file_generator.Validate(error)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string package_dir =
|
||||
StringReplace(file_generator.java_package(), ".", "/", true);
|
||||
if (!package_dir.empty()) package_dir += "/";
|
||||
string package_dir = JavaPackageToDir(file_generator.java_package());
|
||||
|
||||
vector<string> all_files;
|
||||
|
||||
@ -94,19 +100,19 @@ bool JavaGenerator::Generate(const FileDescriptor* file,
|
||||
|
||||
// Generate main java file.
|
||||
scoped_ptr<io::ZeroCopyOutputStream> output(
|
||||
output_directory->Open(java_filename));
|
||||
context->Open(java_filename));
|
||||
io::Printer printer(output.get(), '$');
|
||||
file_generator.Generate(&printer);
|
||||
|
||||
// Generate sibling files.
|
||||
file_generator.GenerateSiblings(package_dir, output_directory, &all_files);
|
||||
file_generator.GenerateSiblings(package_dir, context, &all_files);
|
||||
|
||||
// Generate output list if requested.
|
||||
if (!output_list_file.empty()) {
|
||||
// Generate output list. This is just a simple text file placed in a
|
||||
// deterministic location which lists the .java files being generated.
|
||||
scoped_ptr<io::ZeroCopyOutputStream> srclist_raw_output(
|
||||
output_directory->Open(output_list_file));
|
||||
context->Open(output_list_file));
|
||||
io::Printer srclist_printer(srclist_raw_output.get(), '$');
|
||||
for (int i = 0; i < all_files.size(); i++) {
|
||||
srclist_printer.Print("$filename$\n", "filename", all_files[i]);
|
||||
|
@ -57,7 +57,7 @@ class LIBPROTOC_EXPORT JavaGenerator : public CodeGenerator {
|
||||
// implements CodeGenerator ----------------------------------------
|
||||
bool Generate(const FileDescriptor* file,
|
||||
const string& parameter,
|
||||
OutputDirectory* output_directory,
|
||||
GeneratorContext* context,
|
||||
string* error) const;
|
||||
|
||||
private:
|
||||
|
@ -134,16 +134,27 @@ string FileClassName(const FileDescriptor* file) {
|
||||
}
|
||||
|
||||
string FileJavaPackage(const FileDescriptor* file) {
|
||||
string result;
|
||||
|
||||
if (file->options().has_java_package()) {
|
||||
return file->options().java_package();
|
||||
result = file->options().java_package();
|
||||
} else {
|
||||
string result = kDefaultPackage;
|
||||
result = kDefaultPackage;
|
||||
if (!file->package().empty()) {
|
||||
if (!result.empty()) result += '.';
|
||||
result += file->package();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
string JavaPackageToDir(string package_name) {
|
||||
string package_dir =
|
||||
StringReplace(package_name, ".", "/", true);
|
||||
if (!package_dir.empty()) package_dir += "/";
|
||||
return package_dir;
|
||||
}
|
||||
|
||||
string ToJavaName(const string& full_name, const FileDescriptor* file) {
|
||||
@ -335,6 +346,132 @@ string DefaultValue(const FieldDescriptor* field) {
|
||||
return "";
|
||||
}
|
||||
|
||||
bool IsDefaultValueJavaDefault(const FieldDescriptor* field) {
|
||||
// Switch on CppType since we need to know which default_value_* method
|
||||
// of FieldDescriptor to call.
|
||||
switch (field->cpp_type()) {
|
||||
case FieldDescriptor::CPPTYPE_INT32:
|
||||
return field->default_value_int32() == 0;
|
||||
case FieldDescriptor::CPPTYPE_UINT32:
|
||||
return field->default_value_uint32() == 0;
|
||||
case FieldDescriptor::CPPTYPE_INT64:
|
||||
return field->default_value_int64() == 0L;
|
||||
case FieldDescriptor::CPPTYPE_UINT64:
|
||||
return field->default_value_uint64() == 0L;
|
||||
case FieldDescriptor::CPPTYPE_DOUBLE:
|
||||
return field->default_value_double() == 0.0;
|
||||
case FieldDescriptor::CPPTYPE_FLOAT:
|
||||
return field->default_value_float() == 0.0;
|
||||
case FieldDescriptor::CPPTYPE_BOOL:
|
||||
return field->default_value_bool() == false;
|
||||
|
||||
case FieldDescriptor::CPPTYPE_STRING:
|
||||
case FieldDescriptor::CPPTYPE_ENUM:
|
||||
case FieldDescriptor::CPPTYPE_MESSAGE:
|
||||
return false;
|
||||
|
||||
// No default because we want the compiler to complain if any new
|
||||
// types are added.
|
||||
}
|
||||
|
||||
GOOGLE_LOG(FATAL) << "Can't get here.";
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* bit_masks[] = {
|
||||
"0x00000001",
|
||||
"0x00000002",
|
||||
"0x00000004",
|
||||
"0x00000008",
|
||||
"0x00000010",
|
||||
"0x00000020",
|
||||
"0x00000040",
|
||||
"0x00000080",
|
||||
|
||||
"0x00000100",
|
||||
"0x00000200",
|
||||
"0x00000400",
|
||||
"0x00000800",
|
||||
"0x00001000",
|
||||
"0x00002000",
|
||||
"0x00004000",
|
||||
"0x00008000",
|
||||
|
||||
"0x00010000",
|
||||
"0x00020000",
|
||||
"0x00040000",
|
||||
"0x00080000",
|
||||
"0x00100000",
|
||||
"0x00200000",
|
||||
"0x00400000",
|
||||
"0x00800000",
|
||||
|
||||
"0x01000000",
|
||||
"0x02000000",
|
||||
"0x04000000",
|
||||
"0x08000000",
|
||||
"0x10000000",
|
||||
"0x20000000",
|
||||
"0x40000000",
|
||||
"0x80000000",
|
||||
};
|
||||
|
||||
string GetBitFieldName(int index) {
|
||||
string varName = "bitField";
|
||||
varName += SimpleItoa(index);
|
||||
varName += "_";
|
||||
return varName;
|
||||
}
|
||||
|
||||
string GetBitFieldNameForBit(int bitIndex) {
|
||||
return GetBitFieldName(bitIndex / 32);
|
||||
}
|
||||
|
||||
string GenerateGetBit(int bitIndex) {
|
||||
string varName = GetBitFieldNameForBit(bitIndex);
|
||||
int bitInVarIndex = bitIndex % 32;
|
||||
|
||||
string mask = bit_masks[bitInVarIndex];
|
||||
string result = "((" + varName + " & " + mask + ") == " + mask + ")";
|
||||
return result;
|
||||
}
|
||||
|
||||
string GenerateSetBit(int bitIndex) {
|
||||
string varName = GetBitFieldNameForBit(bitIndex);
|
||||
int bitInVarIndex = bitIndex % 32;
|
||||
|
||||
string mask = bit_masks[bitInVarIndex];
|
||||
string result = varName + " |= " + mask;
|
||||
return result;
|
||||
}
|
||||
|
||||
string GenerateClearBit(int bitIndex) {
|
||||
string varName = GetBitFieldNameForBit(bitIndex);
|
||||
int bitInVarIndex = bitIndex % 32;
|
||||
|
||||
string mask = bit_masks[bitInVarIndex];
|
||||
string result = varName + " = (" + varName + " & ~" + mask + ")";
|
||||
return result;
|
||||
}
|
||||
|
||||
string GenerateGetBitFromLocal(int bitIndex) {
|
||||
string varName = "from_" + GetBitFieldNameForBit(bitIndex);
|
||||
int bitInVarIndex = bitIndex % 32;
|
||||
|
||||
string mask = bit_masks[bitInVarIndex];
|
||||
string result = "((" + varName + " & " + mask + ") == " + mask + ")";
|
||||
return result;
|
||||
}
|
||||
|
||||
string GenerateSetBitToLocal(int bitIndex) {
|
||||
string varName = "to_" + GetBitFieldNameForBit(bitIndex);
|
||||
int bitInVarIndex = bitIndex % 32;
|
||||
|
||||
string mask = bit_masks[bitInVarIndex];
|
||||
string result = varName + " |= " + mask;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace java
|
||||
} // namespace compiler
|
||||
} // namespace protobuf
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user