mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
Remove old protobuf subtree in preparation for git subtree add
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
<PreprocessorDefinitions>_VARIADIC_MAX=10;_WIN32_WINNT=0x0600;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<AdditionalIncludeDirectories>$(RepoDir)\src;$(RepoDir)\src\cpp\protobuf\src;$(RepoDir)\src\cpp\protobuf\vsprojects;$(RepoDir)\src\leveldb;$(RepoDir)\src\leveldb\include;$(RepoDir)\build\proto;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(RepoDir)\src\protobuf\src;$(RepoDir)\src\protobuf\vsprojects;$(RepoDir)\src;$(RepoDir)\src\leveldb;$(RepoDir)\src\leveldb\include;$(RepoDir)\build\proto;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<ExceptionHandling>Async</ExceptionHandling>
|
||||
<DisableSpecificWarnings>4018;4244</DisableSpecificWarnings>
|
||||
|
||||
@@ -1021,7 +1021,6 @@
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\modules\ripple_websocket\ripple_websocket.cpp" />
|
||||
<ClCompile Include="..\..\src\cpp\protobuf_core.cpp" />
|
||||
<ClCompile Include="..\..\src\beast\modules\beast_asio\beast_asio.cpp" />
|
||||
<ClCompile Include="..\..\src\beast\modules\beast_core\beast_core.cpp" />
|
||||
<ClCompile Include="..\..\src\beast\modules\beast_crypto\beast_crypto.cpp" />
|
||||
@@ -1292,6 +1291,7 @@
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\protobuf_core.cpp" />
|
||||
<ClCompile Include="..\..\src\websocket\src\base64\base64.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
|
||||
@@ -159,9 +159,6 @@
|
||||
<ClCompile Include="..\..\src\beast\modules\beast_core\beast_core.cpp">
|
||||
<Filter>[0] src\beast</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\cpp\protobuf_core.cpp">
|
||||
<Filter>[0] src\protobuf</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\modules\ripple_basics\containers\ripple_RangeSet.cpp">
|
||||
<Filter>[1] Ripple\ripple_basics\containers</Filter>
|
||||
</ClCompile>
|
||||
@@ -876,6 +873,9 @@
|
||||
<ClCompile Include="..\..\build\proto\ripple.pb.cc">
|
||||
<Filter>[1] Ripple</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\protobuf_core.cpp">
|
||||
<Filter>[0] src\protobuf</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\modules\ripple_app\ripple_app.h">
|
||||
|
||||
@@ -1,531 +0,0 @@
|
||||
2013-02-27 version 2.5.0:
|
||||
|
||||
General
|
||||
* New notion "import public" that allows a proto file to forward the content
|
||||
it imports to its importers. For example,
|
||||
// foo.proto
|
||||
import public "bar.proto";
|
||||
import "baz.proto";
|
||||
|
||||
// qux.proto
|
||||
import "foo.proto";
|
||||
// Stuff defined in bar.proto may be used in this file, but stuff from
|
||||
// baz.proto may NOT be used without importing it explicitly.
|
||||
This is useful for moving proto files. To move a proto file, just leave
|
||||
a single "import public" in the old proto file.
|
||||
* New enum option "allow_alias" that specifies whether different symbols can
|
||||
be assigned the same numeric value. Default value is "true". Setting it to
|
||||
false causes the compiler to reject enum definitions where multiple symbols
|
||||
have the same numeric value.
|
||||
Note: We plan to flip the default value to "false" in a future release.
|
||||
Projects using enum aliases should set the option to "true" in their .proto
|
||||
files.
|
||||
|
||||
C++
|
||||
* New generated method set_allocated_foo(Type* foo) for message and string
|
||||
fields. This method allows you to set the field to a pre-allocated object
|
||||
and the containing message takes the ownership of that object.
|
||||
* Added SetAllocatedExtension() and ReleaseExtension() to extensions API.
|
||||
* Custom options are now formatted correctly when descriptors are printed in
|
||||
text format.
|
||||
* Various speed optimizations.
|
||||
|
||||
Java
|
||||
* Comments in proto files are now collected and put into generated code as
|
||||
comments for corresponding classes and data members.
|
||||
* Added Parser to parse directly into messages without a Builder. For
|
||||
example,
|
||||
Foo foo = Foo.PARSER.ParseFrom(input);
|
||||
Using Parser is ~25% faster than using Builder to parse messages.
|
||||
* Added getters/setters to access the underlying ByteString of a string field
|
||||
directly.
|
||||
* ByteString now supports more operations: substring(), prepend(), and
|
||||
append(). The implementation of ByteString uses a binary tree structure
|
||||
to support these operations efficiently.
|
||||
* New method findInitializationErrors() that lists all missing required
|
||||
fields.
|
||||
* Various code size and speed optimizations.
|
||||
|
||||
Python
|
||||
* Added support for dynamic message creation. DescriptorDatabase,
|
||||
DescriptorPool, and MessageFactory work like their C++ couterparts to
|
||||
simplify Descriptor construction from *DescriptorProtos, and MessageFactory
|
||||
provides a message instance from a Descriptor.
|
||||
* Added pickle support for protobuf messages.
|
||||
* Unknown fields are now preserved after parsing.
|
||||
* Fixed bug where custom options were not correctly populated. Custom
|
||||
options can be accessed now.
|
||||
* Added EnumTypeWrapper that provides better accessibility to enum types.
|
||||
* Added ParseMessage(descriptor, bytes) to generate a new Message instance
|
||||
from a descriptor and a byte string.
|
||||
|
||||
2011-05-01 version 2.4.1:
|
||||
|
||||
C++
|
||||
* Fixed the frendship problem for old compilers to make the library now gcc 3
|
||||
compatible again.
|
||||
* Fixed vcprojects/extract_includes.bat to extract compiler/plugin.h.
|
||||
|
||||
Java
|
||||
* Removed usages of JDK 1.6 only features to make the library now JDK 1.5
|
||||
compatible again.
|
||||
* Fixed a bug about negative enum values.
|
||||
* serialVersionUID is now defined in generated messages for java serializing.
|
||||
* Fixed protoc to use java.lang.Object, which makes "Object" now a valid
|
||||
message name again.
|
||||
|
||||
Python
|
||||
* Experimental C++ implementation now requires C++ protobuf library installed.
|
||||
See the README.txt in the python directory for details.
|
||||
|
||||
2011-02-02 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
|
||||
* Parsers for repeated numeric fields now always accept both packed and
|
||||
unpacked input. The [packed=true] option only affects serializers.
|
||||
Therefore, it is possible to switch a field to packed format without
|
||||
breaking backwards-compatibility -- as long as all parties are using
|
||||
protobuf 2.3.0 or above, at least.
|
||||
* The generic RPC service code generated by the C++, Java, and Python
|
||||
generators can be disabled via file options:
|
||||
option cc_generic_services = false;
|
||||
option java_generic_services = false;
|
||||
option py_generic_services = false;
|
||||
This allows plugins to generate alternative code, possibly specific to some
|
||||
particular RPC implementation.
|
||||
|
||||
protoc
|
||||
* Now supports a plugin system for code generators. Plugins can generate
|
||||
code for new languages or inject additional code into the output of other
|
||||
code generators. Plugins are just binaries which accept a protocol buffer
|
||||
on stdin and write a protocol buffer to stdout, so they may be written in
|
||||
any language. See src/google/protobuf/compiler/plugin.proto.
|
||||
**WARNING**: Plugins are experimental. The interface may change in a
|
||||
future version.
|
||||
* If the output location ends in .zip or .jar, protoc will write its output
|
||||
to a zip/jar archive instead of a directory. For example:
|
||||
protoc --java_out=myproto_srcs.jar --python_out=myproto.zip myproto.proto
|
||||
Currently the archive contents are not compressed, though this could change
|
||||
in the future.
|
||||
* inf, -inf, and nan can now be used as default values for float and double
|
||||
fields.
|
||||
|
||||
C++
|
||||
* Various speed and code size optimizations.
|
||||
* DynamicMessageFactory is now fully thread-safe.
|
||||
* Message::Utf8DebugString() method is like DebugString() but avoids escaping
|
||||
UTF-8 bytes.
|
||||
* Compiled-in message types can now contain dynamic extensions, through use
|
||||
of CodedInputStream::SetExtensionRegistry().
|
||||
* Now compiles shared libraries (DLLs) by default on Cygwin and MinGW, to
|
||||
match other platforms. Use --disable-shared to avoid this.
|
||||
|
||||
Java
|
||||
* parseDelimitedFrom() and mergeDelimitedFrom() now detect EOF and return
|
||||
false/null instead of throwing an exception.
|
||||
* Fixed some initialization ordering bugs.
|
||||
* Fixes for OpenJDK 7.
|
||||
|
||||
Python
|
||||
* 10-25 times faster than 2.2.0, still pure-Python.
|
||||
* Calling a mutating method on a sub-message always instantiates the message
|
||||
in its parent even if the mutating method doesn't actually mutate anything
|
||||
(e.g. parsing from an empty string).
|
||||
* Expanded descriptors a bit.
|
||||
|
||||
2009-08-11 version 2.2.0:
|
||||
|
||||
C++
|
||||
* Lite mode: The "optimize_for = LITE_RUNTIME" option causes the compiler
|
||||
to generate code which only depends libprotobuf-lite, which is much smaller
|
||||
than libprotobuf but lacks descriptors, reflection, and some other features.
|
||||
* Fixed bug where Message.Swap(Message) was only implemented for
|
||||
optimize_for_speed. Swap now properly implemented in both modes
|
||||
(Issue 91).
|
||||
* Added RemoveLast and SwapElements(index1, index2) to Reflection
|
||||
interface for repeated elements.
|
||||
* Added Swap(Message) to Reflection interface.
|
||||
* Floating-point literals in generated code that are intended to be
|
||||
single-precision now explicitly have 'f' suffix to avoid pedantic warnings
|
||||
produced by some compilers.
|
||||
* The [deprecated=true] option now causes the C++ code generator to generate
|
||||
a GCC-style deprecation annotation (no-op on other compilers).
|
||||
* google::protobuf::GetEnumDescriptor<SomeGeneratedEnumType>() returns the
|
||||
EnumDescriptor for that type -- useful for templates which cannot call
|
||||
SomeGeneratedEnumType_descriptor().
|
||||
* Various optimizations and obscure bug fixes.
|
||||
|
||||
Java
|
||||
* Lite mode: The "optimize_for = LITE_RUNTIME" option causes the compiler
|
||||
to generate code which only depends libprotobuf-lite, which is much smaller
|
||||
than libprotobuf but lacks descriptors, reflection, and some other features.
|
||||
* Lots of style cleanups.
|
||||
|
||||
Python
|
||||
* Fixed endianness bug with floats and doubles.
|
||||
* Text format parsing support.
|
||||
* Fix bug with parsing packed repeated fields in embedded messages.
|
||||
* Ability to initialize fields by passing keyword args to constructor.
|
||||
* Support iterators in extend and __setslice__ for containers.
|
||||
|
||||
2009-05-13 version 2.1.0:
|
||||
|
||||
General
|
||||
* Repeated fields of primitive types (types other that string, group, and
|
||||
nested messages) may now use the option [packed = true] to get a more
|
||||
efficient encoding. In the new encoding, the entire list is written
|
||||
as a single byte blob using the "length-delimited" wire type. Within
|
||||
this blob, the individual values are encoded the same way they would
|
||||
be normally except without a tag before each value (thus, they are
|
||||
tightly "packed").
|
||||
* For each field, the generated code contains an integer constant assigned
|
||||
to the field number. For example, the .proto file:
|
||||
message Foo { optional int bar_baz = 123; }
|
||||
would generate the following constants, all with the integer value 123:
|
||||
C++: Foo::kBarBazFieldNumber
|
||||
Java: Foo.BAR_BAZ_FIELD_NUMBER
|
||||
Python: Foo.BAR_BAZ_FIELD_NUMBER
|
||||
Constants are also generated for extensions, with the same naming scheme.
|
||||
These constants may be used as switch cases.
|
||||
* Updated bundled Google Test to version 1.3.0. Google Test is now bundled
|
||||
in its verbatim form as a nested autoconf package, so you can drop in any
|
||||
other version of Google Test if needed.
|
||||
* optimize_for = SPEED is now the default, by popular demand. Use
|
||||
optimize_for = CODE_SIZE if code size is more important in your app.
|
||||
* It is now an error to define a default value for a repeated field.
|
||||
Previously, this was silently ignored (it had no effect on the generated
|
||||
code).
|
||||
* Fields can now be marked deprecated like:
|
||||
optional int32 foo = 1 [deprecated = true];
|
||||
Currently this does not have any actual effect, but in the future the code
|
||||
generators may generate deprecation annotations in each language.
|
||||
* Cross-compiling should now be possible using the --with-protoc option to
|
||||
configure. See README.txt for more info.
|
||||
|
||||
protoc
|
||||
* --error_format=msvs option causes errors to be printed in Visual Studio
|
||||
format, which should allow them to be clicked on in the build log to go
|
||||
directly to the error location.
|
||||
* The type name resolver will no longer resolve type names to fields. For
|
||||
example, this now works:
|
||||
message Foo {}
|
||||
message Bar {
|
||||
optional int32 Foo = 1;
|
||||
optional Foo baz = 2;
|
||||
}
|
||||
Previously, the type of "baz" would resolve to "Bar.Foo", and you'd get
|
||||
an error because Bar.Foo is a field, not a type. Now the type of "baz"
|
||||
resolves to the message type Foo. This change is unlikely to make a
|
||||
difference to anyone who follows the Protocol Buffers style guide.
|
||||
|
||||
C++
|
||||
* Several optimizations, including but not limited to:
|
||||
- Serialization, especially to flat arrays, is 10%-50% faster, possibly
|
||||
more for small objects.
|
||||
- Several descriptor operations which previously required locking no longer
|
||||
do.
|
||||
- Descriptors are now constructed lazily on first use, rather than at
|
||||
process startup time. This should save memory in programs which do not
|
||||
use descriptors or reflection.
|
||||
- UnknownFieldSet completely redesigned to be more efficient (especially in
|
||||
terms of memory usage).
|
||||
- Various optimizations to reduce code size (though the serialization speed
|
||||
optimizations increased code size).
|
||||
* Message interface has method ParseFromBoundedZeroCopyStream() which parses
|
||||
a limited number of bytes from an input stream rather than parsing until
|
||||
EOF.
|
||||
* GzipInputStream and GzipOutputStream support reading/writing gzip- or
|
||||
zlib-compressed streams if zlib is available.
|
||||
(google/protobuf/io/gzip_stream.h)
|
||||
* DescriptorPool::FindAllExtensions() and corresponding
|
||||
DescriptorDatabase::FindAllExtensions() can be used to enumerate all
|
||||
extensions of a given type.
|
||||
* For each enum type Foo, protoc will generate functions:
|
||||
const string& Foo_Name(Foo value);
|
||||
bool Foo_Parse(const string& name, Foo* result);
|
||||
The former returns the name of the enum constant corresponding to the given
|
||||
value while the latter finds the value corresponding to a name.
|
||||
* RepeatedField and RepeatedPtrField now have back-insertion iterators.
|
||||
* String fields now have setters that take a char* and a size, in addition
|
||||
to the existing ones that took char* or const string&.
|
||||
* DescriptorPool::AllowUnknownDependencies() may be used to tell
|
||||
DescriptorPool to create placeholder descriptors for unknown entities
|
||||
referenced in a FileDescriptorProto. This can allow you to parse a .proto
|
||||
file without having access to other .proto files that it imports, for
|
||||
example.
|
||||
* Updated gtest to latest version. The gtest package is now included as a
|
||||
nested autoconf package, so it should be able to drop new versions into the
|
||||
"gtest" subdirectory without modification.
|
||||
|
||||
Java
|
||||
* Fixed bug where Message.mergeFrom(Message) failed to merge extensions.
|
||||
* Message interface has new method toBuilder() which is equivalent to
|
||||
newBuilderForType().mergeFrom(this).
|
||||
* All enums now implement the ProtocolMessageEnum interface.
|
||||
* Setting a field to null now throws NullPointerException.
|
||||
* Fixed tendency for TextFormat's parsing to overflow the stack when
|
||||
parsing large string values. The underlying problem is with Java's
|
||||
regex implementation (which unfortunately uses recursive backtracking
|
||||
rather than building an NFA). Worked around by making use of possesive
|
||||
quantifiers.
|
||||
* Generated service classes now also generate pure interfaces. For a service
|
||||
Foo, Foo.Interface is a pure interface containing all of the service's
|
||||
defined methods. Foo.newReflectiveService() can be called to wrap an
|
||||
instance of this interface in a class that implements the generic
|
||||
RpcService interface, which provides reflection support that is usually
|
||||
needed by RPC server implementations.
|
||||
* RPC interfaces now support blocking operation in addition to non-blocking.
|
||||
The protocol compiler generates separate blocking and non-blocking stubs
|
||||
which operate against separate blocking and non-blocking RPC interfaces.
|
||||
RPC implementations will have to implement the new interfaces in order to
|
||||
support blocking mode.
|
||||
* New I/O methods parseDelimitedFrom(), mergeDelimitedFrom(), and
|
||||
writeDelimitedTo() read and write "delemited" messages from/to a stream,
|
||||
meaning that the message size precedes the data. This way, you can write
|
||||
multiple messages to a stream without having to worry about delimiting
|
||||
them yourself.
|
||||
* Throw a more descriptive exception when build() is double-called.
|
||||
* Add a method to query whether CodedInputStream is at the end of the input
|
||||
stream.
|
||||
* Add a method to reset a CodedInputStream's size counter; useful when
|
||||
reading many messages with the same stream.
|
||||
* equals() and hashCode() now account for unknown fields.
|
||||
|
||||
Python
|
||||
* Added slicing support for repeated scalar fields. Added slice retrieval and
|
||||
removal of repeated composite fields.
|
||||
* Updated RPC interfaces to allow for blocking operation. A client may
|
||||
now pass None for a callback when making an RPC, in which case the
|
||||
call will block until the response is received, and the response
|
||||
object will be returned directly to the caller. This interface change
|
||||
cannot be used in practice until RPC implementations are updated to
|
||||
implement it.
|
||||
* Changes to input_stream.py should make protobuf compatible with appengine.
|
||||
|
||||
2008-11-25 version 2.0.3:
|
||||
|
||||
protoc
|
||||
* Enum values may now have custom options, using syntax similar to field
|
||||
options.
|
||||
* Fixed bug where .proto files which use custom options but don't actually
|
||||
define them (i.e. they import another .proto file defining the options)
|
||||
had to explicitly import descriptor.proto.
|
||||
* Adjacent string literals in .proto files will now be concatenated, like in
|
||||
C.
|
||||
* If an input file is a Windows absolute path (e.g. "C:\foo\bar.proto") and
|
||||
the import path only contains "." (or contains "." but does not contain
|
||||
the file), protoc incorrectly thought that the file was under ".", because
|
||||
it thought that the path was relative (since it didn't start with a slash).
|
||||
This has been fixed.
|
||||
|
||||
C++
|
||||
* Generated message classes now have a Swap() method which efficiently swaps
|
||||
the contents of two objects.
|
||||
* All message classes now have a SpaceUsed() method which returns an estimate
|
||||
of the number of bytes of allocated memory currently owned by the object.
|
||||
This is particularly useful when you are reusing a single message object
|
||||
to improve performance but want to make sure it doesn't bloat up too large.
|
||||
* New method Message::SerializeAsString() returns a string containing the
|
||||
serialized data. May be more convenient than calling
|
||||
SerializeToString(string*).
|
||||
* In debug mode, log error messages when string-type fields are found to
|
||||
contain bytes that are not valid UTF-8.
|
||||
* Fixed bug where a message with multiple extension ranges couldn't parse
|
||||
extensions.
|
||||
* Fixed bug where MergeFrom(const Message&) didn't do anything if invoked on
|
||||
a message that contained no fields (but possibly contained extensions).
|
||||
* Fixed ShortDebugString() to not be O(n^2). Durr.
|
||||
* Fixed crash in TextFormat parsing if the first token in the input caused a
|
||||
tokenization error.
|
||||
* Fixed obscure bugs in zero_copy_stream_impl.cc.
|
||||
* Added support for HP C++ on Tru64.
|
||||
* Only build tests on "make check", not "make".
|
||||
* Fixed alignment issue that caused crashes when using DynamicMessage on
|
||||
64-bit Sparc machines.
|
||||
* Simplify template usage to work with MSVC 2003.
|
||||
* Work around GCC 4.3.x x86_64 compiler bug that caused crashes on startup.
|
||||
(This affected Fedora 9 in particular.)
|
||||
* Now works on "Solaris 10 using recent Sun Studio".
|
||||
|
||||
Java
|
||||
* New overload of mergeFrom() which parses a slice of a byte array instead
|
||||
of the whole thing.
|
||||
* New method ByteString.asReadOnlyByteBuffer() does what it sounds like.
|
||||
* Improved performance of isInitialized() when optimizing for code size.
|
||||
|
||||
Python
|
||||
* Corrected ListFields() signature in Message base class to match what
|
||||
subclasses actually implement.
|
||||
* Some minor refactoring.
|
||||
* Don't pass self as first argument to superclass constructor (no longer
|
||||
allowed in Python 2.6).
|
||||
|
||||
2008-09-29 version 2.0.2:
|
||||
|
||||
General
|
||||
* License changed from Apache 2.0 to New BSD.
|
||||
* It is now possible to define custom "options", which are basically
|
||||
annotations which may be placed on definitions in a .proto file.
|
||||
For example, you might define a field option called "foo" like so:
|
||||
import "google/protobuf/descriptor.proto"
|
||||
extend google.protobuf.FieldOptions {
|
||||
optional string foo = 12345;
|
||||
}
|
||||
Then you annotate a field using the "foo" option:
|
||||
message MyMessage {
|
||||
optional int32 some_field = 1 [(foo) = "bar"]
|
||||
}
|
||||
The value of this option is then visible via the message's
|
||||
Descriptor:
|
||||
const FieldDescriptor* field =
|
||||
MyMessage::descriptor()->FindFieldByName("some_field");
|
||||
assert(field->options().GetExtension(foo) == "bar");
|
||||
This feature has been implemented and tested in C++ and Java.
|
||||
Other languages may or may not need to do extra work to support
|
||||
custom options, depending on how they construct descriptors.
|
||||
|
||||
C++
|
||||
* Fixed some GCC warnings that only occur when using -pedantic.
|
||||
* Improved static initialization code, making ordering more
|
||||
predictable among other things.
|
||||
* TextFormat will no longer accept messages which contain multiple
|
||||
instances of a singular field. Previously, the latter instance
|
||||
would overwrite the former.
|
||||
* Now works on systems that don't have hash_map.
|
||||
|
||||
Java
|
||||
* Print @Override annotation in generated code where appropriate.
|
||||
|
||||
Python
|
||||
* Strings now use the "unicode" type rather than the "str" type.
|
||||
String fields may still be assigned ASCII "str" values; they will
|
||||
automatically be converted.
|
||||
* Adding a property to an object representing a repeated field now
|
||||
raises an exception. For example:
|
||||
# No longer works (and never should have).
|
||||
message.some_repeated_field.foo = 1
|
||||
|
||||
Windows
|
||||
* We now build static libraries rather than DLLs by default on MSVC.
|
||||
See vsprojects/readme.txt for more information.
|
||||
|
||||
2008-08-15 version 2.0.1:
|
||||
|
||||
protoc
|
||||
* New flags --encode and --decode can be used to convert between protobuf text
|
||||
format and binary format from the command-line.
|
||||
* New flag --descriptor_set_out can be used to write FileDescriptorProtos for
|
||||
all parsed files directly into a single output file. This is particularly
|
||||
useful if you wish to parse .proto files from programs written in languages
|
||||
other than C++: just run protoc as a background process and have it output
|
||||
a FileDescriptorList, then parse that natively.
|
||||
* Improved error message when an enum value's name conflicts with another
|
||||
symbol defined in the enum type's scope, e.g. if two enum types declared
|
||||
in the same scope have values with the same name. This is disallowed for
|
||||
compatibility with C++, but this wasn't clear from the error.
|
||||
* Fixed absolute output paths on Windows.
|
||||
* Allow trailing slashes in --proto_path mappings.
|
||||
|
||||
C++
|
||||
* Reflection objects are now per-class rather than per-instance. To make this
|
||||
possible, the Reflection interface had to be changed such that all methods
|
||||
take the Message instance as a parameter. This change improves performance
|
||||
significantly in memory-bandwidth-limited use cases, since it makes the
|
||||
message objects smaller. Note that source-incompatible interface changes
|
||||
like this will not be made again after the library leaves beta.
|
||||
* Heuristically detect sub-messages when printing unknown fields.
|
||||
* Fix static initialization ordering bug that caused crashes at startup when
|
||||
compiling on Mac with static linking.
|
||||
* Fixed TokenizerTest when compiling with -DNDEBUG on Linux.
|
||||
* Fixed incorrect definition of kint32min.
|
||||
* Fix bytes type setter to work with byte sequences with embedded NULLs.
|
||||
* Other irrelevant tweaks.
|
||||
|
||||
Java
|
||||
* Fixed UnknownFieldSet's parsing of varints larger than 32 bits.
|
||||
* Fixed TextFormat's parsing of "inf" and "nan".
|
||||
* Fixed TextFormat's parsing of comments.
|
||||
* Added info to Java POM that will be required when we upload the
|
||||
package to a Maven repo.
|
||||
|
||||
Python
|
||||
* MergeFrom(message) and CopyFrom(message) are now implemented.
|
||||
* SerializeToString() raises an exception if the message is missing required
|
||||
fields.
|
||||
* Code organization improvements.
|
||||
* Fixed doc comments for RpcController and RpcChannel, which had somehow been
|
||||
swapped.
|
||||
* Fixed text_format_test on Windows where floating-point exponents sometimes
|
||||
contain extra zeros.
|
||||
* Fix Python service CallMethod() implementation.
|
||||
|
||||
Other
|
||||
* Improved readmes.
|
||||
* VIM syntax highlighting improvements.
|
||||
|
||||
2008-07-07 version 2.0.0:
|
||||
|
||||
* First public release.
|
||||
@@ -1,90 +0,0 @@
|
||||
This file contains a list of people who have made large contributions
|
||||
to the public version of Protocol Buffers.
|
||||
|
||||
Original Protocol Buffers design and implementation:
|
||||
Sanjay Ghemawat <sanjay@google.com>
|
||||
Jeff Dean <jeff@google.com>
|
||||
Daniel Dulitz <daniel@google.com>
|
||||
Craig Silverstein
|
||||
Paul Haahr <haahr@google.com>
|
||||
Corey Anderson <corin@google.com>
|
||||
(and many others)
|
||||
|
||||
Proto2 C++ and Java primary author:
|
||||
Kenton Varda <kenton@google.com>
|
||||
|
||||
Proto2 Python primary authors:
|
||||
Will Robinson <robinson@google.com>
|
||||
Petar Petrov <petar@google.com>
|
||||
|
||||
Large code contributions:
|
||||
Jason Hsueh <jasonh@google.com>
|
||||
Joseph Schorr <jschorr@google.com>
|
||||
Wenbo Zhu <wenboz@google.com>
|
||||
|
||||
Large quantity of code reviews:
|
||||
Scott Bruce <sbruce@google.com>
|
||||
Frank Yellin
|
||||
Neal Norwitz <nnorwitz@google.com>
|
||||
Jeffrey Yasskin <jyasskin@google.com>
|
||||
Ambrose Feinstein <ambrose@google.com>
|
||||
|
||||
Documentation:
|
||||
Lisa Carey <lcarey@google.com>
|
||||
|
||||
Maven packaging:
|
||||
Gregory Kick <gak@google.com>
|
||||
|
||||
Patch contributors:
|
||||
Kevin Ko <kevin.s.ko@gmail.com>
|
||||
* Small patch to handle trailing slashes in --proto_path flag.
|
||||
Johan Euphrosine <proppy@aminche.com>
|
||||
* Small patch to fix Python CallMethod().
|
||||
Ulrich Kunitz <kune@deine-taler.de>
|
||||
* Small optimizations to Python serialization.
|
||||
Leandro Lucarella <llucax@gmail.com>
|
||||
* VI syntax highlighting tweaks.
|
||||
* Fix compiler to not make output executable.
|
||||
Dilip Joseph <dilip.antony.joseph@gmail.com>
|
||||
* Heuristic detection of sub-messages when printing unknown fields in
|
||||
text format.
|
||||
Brian Atkinson <nairb774@gmail.com>
|
||||
* Added @Override annotation to generated Java code where appropriate.
|
||||
Vincent Choini<6E>re <Choiniere.Vincent@hydro.qc.ca>
|
||||
* Tru64 support.
|
||||
Monty Taylor <monty.taylor@gmail.com>
|
||||
* Solaris 10 + Sun Studio fixes.
|
||||
Alek Storm <alek.storm@gmail.com>
|
||||
* Slicing support for repeated scalar fields for the Python API.
|
||||
Oleg Smolsky <oleg.smolsky@gmail.com>
|
||||
* MS Visual Studio error format option.
|
||||
* Detect unordered_map in stl_hash.m4.
|
||||
Brian Olson <brianolson@google.com>
|
||||
* gzip/zlib I/O support.
|
||||
Michael Poole <mdpoole@troilus.org>
|
||||
* Fixed warnings about generated constructors not explicitly initializing
|
||||
all fields (only present with certain compiler settings).
|
||||
* Added generation of field number constants.
|
||||
Wink Saville <wink@google.com>
|
||||
* Fixed initialization ordering problem in logging code.
|
||||
Will Pierce <willp@nuclei.com>
|
||||
* Small patch improving performance of in Python serialization.
|
||||
Alexandre Vassalotti <alexandre@peadrop.com>
|
||||
* Emacs mode for Protocol Buffers (editors/protobuf-mode.el).
|
||||
Scott Stafford <scott.stafford@gmail.com>
|
||||
* Added Swap(), SwapElements(), and RemoveLast() to Reflection interface.
|
||||
Alexander Melnikov <alm@sibmail.ru>
|
||||
* HPUX support.
|
||||
Oliver Jowett <oliver.jowett@gmail.com>
|
||||
* Detect whether zlib is new enough in configure script.
|
||||
* Fixes for Solaris 10 32/64-bit confusion.
|
||||
Evan Jones <evanj@mit.edu>
|
||||
* Optimize Java serialization code when writing a small message to a stream.
|
||||
* Optimize Java serialization of strings so that UTF-8 encoding happens only
|
||||
once per string per serialization call.
|
||||
* Clean up some Java warnings.
|
||||
* Fix bug with permanent callbacks that delete themselves when run.
|
||||
Michael Kucharski <m.kucharski@gmail.com>
|
||||
* Added CodedInputStream.getTotalBytesRead().
|
||||
Kacper Kowalik <xarthisius.kk@gmail.com>
|
||||
* Fixed m4/acx_pthread.m4 problem for some Linux distributions.
|
||||
@@ -1,33 +0,0 @@
|
||||
Copyright 2008, Google Inc.
|
||||
All rights reserved.
|
||||
|
||||
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.
|
||||
|
||||
Code generated by the Protocol Buffer compiler is owned by the owner
|
||||
of the input file used when generating it. This code is not
|
||||
standalone and requires a support library to be linked with it. This
|
||||
support library is itself covered by the above license.
|
||||
@@ -1,237 +0,0 @@
|
||||
This file contains detailed but generic information on building and
|
||||
installing the C++ part of this project. For shorter instructions,
|
||||
as well as instructions for compiling and installing the Java or
|
||||
Python parts, see README.
|
||||
|
||||
======================================================================
|
||||
|
||||
Copyright 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software
|
||||
Foundation, Inc.
|
||||
|
||||
This file is free documentation; the Free Software Foundation gives
|
||||
unlimited permission to copy, distribute and modify it.
|
||||
|
||||
|
||||
Basic Installation
|
||||
==================
|
||||
|
||||
These are generic installation instructions.
|
||||
|
||||
The `configure' shell script attempts to guess correct values for
|
||||
various system-dependent variables used during compilation. It uses
|
||||
those values to create a `Makefile' in each directory of the package.
|
||||
It may also create one or more `.h' files containing system-dependent
|
||||
definitions. Finally, it creates a shell script `config.status' that
|
||||
you can run in the future to recreate the current configuration, and a
|
||||
file `config.log' containing compiler output (useful mainly for
|
||||
debugging `configure').
|
||||
|
||||
It can also use an optional file (typically called `config.cache'
|
||||
and enabled with `--cache-file=config.cache' or simply `-C') that saves
|
||||
the results of its tests to speed up reconfiguring. (Caching is
|
||||
disabled by default to prevent problems with accidental use of stale
|
||||
cache files.)
|
||||
|
||||
If you need to do unusual things to compile the package, please try
|
||||
to figure out how `configure' could check whether to do them, and mail
|
||||
diffs or instructions to the address given in the `README' so they can
|
||||
be considered for the next release. If you are using the cache, and at
|
||||
some point `config.cache' contains results you don't want to keep, you
|
||||
may remove or edit it.
|
||||
|
||||
The file `configure.ac' (or `configure.in') is used to create
|
||||
`configure' by a program called `autoconf'. You only need
|
||||
`configure.ac' if you want to change it or regenerate `configure' using
|
||||
a newer version of `autoconf'.
|
||||
|
||||
The simplest way to compile this package is:
|
||||
|
||||
1. `cd' to the directory containing the package's source code and type
|
||||
`./configure' to configure the package for your system. If you're
|
||||
using `csh' on an old version of System V, you might need to type
|
||||
`sh ./configure' instead to prevent `csh' from trying to execute
|
||||
`configure' itself.
|
||||
|
||||
Running `configure' takes awhile. While running, it prints some
|
||||
messages telling which features it is checking for.
|
||||
|
||||
2. Type `make' to compile the package.
|
||||
|
||||
3. Optionally, type `make check' to run any self-tests that come with
|
||||
the package.
|
||||
|
||||
4. Type `make install' to install the programs and any data files and
|
||||
documentation.
|
||||
|
||||
5. You can remove the program binaries and object files from the
|
||||
source code directory by typing `make clean'. To also remove the
|
||||
files that `configure' created (so you can compile the package for
|
||||
a different kind of computer), type `make distclean'. There is
|
||||
also a `make maintainer-clean' target, but that is intended mainly
|
||||
for the package's developers. If you use it, you may have to get
|
||||
all sorts of other programs in order to regenerate files that came
|
||||
with the distribution.
|
||||
|
||||
Compilers and Options
|
||||
=====================
|
||||
|
||||
Some systems require unusual options for compilation or linking that
|
||||
the `configure' script does not know about. Run `./configure --help'
|
||||
for details on some of the pertinent environment variables.
|
||||
|
||||
You can give `configure' initial values for configuration parameters
|
||||
by setting variables in the command line or in the environment. Here
|
||||
is an example:
|
||||
|
||||
./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
|
||||
|
||||
*Note Defining Variables::, for more details.
|
||||
|
||||
Compiling For Multiple Architectures
|
||||
====================================
|
||||
|
||||
You can compile the package for more than one kind of computer at the
|
||||
same time, by placing the object files for each architecture in their
|
||||
own directory. To do this, you must use a version of `make' that
|
||||
supports the `VPATH' variable, such as GNU `make'. `cd' to the
|
||||
directory where you want the object files and executables to go and run
|
||||
the `configure' script. `configure' automatically checks for the
|
||||
source code in the directory that `configure' is in and in `..'.
|
||||
|
||||
If you have to use a `make' that does not support the `VPATH'
|
||||
variable, you have to compile the package for one architecture at a
|
||||
time in the source code directory. After you have installed the
|
||||
package for one architecture, use `make distclean' before reconfiguring
|
||||
for another architecture.
|
||||
|
||||
Installation Names
|
||||
==================
|
||||
|
||||
By default, `make install' will install the package's files in
|
||||
`/usr/local/bin', `/usr/local/man', etc. You can specify an
|
||||
installation prefix other than `/usr/local' by giving `configure' the
|
||||
option `--prefix=PATH'.
|
||||
|
||||
You can specify separate installation prefixes for
|
||||
architecture-specific files and architecture-independent files. If you
|
||||
give `configure' the option `--exec-prefix=PATH', the package will use
|
||||
PATH as the prefix for installing programs and libraries.
|
||||
Documentation and other data files will still use the regular prefix.
|
||||
|
||||
In addition, if you use an unusual directory layout you can give
|
||||
options like `--bindir=PATH' to specify different values for particular
|
||||
kinds of files. Run `configure --help' for a list of the directories
|
||||
you can set and what kinds of files go in them.
|
||||
|
||||
If the package supports it, you can cause programs to be installed
|
||||
with an extra prefix or suffix on their names by giving `configure' the
|
||||
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
|
||||
|
||||
Optional Features
|
||||
=================
|
||||
|
||||
Some packages pay attention to `--enable-FEATURE' options to
|
||||
`configure', where FEATURE indicates an optional part of the package.
|
||||
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
|
||||
is something like `gnu-as' or `x' (for the X Window System). The
|
||||
`README' should mention any `--enable-' and `--with-' options that the
|
||||
package recognizes.
|
||||
|
||||
For packages that use the X Window System, `configure' can usually
|
||||
find the X include and library files automatically, but if it doesn't,
|
||||
you can use the `configure' options `--x-includes=DIR' and
|
||||
`--x-libraries=DIR' to specify their locations.
|
||||
|
||||
Specifying the System Type
|
||||
==========================
|
||||
|
||||
There may be some features `configure' cannot figure out
|
||||
automatically, but needs to determine by the type of machine the package
|
||||
will run on. Usually, assuming the package is built to be run on the
|
||||
_same_ architectures, `configure' can figure that out, but if it prints
|
||||
a message saying it cannot guess the machine type, give it the
|
||||
`--build=TYPE' option. TYPE can either be a short name for the system
|
||||
type, such as `sun4', or a canonical name which has the form:
|
||||
|
||||
CPU-COMPANY-SYSTEM
|
||||
|
||||
where SYSTEM can have one of these forms:
|
||||
|
||||
OS KERNEL-OS
|
||||
|
||||
See the file `config.sub' for the possible values of each field. If
|
||||
`config.sub' isn't included in this package, then this package doesn't
|
||||
need to know the machine type.
|
||||
|
||||
If you are _building_ compiler tools for cross-compiling, you should
|
||||
use the `--target=TYPE' option to select the type of system they will
|
||||
produce code for.
|
||||
|
||||
If you want to _use_ a cross compiler, that generates code for a
|
||||
platform different from the build platform, you should specify the
|
||||
"host" platform (i.e., that on which the generated programs will
|
||||
eventually be run) with `--host=TYPE'.
|
||||
|
||||
Sharing Defaults
|
||||
================
|
||||
|
||||
If you want to set default values for `configure' scripts to share,
|
||||
you can create a site shell script called `config.site' that gives
|
||||
default values for variables like `CC', `cache_file', and `prefix'.
|
||||
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
||||
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
||||
`CONFIG_SITE' environment variable to the location of the site script.
|
||||
A warning: not all `configure' scripts look for a site script.
|
||||
|
||||
Defining Variables
|
||||
==================
|
||||
|
||||
Variables not defined in a site shell script can be set in the
|
||||
environment passed to `configure'. However, some packages may run
|
||||
configure again during the build, and the customized values of these
|
||||
variables may be lost. In order to avoid this problem, you should set
|
||||
them in the `configure' command line, using `VAR=value'. For example:
|
||||
|
||||
./configure CC=/usr/local2/bin/gcc
|
||||
|
||||
will cause the specified gcc to be used as the C compiler (unless it is
|
||||
overridden in the site shell script).
|
||||
|
||||
`configure' Invocation
|
||||
======================
|
||||
|
||||
`configure' recognizes the following options to control how it
|
||||
operates.
|
||||
|
||||
`--help'
|
||||
`-h'
|
||||
Print a summary of the options to `configure', and exit.
|
||||
|
||||
`--version'
|
||||
`-V'
|
||||
Print the version of Autoconf used to generate the `configure'
|
||||
script, and exit.
|
||||
|
||||
`--cache-file=FILE'
|
||||
Enable the cache: use and save the results of the tests in FILE,
|
||||
traditionally `config.cache'. FILE defaults to `/dev/null' to
|
||||
disable caching.
|
||||
|
||||
`--config-cache'
|
||||
`-C'
|
||||
Alias for `--cache-file=config.cache'.
|
||||
|
||||
`--quiet'
|
||||
`--silent'
|
||||
`-q'
|
||||
Do not print messages saying which checks are being made. To
|
||||
suppress all normal output, redirect it to `/dev/null' (any error
|
||||
messages will still be shown).
|
||||
|
||||
`--srcdir=DIR'
|
||||
Look for the package's source code in directory DIR. Usually
|
||||
`configure' can determine that directory automatically.
|
||||
|
||||
`configure' also accepts some other, not widely useful, options. Run
|
||||
`configure --help' for more details.
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
AUTOMAKE_OPTIONS = foreign
|
||||
|
||||
# Build . before src so that our all-local and clean-local hooks kicks in at
|
||||
# the right time.
|
||||
SUBDIRS = . src
|
||||
|
||||
# Always include gtest in distributions.
|
||||
DIST_SUBDIRS = $(subdirs) src
|
||||
|
||||
# Build gtest before we build protobuf tests. We don't add gtest to SUBDIRS
|
||||
# because then "make check" would also build and run all of gtest's own tests,
|
||||
# which takes a lot of time and is generally not useful to us. Also, we don't
|
||||
# want "make install" to recurse into gtest since we don't want to overwrite
|
||||
# the installed version of gtest if there is one.
|
||||
check-local:
|
||||
@echo "Making lib/libgtest.a lib/libgtest_main.a in gtest"
|
||||
@cd gtest && $(MAKE) $(AM_MAKEFLAGS) lib/libgtest.la lib/libgtest_main.la
|
||||
|
||||
# We would like to clean gtest when "make clean" is invoked. But we have to
|
||||
# be careful because clean-local is also invoked during "make distclean", but
|
||||
# "make distclean" already recurses into gtest because it's listed among the
|
||||
# DIST_SUBDIRS. distclean will delete gtest/Makefile, so if we then try to
|
||||
# cd to the directory again and "make clean" it will fail. So, check that the
|
||||
# Makefile exists before recursing.
|
||||
clean-local:
|
||||
@if test -e gtest/Makefile; then \
|
||||
echo "Making clean in gtest"; \
|
||||
cd gtest && $(MAKE) $(AM_MAKEFLAGS) clean; \
|
||||
fi
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = protobuf.pc protobuf-lite.pc
|
||||
|
||||
EXTRA_DIST = \
|
||||
autogen.sh \
|
||||
generate_descriptor_proto.sh \
|
||||
README.txt \
|
||||
INSTALL.txt \
|
||||
COPYING.txt \
|
||||
CONTRIBUTORS.txt \
|
||||
CHANGES.txt \
|
||||
editors/README.txt \
|
||||
editors/proto.vim \
|
||||
editors/protobuf-mode.el \
|
||||
vsprojects/config.h \
|
||||
vsprojects/extract_includes.bat \
|
||||
vsprojects/libprotobuf.vcproj \
|
||||
vsprojects/libprotobuf-lite.vcproj \
|
||||
vsprojects/libprotoc.vcproj \
|
||||
vsprojects/protobuf.sln \
|
||||
vsprojects/protoc.vcproj \
|
||||
vsprojects/readme.txt \
|
||||
vsprojects/test_plugin.vcproj \
|
||||
vsprojects/tests.vcproj \
|
||||
vsprojects/lite-test.vcproj \
|
||||
vsprojects/convert2008to2005.sh \
|
||||
examples/README.txt \
|
||||
examples/Makefile \
|
||||
examples/addressbook.proto \
|
||||
examples/add_person.cc \
|
||||
examples/list_people.cc \
|
||||
examples/AddPerson.java \
|
||||
examples/ListPeople.java \
|
||||
examples/add_person.py \
|
||||
examples/list_people.py \
|
||||
java/src/main/java/com/google/protobuf/AbstractMessage.java \
|
||||
java/src/main/java/com/google/protobuf/AbstractMessageLite.java \
|
||||
java/src/main/java/com/google/protobuf/AbstractParser.java \
|
||||
java/src/main/java/com/google/protobuf/BlockingRpcChannel.java \
|
||||
java/src/main/java/com/google/protobuf/BlockingService.java \
|
||||
java/src/main/java/com/google/protobuf/BoundedByteString.java \
|
||||
java/src/main/java/com/google/protobuf/ByteString.java \
|
||||
java/src/main/java/com/google/protobuf/CodedInputStream.java \
|
||||
java/src/main/java/com/google/protobuf/CodedOutputStream.java \
|
||||
java/src/main/java/com/google/protobuf/Descriptors.java \
|
||||
java/src/main/java/com/google/protobuf/DynamicMessage.java \
|
||||
java/src/main/java/com/google/protobuf/ExtensionRegistry.java \
|
||||
java/src/main/java/com/google/protobuf/ExtensionRegistryLite.java \
|
||||
java/src/main/java/com/google/protobuf/FieldSet.java \
|
||||
java/src/main/java/com/google/protobuf/GeneratedMessage.java \
|
||||
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/LazyField.java \
|
||||
java/src/main/java/com/google/protobuf/LazyStringArrayList.java \
|
||||
java/src/main/java/com/google/protobuf/LazyStringList.java \
|
||||
java/src/main/java/com/google/protobuf/LiteralByteString.java \
|
||||
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/Parser.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/RopeByteString.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/ServiceException.java \
|
||||
java/src/main/java/com/google/protobuf/Service.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/Utf8.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/BoundedByteStringTest.java \
|
||||
java/src/test/java/com/google/protobuf/ByteStringTest.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/IsValidUtf8Test.java \
|
||||
java/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.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/LiteralByteStringTest.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/ParserTest.java \
|
||||
java/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java \
|
||||
java/src/test/java/com/google/protobuf/RopeByteStringSubstringTest.java \
|
||||
java/src/test/java/com/google/protobuf/RopeByteStringTest.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_lite.proto \
|
||||
java/src/test/java/com/google/protobuf/nested_extension.proto \
|
||||
java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto \
|
||||
java/src/test/java/com/google/protobuf/non_nested_extension.proto \
|
||||
java/src/test/java/com/google/protobuf/test_bad_identifiers.proto \
|
||||
java/pom.xml \
|
||||
java/README.txt \
|
||||
python/google/protobuf/internal/generator_test.py \
|
||||
python/google/protobuf/internal/containers.py \
|
||||
python/google/protobuf/internal/decoder.py \
|
||||
python/google/protobuf/internal/descriptor_database_test.py \
|
||||
python/google/protobuf/internal/descriptor_pool_test.py \
|
||||
python/google/protobuf/internal/descriptor_test.py \
|
||||
python/google/protobuf/internal/encoder.py \
|
||||
python/google/protobuf/internal/enum_type_wrapper.py \
|
||||
python/google/protobuf/internal/factory_test1.proto \
|
||||
python/google/protobuf/internal/factory_test2.proto \
|
||||
python/google/protobuf/internal/message_cpp_test.py \
|
||||
python/google/protobuf/internal/message_factory_test.py \
|
||||
python/google/protobuf/internal/message_listener.py \
|
||||
python/google/protobuf/internal/message_test.py \
|
||||
python/google/protobuf/internal/more_extensions.proto \
|
||||
python/google/protobuf/internal/more_extensions_dynamic.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/reflection_cpp_generated_test.py \
|
||||
python/google/protobuf/internal/service_reflection_test.py \
|
||||
python/google/protobuf/internal/test_bad_identifiers.proto \
|
||||
python/google/protobuf/internal/test_util.py \
|
||||
python/google/protobuf/internal/text_format_test.py \
|
||||
python/google/protobuf/internal/type_checkers.py \
|
||||
python/google/protobuf/internal/unknown_fields_test.py \
|
||||
python/google/protobuf/internal/wire_format.py \
|
||||
python/google/protobuf/internal/wire_format_test.py \
|
||||
python/google/protobuf/internal/__init__.py \
|
||||
python/google/protobuf/pyext/python-proto2.cc \
|
||||
python/google/protobuf/pyext/python_descriptor.cc \
|
||||
python/google/protobuf/pyext/python_descriptor.h \
|
||||
python/google/protobuf/pyext/python_protobuf.cc \
|
||||
python/google/protobuf/pyext/python_protobuf.h \
|
||||
python/google/protobuf/descriptor.py \
|
||||
python/google/protobuf/descriptor_database.py \
|
||||
python/google/protobuf/descriptor_pool.py \
|
||||
python/google/protobuf/message.py \
|
||||
python/google/protobuf/message_factory.py \
|
||||
python/google/protobuf/reflection.py \
|
||||
python/google/protobuf/service.py \
|
||||
python/google/protobuf/service_reflection.py \
|
||||
python/google/protobuf/text_format.py \
|
||||
python/google/protobuf/__init__.py \
|
||||
python/google/__init__.py \
|
||||
python/ez_setup.py \
|
||||
python/setup.py \
|
||||
python/mox.py \
|
||||
python/stubout.py \
|
||||
python/README.txt
|
||||
|
||||
# Deletes all the files generated by autogen.sh.
|
||||
MAINTAINERCLEANFILES = \
|
||||
aclocal.m4 \
|
||||
config.guess \
|
||||
config.sub \
|
||||
configure \
|
||||
depcomp \
|
||||
install-sh \
|
||||
ltmain.sh \
|
||||
Makefile.in \
|
||||
missing \
|
||||
mkinstalldirs \
|
||||
config.h.in \
|
||||
stamp.h.in \
|
||||
m4/ltsugar.m4 \
|
||||
m4/libtool.m4 \
|
||||
m4/ltversion.m4 \
|
||||
m4/lt~obsolete.m4 \
|
||||
m4/ltoptions.m4
|
||||
@@ -1,152 +0,0 @@
|
||||
Protocol Buffers - Google's data interchange format
|
||||
Copyright 2008 Google Inc.
|
||||
http://code.google.com/apis/protocolbuffers/
|
||||
|
||||
C++ Installation - Unix
|
||||
=======================
|
||||
|
||||
To build and install the C++ Protocol Buffer runtime and the Protocol
|
||||
Buffer compiler (protoc) execute the following:
|
||||
|
||||
$ ./configure
|
||||
$ make
|
||||
$ make check
|
||||
$ make install
|
||||
|
||||
If "make check" fails, you can still install, but it is likely that
|
||||
some features of this library will not work correctly on your system.
|
||||
Proceed at your own risk.
|
||||
|
||||
"make install" may require superuser privileges.
|
||||
|
||||
For advanced usage information on configure and make, see INSTALL.txt.
|
||||
|
||||
** Hint on install location **
|
||||
|
||||
By default, the package will be installed to /usr/local. However,
|
||||
on many platforms, /usr/local/lib is not part of LD_LIBRARY_PATH.
|
||||
You can add it, but it may be easier to just install to /usr
|
||||
instead. To do this, invoke configure as follows:
|
||||
|
||||
./configure --prefix=/usr
|
||||
|
||||
If you already built the package with a different prefix, make sure
|
||||
to run "make clean" before building again.
|
||||
|
||||
** Compiling dependent packages **
|
||||
|
||||
To compile a package that uses Protocol Buffers, you need to pass
|
||||
various flags to your compiler and linker. As of version 2.2.0,
|
||||
Protocol Buffers integrates with pkg-config to manage this. If you
|
||||
have pkg-config installed, then you can invoke it to get a list of
|
||||
flags like so:
|
||||
|
||||
pkg-config --cflags protobuf # print compiler flags
|
||||
pkg-config --libs protobuf # print linker flags
|
||||
pkg-config --cflags --libs protobuf # print both
|
||||
|
||||
For example:
|
||||
|
||||
c++ my_program.cc my_proto.pb.cc `pkg-config --cflags --libs protobuf`
|
||||
|
||||
Note that packages written prior to the 2.2.0 release of Protocol
|
||||
Buffers may not yet integrate with pkg-config to get flags, and may
|
||||
not pass the correct set of flags to correctly link against
|
||||
libprotobuf. If the package in question uses autoconf, you can
|
||||
often fix the problem by invoking its configure script like:
|
||||
|
||||
configure CXXFLAGS="$(pkg-config --cflags protobuf)" \
|
||||
LIBS="$(pkg-config --libs protobuf)"
|
||||
|
||||
This will force it to use the correct flags.
|
||||
|
||||
If you are writing an autoconf-based package that uses Protocol
|
||||
Buffers, you should probably use the PKG_CHECK_MODULES macro in your
|
||||
configure script like:
|
||||
|
||||
PKG_CHECK_MODULES([protobuf], [protobuf])
|
||||
|
||||
See the pkg-config man page for more info.
|
||||
|
||||
If you only want protobuf-lite, substitute "protobuf-lite" in place
|
||||
of "protobuf" in these examples.
|
||||
|
||||
** Note for cross-compiling **
|
||||
|
||||
The makefiles normally invoke the protoc executable that they just
|
||||
built in order to build tests. When cross-compiling, the protoc
|
||||
executable may not be executable on the host machine. In this case,
|
||||
you must build a copy of protoc for the host machine first, then use
|
||||
the --with-protoc option to tell configure to use it instead. For
|
||||
example:
|
||||
|
||||
./configure --with-protoc=protoc
|
||||
|
||||
This will use the installed protoc (found in your $PATH) instead of
|
||||
trying to execute the one built during the build process. You can
|
||||
also use an executable that hasn't been installed. For example, if
|
||||
you built the protobuf package for your host machine in ../host,
|
||||
you might do:
|
||||
|
||||
./configure --with-protoc=../host/src/protoc
|
||||
|
||||
Either way, you must make sure that the protoc executable you use
|
||||
has the same version as the protobuf source code you are trying to
|
||||
use it with.
|
||||
|
||||
** Note for Solaris users **
|
||||
|
||||
Solaris 10 x86 has a bug that will make linking fail, complaining
|
||||
about libstdc++.la being invalid. We have included a work-around
|
||||
in this package. To use the work-around, run configure as follows:
|
||||
|
||||
./configure LDFLAGS=-L$PWD/src/solaris
|
||||
|
||||
See src/solaris/libstdc++.la for more info on this bug.
|
||||
|
||||
** Note for HP C++ Tru64 users **
|
||||
|
||||
To compile invoke configure as follows:
|
||||
|
||||
./configure CXXFLAGS="-O -std ansi -ieee -D__USE_STD_IOSTREAM"
|
||||
|
||||
Also, you will need to use gmake instead of make.
|
||||
|
||||
C++ Installation - Windows
|
||||
==========================
|
||||
|
||||
If you are using Microsoft Visual C++, see vsprojects/readme.txt.
|
||||
|
||||
If you are using Cygwin or MinGW, follow the Unix installation
|
||||
instructions, above.
|
||||
|
||||
Binary Compatibility Warning
|
||||
============================
|
||||
|
||||
Due to the nature of C++, it is unlikely that any two versions of the
|
||||
Protocol Buffers C++ runtime libraries will have compatible ABIs.
|
||||
That is, if you linked an executable against an older version of
|
||||
libprotobuf, it is unlikely to work with a newer version without
|
||||
re-compiling. This problem, when it occurs, will normally be detected
|
||||
immediately on startup of your app. Still, you may want to consider
|
||||
using static linkage. You can configure this package to install
|
||||
static libraries only using:
|
||||
|
||||
./configure --disable-shared
|
||||
|
||||
Java and Python Installation
|
||||
============================
|
||||
|
||||
The Java and Python runtime libraries for Protocol Buffers are located
|
||||
in the java and python directories. See the README file in each
|
||||
directory for more information on how to compile and install them.
|
||||
Note that both of them require you to first install the Protocol
|
||||
Buffer compiler (protoc), which is part of the C++ package.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
The complete documentation for Protocol Buffers is available via the
|
||||
web at:
|
||||
|
||||
http://code.google.com/apis/protocolbuffers/
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Run this script to generate the configure script and other files that will
|
||||
# be included in the distribution. These files are not checked in because they
|
||||
# are automatically generated.
|
||||
|
||||
set -e
|
||||
|
||||
# Check that we're being run from the right directory.
|
||||
if test ! -f src/google/protobuf/stubs/common.h; then
|
||||
cat >&2 << __EOF__
|
||||
Could not find source code. Make sure you are running this script from the
|
||||
root of the distribution tree.
|
||||
__EOF__
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check that gtest is present. Usually it is already there since the
|
||||
# directory is set up as an SVN external.
|
||||
if test ! -e gtest; then
|
||||
echo "Google Test not present. Fetching gtest-1.5.0 from the web..."
|
||||
curl http://googletest.googlecode.com/files/gtest-1.5.0.tar.bz2 | tar jx
|
||||
mv gtest-1.5.0 gtest
|
||||
fi
|
||||
|
||||
set -ex
|
||||
|
||||
# Temporary hack: Must change C runtime library to "multi-threaded DLL",
|
||||
# otherwise it will be set to "multi-threaded static" when MSVC upgrades
|
||||
# the project file to MSVC 2005/2008. vladl of Google Test says gtest will
|
||||
# probably change their default to match, then this will be unnecessary.
|
||||
# One of these mappings converts the debug configuration and the other
|
||||
# converts the release configuration. I don't know which is which.
|
||||
sed -i -e 's/RuntimeLibrary="5"/RuntimeLibrary="3"/g;
|
||||
s/RuntimeLibrary="4"/RuntimeLibrary="2"/g;' gtest/msvc/*.vcproj
|
||||
|
||||
# TODO(kenton): Remove the ",no-obsolete" part and fix the resulting warnings.
|
||||
autoreconf -f -i -Wall,no-obsolete
|
||||
|
||||
rm -rf autom4te.cache config.h.in~
|
||||
exit 0
|
||||
@@ -1,203 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2009 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.protocolbuffers;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.CodedInputStream;
|
||||
import com.google.protobuf.CodedOutputStream;
|
||||
import com.google.protobuf.Message;
|
||||
|
||||
public class ProtoBench {
|
||||
|
||||
private static final long MIN_SAMPLE_TIME_MS = 2 * 1000;
|
||||
private static final long TARGET_TIME_MS = 30 * 1000;
|
||||
|
||||
private ProtoBench() {
|
||||
// Prevent instantiation
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (args.length < 2 || (args.length % 2) != 0) {
|
||||
System.err.println("Usage: ProtoBench <descriptor type name> <input data>");
|
||||
System.err.println("The descriptor type name is the fully-qualified message name,");
|
||||
System.err.println("e.g. com.google.protocolbuffers.benchmark.Message1");
|
||||
System.err.println("(You can specify multiple pairs of descriptor type name and input data.)");
|
||||
System.exit(1);
|
||||
}
|
||||
boolean success = true;
|
||||
for (int i = 0; i < args.length; i += 2) {
|
||||
success &= runTest(args[i], args[i + 1]);
|
||||
}
|
||||
System.exit(success ? 0 : 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a single test. Error messages are displayed to stderr, and the return value
|
||||
* indicates general success/failure.
|
||||
*/
|
||||
public static boolean runTest(String type, String file) {
|
||||
System.out.println("Benchmarking " + type + " with file " + file);
|
||||
final Message defaultMessage;
|
||||
try {
|
||||
Class<?> clazz = Class.forName(type);
|
||||
Method method = clazz.getDeclaredMethod("getDefaultInstance");
|
||||
defaultMessage = (Message) method.invoke(null);
|
||||
} catch (Exception e) {
|
||||
// We want to do the same thing with all exceptions. Not generally nice,
|
||||
// but this is slightly different.
|
||||
System.err.println("Unable to get default message for " + type);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
final byte[] inputData = readAllBytes(file);
|
||||
final ByteArrayInputStream inputStream = new ByteArrayInputStream(inputData);
|
||||
final ByteString inputString = ByteString.copyFrom(inputData);
|
||||
final Message sampleMessage = defaultMessage.newBuilderForType().mergeFrom(inputString).build();
|
||||
FileOutputStream devNullTemp = null;
|
||||
CodedOutputStream reuseDevNullTemp = null;
|
||||
try {
|
||||
devNullTemp = new FileOutputStream("/dev/null");
|
||||
reuseDevNullTemp = CodedOutputStream.newInstance(devNullTemp);
|
||||
} catch (FileNotFoundException e) {
|
||||
// ignore: this is probably Windows, where /dev/null does not exist
|
||||
}
|
||||
final FileOutputStream devNull = devNullTemp;
|
||||
final CodedOutputStream reuseDevNull = reuseDevNullTemp;
|
||||
benchmark("Serialize to byte string", inputData.length, new Action() {
|
||||
public void execute() { sampleMessage.toByteString(); }
|
||||
});
|
||||
benchmark("Serialize to byte array", inputData.length, new Action() {
|
||||
public void execute() { sampleMessage.toByteArray(); }
|
||||
});
|
||||
benchmark("Serialize to memory stream", inputData.length, new Action() {
|
||||
public void execute() throws IOException {
|
||||
sampleMessage.writeTo(new ByteArrayOutputStream());
|
||||
}
|
||||
});
|
||||
if (devNull != null) {
|
||||
benchmark("Serialize to /dev/null with FileOutputStream", inputData.length, new Action() {
|
||||
public void execute() throws IOException {
|
||||
sampleMessage.writeTo(devNull);
|
||||
}
|
||||
});
|
||||
benchmark("Serialize to /dev/null reusing FileOutputStream", inputData.length, new Action() {
|
||||
public void execute() throws IOException {
|
||||
sampleMessage.writeTo(reuseDevNull);
|
||||
reuseDevNull.flush(); // force the write to the OutputStream
|
||||
}
|
||||
});
|
||||
}
|
||||
benchmark("Deserialize from byte string", inputData.length, new Action() {
|
||||
public void execute() throws IOException {
|
||||
defaultMessage.newBuilderForType().mergeFrom(inputString).build();
|
||||
}
|
||||
});
|
||||
benchmark("Deserialize from byte array", inputData.length, new Action() {
|
||||
public void execute() throws IOException {
|
||||
defaultMessage.newBuilderForType()
|
||||
.mergeFrom(CodedInputStream.newInstance(inputData)).build();
|
||||
}
|
||||
});
|
||||
benchmark("Deserialize from memory stream", inputData.length, new Action() {
|
||||
public void execute() throws IOException {
|
||||
defaultMessage.newBuilderForType()
|
||||
.mergeFrom(CodedInputStream.newInstance(inputStream)).build();
|
||||
inputStream.reset();
|
||||
}
|
||||
});
|
||||
System.out.println();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error: " + e.getMessage());
|
||||
System.err.println("Detailed exception information:");
|
||||
e.printStackTrace(System.err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void benchmark(String name, long dataSize, Action action) throws IOException {
|
||||
// Make sure it's JITted "reasonably" hard before running the first progress test
|
||||
for (int i=0; i < 100; i++) {
|
||||
action.execute();
|
||||
}
|
||||
|
||||
// Run it progressively more times until we've got a reasonable sample
|
||||
int iterations = 1;
|
||||
long elapsed = timeAction(action, iterations);
|
||||
while (elapsed < MIN_SAMPLE_TIME_MS) {
|
||||
iterations *= 2;
|
||||
elapsed = timeAction(action, iterations);
|
||||
}
|
||||
|
||||
// Upscale the sample to the target time. Do this in floating point arithmetic
|
||||
// to avoid overflow issues.
|
||||
iterations = (int) ((TARGET_TIME_MS / (double) elapsed) * iterations);
|
||||
elapsed = timeAction(action, iterations);
|
||||
System.out.println(name + ": " + iterations + " iterations in "
|
||||
+ (elapsed/1000f) + "s; "
|
||||
+ (iterations * dataSize) / (elapsed * 1024 * 1024 / 1000f)
|
||||
+ "MB/s");
|
||||
}
|
||||
|
||||
private static long timeAction(Action action, int iterations) throws IOException {
|
||||
System.gc();
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
action.execute();
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
return end - start;
|
||||
}
|
||||
|
||||
private static byte[] readAllBytes(String filename) throws IOException {
|
||||
RandomAccessFile file = new RandomAccessFile(new File(filename), "r");
|
||||
byte[] content = new byte[(int) file.length()];
|
||||
file.readFully(content);
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface used to capture a single action to benchmark.
|
||||
*/
|
||||
interface Action {
|
||||
void execute() throws IOException;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,136 +0,0 @@
|
||||
package benchmarks;
|
||||
|
||||
option java_outer_classname = "GoogleSize";
|
||||
option optimize_for = CODE_SIZE;
|
||||
|
||||
message SizeMessage1 {
|
||||
required string field1 = 1;
|
||||
optional string field9 = 9;
|
||||
optional string field18 = 18;
|
||||
optional bool field80 = 80 [default=false];
|
||||
optional bool field81 = 81 [default=true];
|
||||
required int32 field2 = 2;
|
||||
required int32 field3 = 3;
|
||||
optional int32 field280 = 280;
|
||||
optional int32 field6 = 6 [default=0];
|
||||
optional int64 field22 = 22;
|
||||
optional string field4 = 4;
|
||||
repeated fixed64 field5 = 5;
|
||||
optional bool field59 = 59 [default=false];
|
||||
optional string field7 = 7;
|
||||
optional int32 field16 = 16;
|
||||
optional int32 field130 = 130 [default=0];
|
||||
optional bool field12 = 12 [default=true];
|
||||
optional bool field17 = 17 [default=true];
|
||||
optional bool field13 = 13 [default=true];
|
||||
optional bool field14 = 14 [default=true];
|
||||
optional int32 field104 = 104 [default=0];
|
||||
optional int32 field100 = 100 [default=0];
|
||||
optional int32 field101 = 101 [default=0];
|
||||
optional string field102 = 102;
|
||||
optional string field103 = 103;
|
||||
optional int32 field29 = 29 [default=0];
|
||||
optional bool field30 = 30 [default=false];
|
||||
optional int32 field60 = 60 [default=-1];
|
||||
optional int32 field271 = 271 [default=-1];
|
||||
optional int32 field272 = 272 [default=-1];
|
||||
optional int32 field150 = 150;
|
||||
optional int32 field23 = 23 [default=0];
|
||||
optional bool field24 = 24 [default=false];
|
||||
optional int32 field25 = 25 [default=0];
|
||||
optional SizeMessage1SubMessage field15 = 15;
|
||||
optional bool field78 = 78;
|
||||
optional int32 field67 = 67 [default=0];
|
||||
optional int32 field68 = 68;
|
||||
optional int32 field128 = 128 [default=0];
|
||||
optional string field129 = 129 [default="xxxxxxxxxxxxxxxxxxxxx"];
|
||||
optional int32 field131 = 131 [default=0];
|
||||
}
|
||||
|
||||
message SizeMessage1SubMessage {
|
||||
optional int32 field1 = 1 [default=0];
|
||||
optional int32 field2 = 2 [default=0];
|
||||
optional int32 field3 = 3 [default=0];
|
||||
optional string field15 = 15;
|
||||
optional bool field12 = 12 [default=true];
|
||||
optional int64 field13 = 13;
|
||||
optional int64 field14 = 14;
|
||||
optional int32 field16 = 16;
|
||||
optional int32 field19 = 19 [default=2];
|
||||
optional bool field20 = 20 [default=true];
|
||||
optional bool field28 = 28 [default=true];
|
||||
optional fixed64 field21 = 21;
|
||||
optional int32 field22 = 22;
|
||||
optional bool field23 = 23 [ default=false ];
|
||||
optional bool field206 = 206 [default=false];
|
||||
optional fixed32 field203 = 203;
|
||||
optional int32 field204 = 204;
|
||||
optional string field205 = 205;
|
||||
optional uint64 field207 = 207;
|
||||
optional uint64 field300 = 300;
|
||||
}
|
||||
|
||||
message SizeMessage2 {
|
||||
optional string field1 = 1;
|
||||
optional int64 field3 = 3;
|
||||
optional int64 field4 = 4;
|
||||
optional int64 field30 = 30;
|
||||
optional bool field75 = 75 [default=false];
|
||||
optional string field6 = 6;
|
||||
optional bytes field2 = 2;
|
||||
optional int32 field21 = 21 [default=0];
|
||||
optional int32 field71 = 71;
|
||||
optional float field25 = 25;
|
||||
optional int32 field109 = 109 [default=0];
|
||||
optional int32 field210 = 210 [default=0];
|
||||
optional int32 field211 = 211 [default=0];
|
||||
optional int32 field212 = 212 [default=0];
|
||||
optional int32 field213 = 213 [default=0];
|
||||
optional int32 field216 = 216 [default=0];
|
||||
optional int32 field217 = 217 [default=0];
|
||||
optional int32 field218 = 218 [default=0];
|
||||
optional int32 field220 = 220 [default=0];
|
||||
optional int32 field221 = 221 [default=0];
|
||||
optional float field222 = 222 [default=0.0];
|
||||
optional int32 field63 = 63;
|
||||
|
||||
repeated group Group1 = 10 {
|
||||
required float field11 = 11;
|
||||
optional float field26 = 26;
|
||||
optional string field12 = 12;
|
||||
optional string field13 = 13;
|
||||
repeated string field14 = 14;
|
||||
required uint64 field15 = 15;
|
||||
optional int32 field5 = 5;
|
||||
optional string field27 = 27;
|
||||
optional int32 field28 = 28;
|
||||
optional string field29 = 29;
|
||||
optional string field16 = 16;
|
||||
repeated string field22 = 22;
|
||||
repeated int32 field73 = 73;
|
||||
optional int32 field20 = 20 [default=0];
|
||||
optional string field24 = 24;
|
||||
optional SizeMessage2GroupedMessage field31 = 31;
|
||||
}
|
||||
repeated string field128 = 128;
|
||||
optional int64 field131 = 131;
|
||||
repeated string field127 = 127;
|
||||
optional int32 field129 = 129;
|
||||
repeated int64 field130 = 130;
|
||||
optional bool field205 = 205 [default=false];
|
||||
optional bool field206 = 206 [default=false];
|
||||
}
|
||||
|
||||
message SizeMessage2GroupedMessage {
|
||||
optional float field1 = 1;
|
||||
optional float field2 = 2;
|
||||
optional float field3 = 3 [default=0.0];
|
||||
optional bool field4 = 4;
|
||||
optional bool field5 = 5;
|
||||
optional bool field6 = 6 [default=true];
|
||||
optional bool field7 = 7 [default=false];
|
||||
optional float field8 = 8;
|
||||
optional bool field9 = 9;
|
||||
optional float field10 = 10;
|
||||
optional int64 field11 = 11;
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
package benchmarks;
|
||||
|
||||
option java_outer_classname = "GoogleSpeed";
|
||||
option optimize_for = SPEED;
|
||||
|
||||
message SpeedMessage1 {
|
||||
required string field1 = 1;
|
||||
optional string field9 = 9;
|
||||
optional string field18 = 18;
|
||||
optional bool field80 = 80 [default=false];
|
||||
optional bool field81 = 81 [default=true];
|
||||
required int32 field2 = 2;
|
||||
required int32 field3 = 3;
|
||||
optional int32 field280 = 280;
|
||||
optional int32 field6 = 6 [default=0];
|
||||
optional int64 field22 = 22;
|
||||
optional string field4 = 4;
|
||||
repeated fixed64 field5 = 5;
|
||||
optional bool field59 = 59 [default=false];
|
||||
optional string field7 = 7;
|
||||
optional int32 field16 = 16;
|
||||
optional int32 field130 = 130 [default=0];
|
||||
optional bool field12 = 12 [default=true];
|
||||
optional bool field17 = 17 [default=true];
|
||||
optional bool field13 = 13 [default=true];
|
||||
optional bool field14 = 14 [default=true];
|
||||
optional int32 field104 = 104 [default=0];
|
||||
optional int32 field100 = 100 [default=0];
|
||||
optional int32 field101 = 101 [default=0];
|
||||
optional string field102 = 102;
|
||||
optional string field103 = 103;
|
||||
optional int32 field29 = 29 [default=0];
|
||||
optional bool field30 = 30 [default=false];
|
||||
optional int32 field60 = 60 [default=-1];
|
||||
optional int32 field271 = 271 [default=-1];
|
||||
optional int32 field272 = 272 [default=-1];
|
||||
optional int32 field150 = 150;
|
||||
optional int32 field23 = 23 [default=0];
|
||||
optional bool field24 = 24 [default=false];
|
||||
optional int32 field25 = 25 [default=0];
|
||||
optional SpeedMessage1SubMessage field15 = 15;
|
||||
optional bool field78 = 78;
|
||||
optional int32 field67 = 67 [default=0];
|
||||
optional int32 field68 = 68;
|
||||
optional int32 field128 = 128 [default=0];
|
||||
optional string field129 = 129 [default="xxxxxxxxxxxxxxxxxxxxx"];
|
||||
optional int32 field131 = 131 [default=0];
|
||||
}
|
||||
|
||||
message SpeedMessage1SubMessage {
|
||||
optional int32 field1 = 1 [default=0];
|
||||
optional int32 field2 = 2 [default=0];
|
||||
optional int32 field3 = 3 [default=0];
|
||||
optional string field15 = 15;
|
||||
optional bool field12 = 12 [default=true];
|
||||
optional int64 field13 = 13;
|
||||
optional int64 field14 = 14;
|
||||
optional int32 field16 = 16;
|
||||
optional int32 field19 = 19 [default=2];
|
||||
optional bool field20 = 20 [default=true];
|
||||
optional bool field28 = 28 [default=true];
|
||||
optional fixed64 field21 = 21;
|
||||
optional int32 field22 = 22;
|
||||
optional bool field23 = 23 [ default=false ];
|
||||
optional bool field206 = 206 [default=false];
|
||||
optional fixed32 field203 = 203;
|
||||
optional int32 field204 = 204;
|
||||
optional string field205 = 205;
|
||||
optional uint64 field207 = 207;
|
||||
optional uint64 field300 = 300;
|
||||
}
|
||||
|
||||
message SpeedMessage2 {
|
||||
optional string field1 = 1;
|
||||
optional int64 field3 = 3;
|
||||
optional int64 field4 = 4;
|
||||
optional int64 field30 = 30;
|
||||
optional bool field75 = 75 [default=false];
|
||||
optional string field6 = 6;
|
||||
optional bytes field2 = 2;
|
||||
optional int32 field21 = 21 [default=0];
|
||||
optional int32 field71 = 71;
|
||||
optional float field25 = 25;
|
||||
optional int32 field109 = 109 [default=0];
|
||||
optional int32 field210 = 210 [default=0];
|
||||
optional int32 field211 = 211 [default=0];
|
||||
optional int32 field212 = 212 [default=0];
|
||||
optional int32 field213 = 213 [default=0];
|
||||
optional int32 field216 = 216 [default=0];
|
||||
optional int32 field217 = 217 [default=0];
|
||||
optional int32 field218 = 218 [default=0];
|
||||
optional int32 field220 = 220 [default=0];
|
||||
optional int32 field221 = 221 [default=0];
|
||||
optional float field222 = 222 [default=0.0];
|
||||
optional int32 field63 = 63;
|
||||
|
||||
repeated group Group1 = 10 {
|
||||
required float field11 = 11;
|
||||
optional float field26 = 26;
|
||||
optional string field12 = 12;
|
||||
optional string field13 = 13;
|
||||
repeated string field14 = 14;
|
||||
required uint64 field15 = 15;
|
||||
optional int32 field5 = 5;
|
||||
optional string field27 = 27;
|
||||
optional int32 field28 = 28;
|
||||
optional string field29 = 29;
|
||||
optional string field16 = 16;
|
||||
repeated string field22 = 22;
|
||||
repeated int32 field73 = 73;
|
||||
optional int32 field20 = 20 [default=0];
|
||||
optional string field24 = 24;
|
||||
optional SpeedMessage2GroupedMessage field31 = 31;
|
||||
}
|
||||
repeated string field128 = 128;
|
||||
optional int64 field131 = 131;
|
||||
repeated string field127 = 127;
|
||||
optional int32 field129 = 129;
|
||||
repeated int64 field130 = 130;
|
||||
optional bool field205 = 205 [default=false];
|
||||
optional bool field206 = 206 [default=false];
|
||||
}
|
||||
|
||||
message SpeedMessage2GroupedMessage {
|
||||
optional float field1 = 1;
|
||||
optional float field2 = 2;
|
||||
optional float field3 = 3 [default=0.0];
|
||||
optional bool field4 = 4;
|
||||
optional bool field5 = 5;
|
||||
optional bool field6 = 6 [default=true];
|
||||
optional bool field7 = 7 [default=false];
|
||||
optional float field8 = 8;
|
||||
optional bool field9 = 9;
|
||||
optional float field10 = 10;
|
||||
optional int64 field11 = 11;
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
Contents
|
||||
--------
|
||||
|
||||
This folder contains three kinds of file:
|
||||
|
||||
- Code, such as ProtoBench.java, to build the benchmarking framework.
|
||||
- Protocol buffer definitions (.proto files)
|
||||
- Sample data files
|
||||
|
||||
If we end up with a lot of different benchmarks it may be worth
|
||||
separating these out info different directories, but while there are
|
||||
so few they might as well all be together.
|
||||
|
||||
Running a benchmark (Java)
|
||||
--------------------------
|
||||
|
||||
1) Build protoc and the Java protocol buffer library. The examples
|
||||
below assume a jar file (protobuf.jar) has been built and copied
|
||||
into this directory.
|
||||
|
||||
2) Build ProtoBench:
|
||||
$ javac -d tmp -cp protobuf.jar ProtoBench.java
|
||||
|
||||
3) Generate code for the relevant benchmark protocol buffer, e.g.
|
||||
$ protoc --java_out=tmp google_size.proto google_speed.proto
|
||||
|
||||
4) Build the generated code, e.g.
|
||||
$ cd tmp
|
||||
$ javac -d . -cp ../protobuf.jar benchmarks/*.java
|
||||
|
||||
5) Run the test. Arguments are given in pairs - the first argument
|
||||
is the descriptor type; the second is the filename. For example:
|
||||
$ java -cp .;../protobuf.jar com.google.protocolbuffers.ProtoBench
|
||||
benchmarks.GoogleSize$SizeMessage1 ../google_message1.dat
|
||||
benchmarks.GoogleSpeed$SpeedMessage1 ../google_message1.dat
|
||||
benchmarks.GoogleSize$SizeMessage2 ../google_message2.dat
|
||||
benchmarks.GoogleSpeed$SpeedMessage2 ../google_message2.dat
|
||||
|
||||
6) Wait! Each test runs for around 30 seconds, and there are 6 tests
|
||||
per class/data combination. The above command would therefore take
|
||||
about 12 minutes to run.
|
||||
|
||||
|
||||
Benchmarks available
|
||||
--------------------
|
||||
|
||||
From Google:
|
||||
google_size.proto and google_speed.proto, messages
|
||||
google_message1.dat and google_message2.dat. The proto files are
|
||||
equivalent, but optimized differently.
|
||||
@@ -1,150 +0,0 @@
|
||||
## Process this file with autoconf to produce configure.
|
||||
## In general, the safest way to proceed is to run ./autogen.sh
|
||||
|
||||
AC_PREREQ(2.59)
|
||||
|
||||
# Note: If you change the version, you must also update it in:
|
||||
# * java/pom.xml
|
||||
# * python/setup.py
|
||||
# * src/google/protobuf/stubs/common.h
|
||||
# * src/Makefile.am (Update -version-info for LDFLAGS if needed)
|
||||
#
|
||||
# In the SVN trunk, the version should always be the next anticipated release
|
||||
# version with the "-pre" suffix. (We used to use "-SNAPSHOT" but this pushed
|
||||
# the size of one file name in the dist tarfile over the 99-char limit.)
|
||||
AC_INIT([Protocol Buffers],[2.5.1-pre],[protobuf@googlegroups.com],[protobuf])
|
||||
|
||||
AM_MAINTAINER_MODE([enable])
|
||||
|
||||
AC_CONFIG_SRCDIR(src/google/protobuf/message.cc)
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
# autoconf's default CXXFLAGS are usually "-g -O2". These aren't necessarily
|
||||
# the best choice for libprotobuf.
|
||||
AS_IF([test "x${ac_cv_env_CFLAGS_set}" = "x"],
|
||||
[CFLAGS=""])
|
||||
AS_IF([test "x${ac_cv_env_CXXFLAGS_set}" = "x"],
|
||||
[CXXFLAGS=""])
|
||||
|
||||
AC_CANONICAL_TARGET
|
||||
|
||||
AM_INIT_AUTOMAKE
|
||||
|
||||
AC_ARG_WITH([zlib],
|
||||
[AS_HELP_STRING([--with-zlib],
|
||||
[include classes for streaming compressed data in and out @<:@default=check@:>@])],
|
||||
[],[with_zlib=check])
|
||||
|
||||
AC_ARG_WITH([protoc],
|
||||
[AS_HELP_STRING([--with-protoc=COMMAND],
|
||||
[use the given protoc command instead of building a new one when building tests (useful for cross-compiling)])],
|
||||
[],[with_protoc=no])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
AC_LANG([C++])
|
||||
ACX_USE_SYSTEM_EXTENSIONS
|
||||
AM_CONDITIONAL(GCC, test "$GCC" = yes) # let the Makefile know if we're gcc
|
||||
|
||||
# test_util.cc takes forever to compile with GCC and optimization turned on.
|
||||
AC_MSG_CHECKING([C++ compiler flags...])
|
||||
AS_IF([test "x${ac_cv_env_CXXFLAGS_set}" = "x"],[
|
||||
AS_IF([test "$GCC" = "yes"],[
|
||||
PROTOBUF_OPT_FLAG="-O2"
|
||||
CXXFLAGS="${CXXFLAGS} -g"
|
||||
])
|
||||
|
||||
# Protocol Buffers contains several checks that are intended to be used only
|
||||
# for debugging and which might hurt performance. Most users are probably
|
||||
# end users who don't want these checks, so add -DNDEBUG by default.
|
||||
CXXFLAGS="$CXXFLAGS -DNDEBUG"
|
||||
|
||||
AC_MSG_RESULT([use default: $PROTOBUF_OPT_FLAG $CXXFLAGS])
|
||||
],[
|
||||
AC_MSG_RESULT([use user-supplied: $CXXFLAGS])
|
||||
])
|
||||
|
||||
AC_SUBST(PROTOBUF_OPT_FLAG)
|
||||
|
||||
ACX_CHECK_SUNCC
|
||||
|
||||
# Have to do libtool after SUNCC, other wise it "helpfully" adds Crun Cstd
|
||||
# to the link
|
||||
AC_PROG_LIBTOOL
|
||||
|
||||
# Checks for header files.
|
||||
AC_HEADER_STDC
|
||||
AC_CHECK_HEADERS([fcntl.h inttypes.h limits.h stdlib.h unistd.h])
|
||||
|
||||
# Checks for library functions.
|
||||
AC_FUNC_MEMCMP
|
||||
AC_FUNC_STRTOD
|
||||
AC_CHECK_FUNCS([ftruncate memset mkdir strchr strerror strtol])
|
||||
|
||||
# Check for zlib.
|
||||
HAVE_ZLIB=0
|
||||
AS_IF([test "$with_zlib" != no], [
|
||||
AC_MSG_CHECKING([zlib version])
|
||||
|
||||
# First check the zlib header version.
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_PROGRAM([[
|
||||
#include <zlib.h>
|
||||
#if !defined(ZLIB_VERNUM) || (ZLIB_VERNUM < 0x1204)
|
||||
# error zlib version too old
|
||||
#endif
|
||||
]], [])], [
|
||||
AC_MSG_RESULT([ok (1.2.0.4 or later)])
|
||||
|
||||
# Also need to add -lz to the linker flags and make sure this succeeds.
|
||||
AC_SEARCH_LIBS([zlibVersion], [z], [
|
||||
AC_DEFINE([HAVE_ZLIB], [1], [Enable classes using zlib compression.])
|
||||
HAVE_ZLIB=1
|
||||
], [
|
||||
AS_IF([test "$with_zlib" != check], [
|
||||
AC_MSG_FAILURE([--with-zlib was given, but no working zlib library was found])
|
||||
])
|
||||
])
|
||||
], [
|
||||
AS_IF([test "$with_zlib" = check], [
|
||||
AC_MSG_RESULT([headers missing or too old (requires 1.2.0.4)])
|
||||
], [
|
||||
AC_MSG_FAILURE([--with-zlib was given, but zlib headers were not present or were too old (requires 1.2.0.4)])
|
||||
])
|
||||
])
|
||||
])
|
||||
AM_CONDITIONAL([HAVE_ZLIB], [test $HAVE_ZLIB = 1])
|
||||
|
||||
AS_IF([test "$with_protoc" != "no"], [
|
||||
PROTOC=$with_protoc
|
||||
AS_IF([test "$with_protoc" = "yes"], [
|
||||
# No argument given. Use system protoc.
|
||||
PROTOC=protoc
|
||||
])
|
||||
AS_IF([echo "$PROTOC" | grep -q '^@<:@^/@:>@.*/'], [
|
||||
# Does not start with a slash, but contains a slash. So, it's a relative
|
||||
# path (as opposed to an absolute path or an executable in $PATH).
|
||||
# Since it will actually be executed from the src directory, prefix with
|
||||
# the current directory. We also insert $ac_top_build_prefix in case this
|
||||
# is a nested package and --with-protoc was actually given on the outer
|
||||
# package's configure script.
|
||||
PROTOC=`pwd`/${ac_top_build_prefix}$PROTOC
|
||||
])
|
||||
AC_SUBST([PROTOC])
|
||||
])
|
||||
AM_CONDITIONAL([USE_EXTERNAL_PROTOC], [test "$with_protoc" != "no"])
|
||||
|
||||
ACX_PTHREAD
|
||||
AC_CXX_STL_HASH
|
||||
|
||||
# HACK: Make gtest's configure script pick up our copy of CFLAGS and CXXFLAGS,
|
||||
# since the flags added by ACX_CHECK_SUNCC must be used when compiling gtest
|
||||
# too.
|
||||
export CFLAGS
|
||||
export CXXFLAGS
|
||||
AC_CONFIG_SUBDIRS([gtest])
|
||||
|
||||
AC_CONFIG_FILES([Makefile src/Makefile protobuf.pc protobuf-lite.pc])
|
||||
AC_OUTPUT
|
||||
@@ -1,5 +0,0 @@
|
||||
This directory contains syntax highlighting and configuration files for editors
|
||||
to properly display Protocol Buffer files.
|
||||
|
||||
See each file's header comment for directions on how to use it with the
|
||||
appropriate editor.
|
||||
@@ -1,105 +0,0 @@
|
||||
" Protocol Buffers - Google's data interchange format
|
||||
" Copyright 2008 Google Inc. All rights reserved.
|
||||
" http://code.google.com/p/protobuf/
|
||||
"
|
||||
" Redistribution and use in source and binary forms, with or without
|
||||
" modification, are permitted provided that the following conditions are
|
||||
" met:
|
||||
"
|
||||
" * Redistributions of source code must retain the above copyright
|
||||
" notice, this list of conditions and the following disclaimer.
|
||||
" * Redistributions in binary form must reproduce the above
|
||||
" copyright notice, this list of conditions and the following disclaimer
|
||||
" in the documentation and/or other materials provided with the
|
||||
" distribution.
|
||||
" * Neither the name of Google Inc. nor the names of its
|
||||
" contributors may be used to endorse or promote products derived from
|
||||
" this software without specific prior written permission.
|
||||
"
|
||||
" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
" OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
" This is the Vim syntax file for Google Protocol Buffers.
|
||||
"
|
||||
" Usage:
|
||||
"
|
||||
" 1. cp proto.vim ~/.vim/syntax/
|
||||
" 2. Add the following to ~/.vimrc:
|
||||
"
|
||||
" augroup filetype
|
||||
" au! BufRead,BufNewFile *.proto setfiletype proto
|
||||
" augroup end
|
||||
"
|
||||
" Or just create a new file called ~/.vim/ftdetect/proto.vim with the
|
||||
" previous lines on it.
|
||||
|
||||
if version < 600
|
||||
syntax clear
|
||||
elseif exists("b:current_syntax")
|
||||
finish
|
||||
endif
|
||||
|
||||
syn case match
|
||||
|
||||
syn keyword pbTodo contained TODO FIXME XXX
|
||||
syn cluster pbCommentGrp contains=pbTodo
|
||||
|
||||
syn keyword pbSyntax syntax import option
|
||||
syn keyword pbStructure package message group
|
||||
syn keyword pbRepeat optional required repeated
|
||||
syn keyword pbDefault default
|
||||
syn keyword pbExtend extend extensions to max
|
||||
syn keyword pbRPC service rpc returns
|
||||
|
||||
syn keyword pbType int32 int64 uint32 uint64 sint32 sint64
|
||||
syn keyword pbType fixed32 fixed64 sfixed32 sfixed64
|
||||
syn keyword pbType float double bool string bytes
|
||||
syn keyword pbTypedef enum
|
||||
syn keyword pbBool true false
|
||||
|
||||
syn match pbInt /-\?\<\d\+\>/
|
||||
syn match pbInt /\<0[xX]\x+\>/
|
||||
syn match pbFloat /\<-\?\d*\(\.\d*\)\?/
|
||||
syn region pbComment start="\/\*" end="\*\/" contains=@pbCommentGrp
|
||||
syn region pbComment start="//" skip="\\$" end="$" keepend contains=@pbCommentGrp
|
||||
syn region pbString start=/"/ skip=/\\./ end=/"/
|
||||
syn region pbString start=/'/ skip=/\\./ end=/'/
|
||||
|
||||
if version >= 508 || !exists("did_proto_syn_inits")
|
||||
if version < 508
|
||||
let did_proto_syn_inits = 1
|
||||
command -nargs=+ HiLink hi link <args>
|
||||
else
|
||||
command -nargs=+ HiLink hi def link <args>
|
||||
endif
|
||||
|
||||
HiLink pbTodo Todo
|
||||
|
||||
HiLink pbSyntax Include
|
||||
HiLink pbStructure Structure
|
||||
HiLink pbRepeat Repeat
|
||||
HiLink pbDefault Keyword
|
||||
HiLink pbExtend Keyword
|
||||
HiLink pbRPC Keyword
|
||||
HiLink pbType Type
|
||||
HiLink pbTypedef Typedef
|
||||
HiLink pbBool Boolean
|
||||
|
||||
HiLink pbInt Number
|
||||
HiLink pbFloat Float
|
||||
HiLink pbComment Comment
|
||||
HiLink pbString String
|
||||
|
||||
delcommand HiLink
|
||||
endif
|
||||
|
||||
let b:current_syntax = "proto"
|
||||
@@ -1,220 +0,0 @@
|
||||
;;; protobuf-mode.el --- major mode for editing protocol buffers.
|
||||
|
||||
;; Author: Alexandre Vassalotti <alexandre@peadrop.com>
|
||||
;; Created: 23-Apr-2009
|
||||
;; Version: 0.3
|
||||
;; Keywords: google protobuf languages
|
||||
|
||||
;; 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.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; Installation:
|
||||
;; - Put `protobuf-mode.el' in your Emacs load-path.
|
||||
;; - Add this line to your .emacs file:
|
||||
;; (require 'protobuf-mode)
|
||||
;;
|
||||
;; You can customize this mode just like any mode derived from CC Mode. If
|
||||
;; you want to add customizations specific to protobuf-mode, you can use the
|
||||
;; `protobuf-mode-hook'. For example, the following would make protocol-mode
|
||||
;; use 2-space indentation:
|
||||
;;
|
||||
;; (defconst my-protobuf-style
|
||||
;; '((c-basic-offset . 2)
|
||||
;; (indent-tabs-mode . nil)))
|
||||
;;
|
||||
;; (add-hook 'protobuf-mode-hook
|
||||
;; (lambda () (c-add-style "my-style" my-protobuf-style t)))
|
||||
;;
|
||||
;; Refer to the documentation of CC Mode for more information about
|
||||
;; customization details and how to use this mode.
|
||||
;;
|
||||
;; TODO:
|
||||
;; - Make highlighting for enum values work properly.
|
||||
;; - Fix the parser to recognize extensions as identifiers and not
|
||||
;; as casts.
|
||||
;; - Improve the parsing of option assignment lists. For example:
|
||||
;; optional int32 foo = 1 [(my_field_option) = 4.5];
|
||||
;; - Add support for fully-qualified identifiers (e.g., with a leading ".").
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cc-mode)
|
||||
|
||||
(eval-when-compile
|
||||
(require 'cc-langs)
|
||||
(require 'cc-fonts))
|
||||
|
||||
;; This mode does not inherit properties from other modes. So, we do not use
|
||||
;; the usual `c-add-language' function.
|
||||
(eval-and-compile
|
||||
(put 'protobuf-mode 'c-mode-prefix "protobuf-"))
|
||||
|
||||
;; The following code uses of the `c-lang-defconst' macro define syntactic
|
||||
;; features of protocol buffer language. Refer to the documentation in the
|
||||
;; cc-langs.el file for information about the meaning of the -kwds variables.
|
||||
|
||||
(c-lang-defconst c-primitive-type-kwds
|
||||
protobuf '("double" "float" "int32" "int64" "uint32" "uint64" "sint32"
|
||||
"sint64" "fixed32" "fixed64" "sfixed32" "sfixed64" "bool"
|
||||
"string" "bytes" "group"))
|
||||
|
||||
(c-lang-defconst c-modifier-kwds
|
||||
protobuf '("required" "optional" "repeated"))
|
||||
|
||||
(c-lang-defconst c-class-decl-kwds
|
||||
protobuf '("message" "enum" "service"))
|
||||
|
||||
(c-lang-defconst c-constant-kwds
|
||||
protobuf '("true" "false"))
|
||||
|
||||
(c-lang-defconst c-other-decl-kwds
|
||||
protobuf '("package" "import"))
|
||||
|
||||
(c-lang-defconst c-other-kwds
|
||||
protobuf '("default" "max"))
|
||||
|
||||
(c-lang-defconst c-identifier-ops
|
||||
;; Handle extended identifiers like google.protobuf.MessageOptions
|
||||
protobuf '((left-assoc ".")))
|
||||
|
||||
;; The following keywords do not fit well in keyword classes defined by
|
||||
;; cc-mode. So, we approximate as best we can.
|
||||
|
||||
(c-lang-defconst c-type-list-kwds
|
||||
protobuf '("extensions" "to"))
|
||||
|
||||
(c-lang-defconst c-typeless-decl-kwds
|
||||
protobuf '("extend" "rpc" "option" "returns"))
|
||||
|
||||
|
||||
;; Here we remove default syntax for loops, if-statements and other C
|
||||
;; syntactic features that are not supported by the protocol buffer language.
|
||||
|
||||
(c-lang-defconst c-brace-list-decl-kwds
|
||||
;; Remove syntax for C-style enumerations.
|
||||
protobuf nil)
|
||||
|
||||
(c-lang-defconst c-block-stmt-1-kwds
|
||||
;; Remove syntax for "do" and "else" keywords.
|
||||
protobuf nil)
|
||||
|
||||
(c-lang-defconst c-block-stmt-2-kwds
|
||||
;; Remove syntax for "for", "if", "switch" and "while" keywords.
|
||||
protobuf nil)
|
||||
|
||||
(c-lang-defconst c-simple-stmt-kwds
|
||||
;; Remove syntax for "break", "continue", "goto" and "return" keywords.
|
||||
protobuf nil)
|
||||
|
||||
(c-lang-defconst c-paren-stmt-kwds
|
||||
;; Remove special case for the "(;;)" in for-loops.
|
||||
protobuf nil)
|
||||
|
||||
(c-lang-defconst c-label-kwds
|
||||
;; Remove case label syntax for the "case" and "default" keywords.
|
||||
protobuf nil)
|
||||
|
||||
(c-lang-defconst c-before-label-kwds
|
||||
;; Remove special case for the label in a goto statement.
|
||||
protobuf nil)
|
||||
|
||||
(c-lang-defconst c-cpp-matchers
|
||||
;; Disable all the C preprocessor syntax.
|
||||
protobuf nil)
|
||||
|
||||
(c-lang-defconst c-decl-prefix-re
|
||||
;; Same as for C, except it does not match "(". This is needed for disabling
|
||||
;; the syntax for casts.
|
||||
protobuf "\\([\{\};,]+\\)")
|
||||
|
||||
|
||||
;; Add support for variable levels of syntax highlighting.
|
||||
|
||||
(defconst protobuf-font-lock-keywords-1 (c-lang-const c-matchers-1 protobuf)
|
||||
"Minimal highlighting for protobuf-mode.")
|
||||
|
||||
(defconst protobuf-font-lock-keywords-2 (c-lang-const c-matchers-2 protobuf)
|
||||
"Fast normal highlighting for protobuf-mode.")
|
||||
|
||||
(defconst protobuf-font-lock-keywords-3 (c-lang-const c-matchers-3 protobuf)
|
||||
"Accurate normal highlighting for protobuf-mode.")
|
||||
|
||||
(defvar protobuf-font-lock-keywords protobuf-font-lock-keywords-3
|
||||
"Default expressions to highlight in protobuf-mode.")
|
||||
|
||||
;; Our syntax table is auto-generated from the keyword classes we defined
|
||||
;; previously with the `c-lang-const' macro.
|
||||
(defvar protobuf-mode-syntax-table nil
|
||||
"Syntax table used in protobuf-mode buffers.")
|
||||
(or protobuf-mode-syntax-table
|
||||
(setq protobuf-mode-syntax-table
|
||||
(funcall (c-lang-const c-make-mode-syntax-table protobuf))))
|
||||
|
||||
(defvar protobuf-mode-abbrev-table nil
|
||||
"Abbreviation table used in protobuf-mode buffers.")
|
||||
|
||||
(defvar protobuf-mode-map nil
|
||||
"Keymap used in protobuf-mode buffers.")
|
||||
(or protobuf-mode-map
|
||||
(setq protobuf-mode-map (c-make-inherited-keymap)))
|
||||
|
||||
(easy-menu-define protobuf-menu protobuf-mode-map
|
||||
"Protocol Buffers Mode Commands"
|
||||
(cons "Protocol Buffers" (c-lang-const c-mode-menu protobuf)))
|
||||
|
||||
;;;###autoload (add-to-list 'auto-mode-alist '("\\.proto\\'" . protobuf-mode))
|
||||
|
||||
;;;###autoload
|
||||
(defun protobuf-mode ()
|
||||
"Major mode for editing Protocol Buffers description language.
|
||||
|
||||
The hook `c-mode-common-hook' is run with no argument at mode
|
||||
initialization, then `protobuf-mode-hook'.
|
||||
|
||||
Key bindings:
|
||||
\\{protobuf-mode-map}"
|
||||
(interactive)
|
||||
(kill-all-local-variables)
|
||||
(set-syntax-table protobuf-mode-syntax-table)
|
||||
(setq major-mode 'protobuf-mode
|
||||
mode-name "Protocol-Buffers"
|
||||
local-abbrev-table protobuf-mode-abbrev-table
|
||||
abbrev-mode t)
|
||||
(use-local-map protobuf-mode-map)
|
||||
(c-initialize-cc-mode t)
|
||||
(if (fboundp 'c-make-emacs-variables-local)
|
||||
(c-make-emacs-variables-local))
|
||||
(c-init-language-vars protobuf-mode)
|
||||
(c-common-init 'protobuf-mode)
|
||||
(easy-menu-add protobuf-menu)
|
||||
(c-run-mode-hooks 'c-mode-common-hook 'protobuf-mode-hook)
|
||||
(c-update-modeline))
|
||||
|
||||
(provide 'protobuf-mode)
|
||||
|
||||
;;; protobuf-mode.el ends here
|
||||
@@ -1,95 +0,0 @@
|
||||
// See README.txt for information and build instructions.
|
||||
|
||||
import com.example.tutorial.AddressBookProtos.AddressBook;
|
||||
import com.example.tutorial.AddressBookProtos.Person;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
|
||||
class AddPerson {
|
||||
// This function fills in a Person message based on user input.
|
||||
static Person PromptForAddress(BufferedReader stdin,
|
||||
PrintStream stdout) throws IOException {
|
||||
Person.Builder person = Person.newBuilder();
|
||||
|
||||
stdout.print("Enter person ID: ");
|
||||
person.setId(Integer.valueOf(stdin.readLine()));
|
||||
|
||||
stdout.print("Enter name: ");
|
||||
person.setName(stdin.readLine());
|
||||
|
||||
stdout.print("Enter email address (blank for none): ");
|
||||
String email = stdin.readLine();
|
||||
if (email.length() > 0) {
|
||||
person.setEmail(email);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
stdout.print("Enter a phone number (or leave blank to finish): ");
|
||||
String number = stdin.readLine();
|
||||
if (number.length() == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
Person.PhoneNumber.Builder phoneNumber =
|
||||
Person.PhoneNumber.newBuilder().setNumber(number);
|
||||
|
||||
stdout.print("Is this a mobile, home, or work phone? ");
|
||||
String type = stdin.readLine();
|
||||
if (type.equals("mobile")) {
|
||||
phoneNumber.setType(Person.PhoneType.MOBILE);
|
||||
} else if (type.equals("home")) {
|
||||
phoneNumber.setType(Person.PhoneType.HOME);
|
||||
} else if (type.equals("work")) {
|
||||
phoneNumber.setType(Person.PhoneType.WORK);
|
||||
} else {
|
||||
stdout.println("Unknown phone type. Using default.");
|
||||
}
|
||||
|
||||
person.addPhone(phoneNumber);
|
||||
}
|
||||
|
||||
return person.build();
|
||||
}
|
||||
|
||||
// Main function: Reads the entire address book from a file,
|
||||
// adds one person based on user input, then writes it back out to the same
|
||||
// file.
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length != 1) {
|
||||
System.err.println("Usage: AddPerson ADDRESS_BOOK_FILE");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
AddressBook.Builder addressBook = AddressBook.newBuilder();
|
||||
|
||||
// Read the existing address book.
|
||||
try {
|
||||
FileInputStream input = new FileInputStream(args[0]);
|
||||
try {
|
||||
addressBook.mergeFrom(input);
|
||||
} finally {
|
||||
try { input.close(); } catch (Throwable ignore) {}
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
System.out.println(args[0] + ": File not found. Creating a new file.");
|
||||
}
|
||||
|
||||
// Add an address.
|
||||
addressBook.addPerson(
|
||||
PromptForAddress(new BufferedReader(new InputStreamReader(System.in)),
|
||||
System.out));
|
||||
|
||||
// Write the new address book back to disk.
|
||||
FileOutputStream output = new FileOutputStream(args[0]);
|
||||
try {
|
||||
addressBook.build().writeTo(output);
|
||||
} finally {
|
||||
output.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
// See README.txt for information and build instructions.
|
||||
|
||||
import com.example.tutorial.AddressBookProtos.AddressBook;
|
||||
import com.example.tutorial.AddressBookProtos.Person;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
|
||||
class ListPeople {
|
||||
// Iterates though all people in the AddressBook and prints info about them.
|
||||
static void Print(AddressBook addressBook) {
|
||||
for (Person person: addressBook.getPersonList()) {
|
||||
System.out.println("Person ID: " + person.getId());
|
||||
System.out.println(" Name: " + person.getName());
|
||||
if (person.hasEmail()) {
|
||||
System.out.println(" E-mail address: " + person.getEmail());
|
||||
}
|
||||
|
||||
for (Person.PhoneNumber phoneNumber : person.getPhoneList()) {
|
||||
switch (phoneNumber.getType()) {
|
||||
case MOBILE:
|
||||
System.out.print(" Mobile phone #: ");
|
||||
break;
|
||||
case HOME:
|
||||
System.out.print(" Home phone #: ");
|
||||
break;
|
||||
case WORK:
|
||||
System.out.print(" Work phone #: ");
|
||||
break;
|
||||
}
|
||||
System.out.println(phoneNumber.getNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Main function: Reads the entire address book from a file and prints all
|
||||
// the information inside.
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length != 1) {
|
||||
System.err.println("Usage: ListPeople ADDRESS_BOOK_FILE");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
// Read the existing address book.
|
||||
AddressBook addressBook =
|
||||
AddressBook.parseFrom(new FileInputStream(args[0]));
|
||||
|
||||
Print(addressBook);
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
# See README.txt.
|
||||
|
||||
.PHONY: all cpp java python clean
|
||||
|
||||
all: cpp java python
|
||||
|
||||
cpp: add_person_cpp list_people_cpp
|
||||
java: add_person_java list_people_java
|
||||
python: add_person_python list_people_python
|
||||
|
||||
clean:
|
||||
rm -f add_person_cpp list_people_cpp add_person_java list_people_java add_person_python list_people_python
|
||||
rm -f javac_middleman AddPerson*.class ListPeople*.class com/example/tutorial/*.class
|
||||
rm -f protoc_middleman addressbook.pb.cc addressbook.pb.h addressbook_pb2.py com/example/tutorial/AddressBookProtos.java
|
||||
rm -f *.pyc
|
||||
rmdir com/example/tutorial 2>/dev/null || true
|
||||
rmdir com/example 2>/dev/null || true
|
||||
rmdir com 2>/dev/null || true
|
||||
|
||||
protoc_middleman: addressbook.proto
|
||||
protoc --cpp_out=. --java_out=. --python_out=. addressbook.proto
|
||||
@touch protoc_middleman
|
||||
|
||||
add_person_cpp: add_person.cc protoc_middleman
|
||||
pkg-config --cflags protobuf # fails if protobuf is not installed
|
||||
c++ add_person.cc addressbook.pb.cc -o add_person_cpp `pkg-config --cflags --libs protobuf`
|
||||
|
||||
list_people_cpp: list_people.cc protoc_middleman
|
||||
pkg-config --cflags protobuf # fails if protobuf is not installed
|
||||
c++ list_people.cc addressbook.pb.cc -o list_people_cpp `pkg-config --cflags --libs protobuf`
|
||||
|
||||
javac_middleman: AddPerson.java ListPeople.java protoc_middleman
|
||||
javac AddPerson.java ListPeople.java com/example/tutorial/AddressBookProtos.java
|
||||
@touch javac_middleman
|
||||
|
||||
add_person_java: javac_middleman
|
||||
@echo "Writing shortcut script add_person_java..."
|
||||
@echo '#! /bin/sh' > add_person_java
|
||||
@echo 'java -classpath .:$$CLASSPATH AddPerson "$$@"' >> add_person_java
|
||||
@chmod +x add_person_java
|
||||
|
||||
list_people_java: javac_middleman
|
||||
@echo "Writing shortcut script list_people_java..."
|
||||
@echo '#! /bin/sh' > list_people_java
|
||||
@echo 'java -classpath .:$$CLASSPATH ListPeople "$$@"' >> list_people_java
|
||||
@chmod +x list_people_java
|
||||
|
||||
add_person_python: add_person.py protoc_middleman
|
||||
@echo "Writing shortcut script add_person_python..."
|
||||
@echo '#! /bin/sh' > add_person_python
|
||||
@echo './add_person.py "$$@"' >> add_person_python
|
||||
@chmod +x add_person_python
|
||||
|
||||
list_people_python: list_people.py protoc_middleman
|
||||
@echo "Writing shortcut script list_people_python..."
|
||||
@echo '#! /bin/sh' > list_people_python
|
||||
@echo './list_people.py "$$@"' >> list_people_python
|
||||
@chmod +x list_people_python
|
||||
@@ -1,29 +0,0 @@
|
||||
This directory contains example code that uses Protocol Buffers to manage an
|
||||
address book. Two programs are provided, each with three different
|
||||
implementations, one written in each of C++, Java, and Python. The add_person
|
||||
example adds a new person to an address book, prompting the user to input
|
||||
the person's information. The list_people example lists people already in the
|
||||
address book. The examples use the exact same format in all three languages,
|
||||
so you can, for example, use add_person_java to create an address book and then
|
||||
use list_people_python to read it.
|
||||
|
||||
You must install the protobuf package before you can build these.
|
||||
|
||||
To build all the examples (on a unix-like system), simply run "make". This
|
||||
creates the following executable files in the current directory:
|
||||
add_person_cpp list_people_cpp
|
||||
add_person_java list_people_java
|
||||
add_person_python list_people_python
|
||||
|
||||
If you only want to compile examples in one language, use "make cpp"*,
|
||||
"make java", or "make python".
|
||||
|
||||
All of these programs simply take an address book file as their parameter.
|
||||
The add_person programs will create the file if it doesn't already exist.
|
||||
|
||||
These examples are part of the Protocol Buffers tutorial, located at:
|
||||
http://code.google.com/apis/protocolbuffers/docs/tutorials.html
|
||||
|
||||
* Note that on some platforms you may have to edit the Makefile and remove
|
||||
"-lpthread" from the linker commands (perhaps replacing it with something else).
|
||||
We didn't do this automatically because we wanted to keep the example simple.
|
||||
@@ -1,95 +0,0 @@
|
||||
// See README.txt for information and build instructions.
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include "addressbook.pb.h"
|
||||
using namespace std;
|
||||
|
||||
// This function fills in a Person message based on user input.
|
||||
void PromptForAddress(tutorial::Person* person) {
|
||||
cout << "Enter person ID number: ";
|
||||
int id;
|
||||
cin >> id;
|
||||
person->set_id(id);
|
||||
cin.ignore(256, '\n');
|
||||
|
||||
cout << "Enter name: ";
|
||||
getline(cin, *person->mutable_name());
|
||||
|
||||
cout << "Enter email address (blank for none): ";
|
||||
string email;
|
||||
getline(cin, email);
|
||||
if (!email.empty()) {
|
||||
person->set_email(email);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
cout << "Enter a phone number (or leave blank to finish): ";
|
||||
string number;
|
||||
getline(cin, number);
|
||||
if (number.empty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
tutorial::Person::PhoneNumber* phone_number = person->add_phone();
|
||||
phone_number->set_number(number);
|
||||
|
||||
cout << "Is this a mobile, home, or work phone? ";
|
||||
string type;
|
||||
getline(cin, type);
|
||||
if (type == "mobile") {
|
||||
phone_number->set_type(tutorial::Person::MOBILE);
|
||||
} else if (type == "home") {
|
||||
phone_number->set_type(tutorial::Person::HOME);
|
||||
} else if (type == "work") {
|
||||
phone_number->set_type(tutorial::Person::WORK);
|
||||
} else {
|
||||
cout << "Unknown phone type. Using default." << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Main function: Reads the entire address book from a file,
|
||||
// adds one person based on user input, then writes it back out to the same
|
||||
// file.
|
||||
int main(int argc, char* argv[]) {
|
||||
// Verify that the version of the library that we linked against is
|
||||
// compatible with the version of the headers we compiled against.
|
||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
|
||||
if (argc != 2) {
|
||||
cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
tutorial::AddressBook address_book;
|
||||
|
||||
{
|
||||
// Read the existing address book.
|
||||
fstream input(argv[1], ios::in | ios::binary);
|
||||
if (!input) {
|
||||
cout << argv[1] << ": File not found. Creating a new file." << endl;
|
||||
} else if (!address_book.ParseFromIstream(&input)) {
|
||||
cerr << "Failed to parse address book." << endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Add an address.
|
||||
PromptForAddress(address_book.add_person());
|
||||
|
||||
{
|
||||
// Write the new address book back to disk.
|
||||
fstream output(argv[1], ios::out | ios::trunc | ios::binary);
|
||||
if (!address_book.SerializeToOstream(&output)) {
|
||||
cerr << "Failed to write address book." << endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Optional: Delete all global objects allocated by libprotobuf.
|
||||
google::protobuf::ShutdownProtobufLibrary();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
#! /usr/bin/python
|
||||
|
||||
# See README.txt for information and build instructions.
|
||||
|
||||
import addressbook_pb2
|
||||
import sys
|
||||
|
||||
# This function fills in a Person message based on user input.
|
||||
def PromptForAddress(person):
|
||||
person.id = int(raw_input("Enter person ID number: "))
|
||||
person.name = raw_input("Enter name: ")
|
||||
|
||||
email = raw_input("Enter email address (blank for none): ")
|
||||
if email != "":
|
||||
person.email = email
|
||||
|
||||
while True:
|
||||
number = raw_input("Enter a phone number (or leave blank to finish): ")
|
||||
if number == "":
|
||||
break
|
||||
|
||||
phone_number = person.phone.add()
|
||||
phone_number.number = number
|
||||
|
||||
type = raw_input("Is this a mobile, home, or work phone? ")
|
||||
if type == "mobile":
|
||||
phone_number.type = addressbook_pb2.Person.MOBILE
|
||||
elif type == "home":
|
||||
phone_number.type = addressbook_pb2.Person.HOME
|
||||
elif type == "work":
|
||||
phone_number.type = addressbook_pb2.Person.WORK
|
||||
else:
|
||||
print "Unknown phone type; leaving as default value."
|
||||
|
||||
# Main procedure: Reads the entire address book from a file,
|
||||
# adds one person based on user input, then writes it back out to the same
|
||||
# file.
|
||||
if len(sys.argv) != 2:
|
||||
print "Usage:", sys.argv[0], "ADDRESS_BOOK_FILE"
|
||||
sys.exit(-1)
|
||||
|
||||
address_book = addressbook_pb2.AddressBook()
|
||||
|
||||
# Read the existing address book.
|
||||
try:
|
||||
f = open(sys.argv[1], "rb")
|
||||
address_book.ParseFromString(f.read())
|
||||
f.close()
|
||||
except IOError:
|
||||
print sys.argv[1] + ": File not found. Creating a new file."
|
||||
|
||||
# Add an address.
|
||||
PromptForAddress(address_book.person.add())
|
||||
|
||||
# Write the new address book back to disk.
|
||||
f = open(sys.argv[1], "wb")
|
||||
f.write(address_book.SerializeToString())
|
||||
f.close()
|
||||
@@ -1,30 +0,0 @@
|
||||
// See README.txt for information and build instructions.
|
||||
|
||||
package tutorial;
|
||||
|
||||
option java_package = "com.example.tutorial";
|
||||
option java_outer_classname = "AddressBookProtos";
|
||||
|
||||
message Person {
|
||||
required string name = 1;
|
||||
required int32 id = 2; // Unique ID number for this person.
|
||||
optional string email = 3;
|
||||
|
||||
enum PhoneType {
|
||||
MOBILE = 0;
|
||||
HOME = 1;
|
||||
WORK = 2;
|
||||
}
|
||||
|
||||
message PhoneNumber {
|
||||
required string number = 1;
|
||||
optional PhoneType type = 2 [default = HOME];
|
||||
}
|
||||
|
||||
repeated PhoneNumber phone = 4;
|
||||
}
|
||||
|
||||
// Our address book file is just one of these.
|
||||
message AddressBook {
|
||||
repeated Person person = 1;
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
// See README.txt for information and build instructions.
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include "addressbook.pb.h"
|
||||
using namespace std;
|
||||
|
||||
// Iterates though all people in the AddressBook and prints info about them.
|
||||
void ListPeople(const tutorial::AddressBook& address_book) {
|
||||
for (int i = 0; i < address_book.person_size(); i++) {
|
||||
const tutorial::Person& person = address_book.person(i);
|
||||
|
||||
cout << "Person ID: " << person.id() << endl;
|
||||
cout << " Name: " << person.name() << endl;
|
||||
if (person.has_email()) {
|
||||
cout << " E-mail address: " << person.email() << endl;
|
||||
}
|
||||
|
||||
for (int j = 0; j < person.phone_size(); j++) {
|
||||
const tutorial::Person::PhoneNumber& phone_number = person.phone(j);
|
||||
|
||||
switch (phone_number.type()) {
|
||||
case tutorial::Person::MOBILE:
|
||||
cout << " Mobile phone #: ";
|
||||
break;
|
||||
case tutorial::Person::HOME:
|
||||
cout << " Home phone #: ";
|
||||
break;
|
||||
case tutorial::Person::WORK:
|
||||
cout << " Work phone #: ";
|
||||
break;
|
||||
}
|
||||
cout << phone_number.number() << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Main function: Reads the entire address book from a file and prints all
|
||||
// the information inside.
|
||||
int main(int argc, char* argv[]) {
|
||||
// Verify that the version of the library that we linked against is
|
||||
// compatible with the version of the headers we compiled against.
|
||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
|
||||
if (argc != 2) {
|
||||
cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
tutorial::AddressBook address_book;
|
||||
|
||||
{
|
||||
// Read the existing address book.
|
||||
fstream input(argv[1], ios::in | ios::binary);
|
||||
if (!address_book.ParseFromIstream(&input)) {
|
||||
cerr << "Failed to parse address book." << endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
ListPeople(address_book);
|
||||
|
||||
// Optional: Delete all global objects allocated by libprotobuf.
|
||||
google::protobuf::ShutdownProtobufLibrary();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
#! /usr/bin/python
|
||||
|
||||
# See README.txt for information and build instructions.
|
||||
|
||||
import addressbook_pb2
|
||||
import sys
|
||||
|
||||
# Iterates though all people in the AddressBook and prints info about them.
|
||||
def ListPeople(address_book):
|
||||
for person in address_book.person:
|
||||
print "Person ID:", person.id
|
||||
print " Name:", person.name
|
||||
if person.HasField('email'):
|
||||
print " E-mail address:", person.email
|
||||
|
||||
for phone_number in person.phone:
|
||||
if phone_number.type == addressbook_pb2.Person.MOBILE:
|
||||
print " Mobile phone #:",
|
||||
elif phone_number.type == addressbook_pb2.Person.HOME:
|
||||
print " Home phone #:",
|
||||
elif phone_number.type == addressbook_pb2.Person.WORK:
|
||||
print " Work phone #:",
|
||||
print phone_number.number
|
||||
|
||||
# Main procedure: Reads the entire address book from a file and prints all
|
||||
# the information inside.
|
||||
if len(sys.argv) != 2:
|
||||
print "Usage:", sys.argv[0], "ADDRESS_BOOK_FILE"
|
||||
sys.exit(-1)
|
||||
|
||||
address_book = addressbook_pb2.AddressBook()
|
||||
|
||||
# Read the existing address book.
|
||||
f = open(sys.argv[1], "rb")
|
||||
address_book.ParseFromString(f.read())
|
||||
f.close()
|
||||
|
||||
ListPeople(address_book)
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Run this script to regenerate descriptor.pb.{h,cc} after the protocol
|
||||
# compiler changes. Since these files are compiled into the protocol compiler
|
||||
# itself, they cannot be generated automatically by a make rule. "make check"
|
||||
# will fail if these files do not match what the protocol compiler would
|
||||
# generate.
|
||||
#
|
||||
# HINT: Flags passed to generate_descriptor_proto.sh will be passed directly
|
||||
# to make when building protoc. This is particularly useful for passing
|
||||
# -j4 to run 4 jobs simultaneously.
|
||||
|
||||
if test ! -e src/google/protobuf/stubs/common.h; then
|
||||
cat >&2 << __EOF__
|
||||
Could not find source code. Make sure you are running this script from the
|
||||
root of the distribution tree.
|
||||
__EOF__
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test ! -e src/Makefile; then
|
||||
cat >&2 << __EOF__
|
||||
Could not find src/Makefile. You must run ./configure (and perhaps
|
||||
./autogen.sh) first.
|
||||
__EOF__
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd src
|
||||
make $@ protoc &&
|
||||
./protoc --cpp_out=dllexport_decl=LIBPROTOBUF_EXPORT:. google/protobuf/descriptor.proto && \
|
||||
./protoc --cpp_out=dllexport_decl=LIBPROTOC_EXPORT:. google/protobuf/compiler/plugin.proto
|
||||
cd ..
|
||||
@@ -1,96 +0,0 @@
|
||||
Protocol Buffers - Google's data interchange format
|
||||
Copyright 2008 Google Inc.
|
||||
|
||||
This directory contains the Java Protocol Buffers runtime library.
|
||||
|
||||
Installation - With Maven
|
||||
=========================
|
||||
|
||||
The Protocol Buffers build is managed using Maven. If you would
|
||||
rather build without Maven, see below.
|
||||
|
||||
1) Install Apache Maven if you don't have it:
|
||||
|
||||
http://maven.apache.org/
|
||||
|
||||
2) Build the C++ code, or obtain a binary distribution of protoc. If
|
||||
you install a binary distribution, make sure that it is the same
|
||||
version as this package. If in doubt, run:
|
||||
|
||||
$ protoc --version
|
||||
|
||||
You will need to place the protoc executable in ../src. (If you
|
||||
built it yourself, it should already be there.)
|
||||
|
||||
3) Run the tests:
|
||||
|
||||
$ mvn test
|
||||
|
||||
If some tests fail, this library may not work correctly on your
|
||||
system. Continue at your own risk.
|
||||
|
||||
4) Install the library into your Maven repository:
|
||||
|
||||
$ mvn install
|
||||
|
||||
5) If you do not use Maven to manage your own build, you can build a
|
||||
.jar file to use:
|
||||
|
||||
$ mvn package
|
||||
|
||||
The .jar will be placed in the "target" directory.
|
||||
|
||||
Installation - 'Lite' Version - With Maven
|
||||
==========================================
|
||||
|
||||
Building the 'lite' version of the Java Protocol Buffers library is
|
||||
the same as building the full version, except that all commands are
|
||||
run using the 'lite' profile. (see
|
||||
http://maven.apache.org/guides/introduction/introduction-to-profiles.html)
|
||||
|
||||
E.g. to install the lite version of the jar, you would run:
|
||||
|
||||
$ mvn install -P lite
|
||||
|
||||
The resulting artifact has the 'lite' classifier. To reference it
|
||||
for dependency resolution, you would specify it as:
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
<version>${version}</version>
|
||||
<classifier>lite</classifier>
|
||||
</dependency>
|
||||
|
||||
Installation - Without Maven
|
||||
============================
|
||||
|
||||
If you would rather not install Maven to build the library, you may
|
||||
follow these instructions instead. Note that these instructions skip
|
||||
running unit tests.
|
||||
|
||||
1) Build the C++ code, or obtain a binary distribution of protoc. If
|
||||
you install a binary distribution, make sure that it is the same
|
||||
version as this package. If in doubt, run:
|
||||
|
||||
$ protoc --version
|
||||
|
||||
If you built the C++ code without installing, the compiler binary
|
||||
should be located in ../src.
|
||||
|
||||
2) Invoke protoc to build DescriptorProtos.java:
|
||||
|
||||
$ protoc --java_out=src/main/java -I../src \
|
||||
../src/google/protobuf/descriptor.proto
|
||||
|
||||
3) Compile the code in src/main/java using whatever means you prefer.
|
||||
|
||||
4) Install the classes wherever you prefer.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
The complete documentation for Protocol Buffers is available via the
|
||||
web at:
|
||||
|
||||
http://code.google.com/apis/protocolbuffers/
|
||||
@@ -1,207 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.google</groupId>
|
||||
<artifactId>google</artifactId>
|
||||
<version>1</version>
|
||||
</parent>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
<version>2.5.1-pre</version>
|
||||
<packaging>bundle</packaging>
|
||||
<name>Protocol Buffer Java API</name>
|
||||
<description>
|
||||
Protocol Buffers are a way of encoding structured data in an efficient yet
|
||||
extensible format.
|
||||
</description>
|
||||
<inceptionYear>2008</inceptionYear>
|
||||
<url>http://code.google.com/p/protobuf</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>New BSD license</name>
|
||||
<url>http://www.opensource.org/licenses/bsd-license.php</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
<scm>
|
||||
<url>http://code.google.com/p/protobuf/source/browse</url>
|
||||
<connection>
|
||||
scm:svn:http://protobuf.googlecode.com/svn/trunk/
|
||||
</connection>
|
||||
</scm>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.easymock</groupId>
|
||||
<artifactId>easymock</artifactId>
|
||||
<version>2.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.easymock</groupId>
|
||||
<artifactId>easymockclassextension</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.5</source>
|
||||
<target>1.5</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/*Test.java</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>generate-sources</id>
|
||||
<phase>generate-sources</phase>
|
||||
<configuration>
|
||||
<tasks>
|
||||
<mkdir dir="target/generated-sources" />
|
||||
<exec executable="../src/protoc">
|
||||
<arg value="--java_out=target/generated-sources" />
|
||||
<arg value="--proto_path=../src" />
|
||||
<arg value="../src/google/protobuf/descriptor.proto" />
|
||||
</exec>
|
||||
</tasks>
|
||||
<sourceRoot>target/generated-sources</sourceRoot>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>generate-test-sources</id>
|
||||
<phase>generate-test-sources</phase>
|
||||
<configuration>
|
||||
<tasks>
|
||||
<mkdir dir="target/generated-test-sources" />
|
||||
<exec executable="../src/protoc">
|
||||
<arg value="--java_out=target/generated-test-sources" />
|
||||
<arg value="--proto_path=../src" />
|
||||
<arg value="--proto_path=src/test/java" />
|
||||
<arg value="../src/google/protobuf/unittest.proto" />
|
||||
<arg value="../src/google/protobuf/unittest_import.proto" />
|
||||
<arg value="../src/google/protobuf/unittest_import_public.proto" />
|
||||
<arg value="../src/google/protobuf/unittest_mset.proto" />
|
||||
<arg
|
||||
value="src/test/java/com/google/protobuf/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
|
||||
value="../src/google/protobuf/unittest_custom_options.proto" />
|
||||
<arg value="../src/google/protobuf/unittest_lite.proto" />
|
||||
<arg value="../src/google/protobuf/unittest_import_lite.proto" />
|
||||
<arg value="../src/google/protobuf/unittest_import_public_lite.proto" />
|
||||
<arg value="../src/google/protobuf/unittest_lite_imports_nonlite.proto" />
|
||||
<arg value="../src/google/protobuf/unittest_enormous_descriptor.proto" />
|
||||
<arg value="../src/google/protobuf/unittest_no_generic_services.proto" />
|
||||
</exec>
|
||||
</tasks>
|
||||
<testSourceRoot>target/generated-test-sources</testSourceRoot>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Bundle-DocURL>http://code.google.com/p/protobuf</Bundle-DocURL>
|
||||
<Bundle-SymbolicName>com.google.protobuf</Bundle-SymbolicName>
|
||||
<Export-Package>com.google.protobuf;version=2.5.0</Export-Package>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>lite</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/AbstractMessageLite.java</include>
|
||||
<include>**/ByteString.java</include>
|
||||
<include>**/CodedInputStream.java</include>
|
||||
<include>**/CodedOutputStream.java</include>
|
||||
<include>**/ExtensionRegistryLite.java</include>
|
||||
<include>**/FieldSet.java</include>
|
||||
<include>**/GeneratedMessageLite.java</include>
|
||||
<include>**/Internal.java</include>
|
||||
<include>**/InvalidProtocolBufferException.java</include>
|
||||
<include>**/LazyStringArrayList.java</include>
|
||||
<include>**/LazyStringList.java</include>
|
||||
<include>**/MessageLite.java</include>
|
||||
<include>**/MessageLiteOrBuilder.java</include>
|
||||
<include>**/SmallSortedMap.java</include>
|
||||
<include>**/UninitializedMessageException.java</include>
|
||||
<include>**/UnmodifiableLazyStringList.java</include>
|
||||
<include>**/WireFormat.java</include>
|
||||
<include>**/Parser.java</include>
|
||||
<include>**/AbstractParser.java</include>
|
||||
<include>**/BoundedByteString.java</include>
|
||||
<include>**/LiteralByteString.java</include>
|
||||
<include>**/RopeByteString.java</include>
|
||||
<include>**/Utf8.java</include>
|
||||
<include>**/LazyField.java</include>
|
||||
</includes>
|
||||
<testIncludes>
|
||||
<testInclude>**/LiteTest.java</testInclude>
|
||||
<testInclude>**/*Lite.java</testInclude>
|
||||
</testIncludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/LiteTest.java</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<classifier>lite</classifier>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
@@ -1,930 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.Descriptors.Descriptor;
|
||||
import com.google.protobuf.Descriptors.FieldDescriptor;
|
||||
import com.google.protobuf.GeneratedMessage.ExtendableBuilder;
|
||||
import com.google.protobuf.Internal.EnumLite;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A partial implementation of the {@link Message} interface which implements
|
||||
* as many methods of that interface as possible in terms of other methods.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public abstract class AbstractMessage extends AbstractMessageLite
|
||||
implements Message {
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean isInitialized() {
|
||||
// Check that all required fields are present.
|
||||
for (final FieldDescriptor field : getDescriptorForType().getFields()) {
|
||||
if (field.isRequired()) {
|
||||
if (!hasField(field)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that embedded messages are initialized.
|
||||
for (final Map.Entry<FieldDescriptor, Object> entry :
|
||||
getAllFields().entrySet()) {
|
||||
final FieldDescriptor field = entry.getKey();
|
||||
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
if (field.isRepeated()) {
|
||||
for (final Message element : (List<Message>) entry.getValue()) {
|
||||
if (!element.isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!((Message) entry.getValue()).isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<String> findInitializationErrors() {
|
||||
return Builder.findMissingFields(this);
|
||||
}
|
||||
|
||||
public String getInitializationErrorString() {
|
||||
return delimitWithCommas(findInitializationErrors());
|
||||
}
|
||||
|
||||
private static String delimitWithCommas(List<String> parts) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (String part : parts) {
|
||||
if (result.length() > 0) {
|
||||
result.append(", ");
|
||||
}
|
||||
result.append(part);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return TextFormat.printToString(this);
|
||||
}
|
||||
|
||||
public void writeTo(final CodedOutputStream output) throws IOException {
|
||||
final boolean isMessageSet =
|
||||
getDescriptorForType().getOptions().getMessageSetWireFormat();
|
||||
|
||||
for (final Map.Entry<FieldDescriptor, Object> entry :
|
||||
getAllFields().entrySet()) {
|
||||
final FieldDescriptor field = entry.getKey();
|
||||
final Object value = entry.getValue();
|
||||
if (isMessageSet && field.isExtension() &&
|
||||
field.getType() == FieldDescriptor.Type.MESSAGE &&
|
||||
!field.isRepeated()) {
|
||||
output.writeMessageSetExtension(field.getNumber(), (Message) value);
|
||||
} else {
|
||||
FieldSet.writeField(field, value, output);
|
||||
}
|
||||
}
|
||||
|
||||
final UnknownFieldSet unknownFields = getUnknownFields();
|
||||
if (isMessageSet) {
|
||||
unknownFields.writeAsMessageSetTo(output);
|
||||
} else {
|
||||
unknownFields.writeTo(output);
|
||||
}
|
||||
}
|
||||
|
||||
private int memoizedSize = -1;
|
||||
|
||||
public int getSerializedSize() {
|
||||
int size = memoizedSize;
|
||||
if (size != -1) {
|
||||
return size;
|
||||
}
|
||||
|
||||
size = 0;
|
||||
final boolean isMessageSet =
|
||||
getDescriptorForType().getOptions().getMessageSetWireFormat();
|
||||
|
||||
for (final Map.Entry<FieldDescriptor, Object> entry :
|
||||
getAllFields().entrySet()) {
|
||||
final FieldDescriptor field = entry.getKey();
|
||||
final Object value = entry.getValue();
|
||||
if (isMessageSet && field.isExtension() &&
|
||||
field.getType() == FieldDescriptor.Type.MESSAGE &&
|
||||
!field.isRepeated()) {
|
||||
size += CodedOutputStream.computeMessageSetExtensionSize(
|
||||
field.getNumber(), (Message) value);
|
||||
} else {
|
||||
size += FieldSet.computeFieldSize(field, value);
|
||||
}
|
||||
}
|
||||
|
||||
final UnknownFieldSet unknownFields = getUnknownFields();
|
||||
if (isMessageSet) {
|
||||
size += unknownFields.getSerializedSizeAsMessageSet();
|
||||
} else {
|
||||
size += unknownFields.getSerializedSize();
|
||||
}
|
||||
|
||||
memoizedSize = size;
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (other == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof Message)) {
|
||||
return false;
|
||||
}
|
||||
final Message otherMessage = (Message) other;
|
||||
if (getDescriptorForType() != otherMessage.getDescriptorForType()) {
|
||||
return false;
|
||||
}
|
||||
return getAllFields().equals(otherMessage.getAllFields()) &&
|
||||
getUnknownFields().equals(otherMessage.getUnknownFields());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 41;
|
||||
hash = (19 * hash) + getDescriptorForType().hashCode();
|
||||
hash = hashFields(hash, getAllFields());
|
||||
hash = (29 * hash) + getUnknownFields().hashCode();
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Package private helper method for AbstractParser to create
|
||||
* UninitializedMessageException with missing field information.
|
||||
*/
|
||||
@Override
|
||||
UninitializedMessageException newUninitializedMessageException() {
|
||||
return Builder.newUninitializedMessageException(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for implementing {@link Message#hashCode()}.
|
||||
* <p>
|
||||
* This is needed because {@link java.lang.Enum#hashCode()} is final, but we
|
||||
* need to use the field number as the hash code to ensure compatibility
|
||||
* between statically and dynamically generated enum objects.
|
||||
*/
|
||||
protected static int hashEnum(EnumLite e) {
|
||||
return e.getNumber();
|
||||
}
|
||||
|
||||
/** Helper method for implementing {@link Message#hashCode()}. */
|
||||
protected static int hashEnumList(List<? extends EnumLite> list) {
|
||||
int hash = 1;
|
||||
for (EnumLite e : list) {
|
||||
hash = 31 * hash + hashEnum(e);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
/**
|
||||
* A partial implementation of the {@link Message.Builder} interface which
|
||||
* implements as many methods of that interface as possible in terms of
|
||||
* other methods.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static abstract class Builder<BuilderType extends Builder>
|
||||
extends AbstractMessageLite.Builder<BuilderType>
|
||||
implements Message.Builder {
|
||||
// The compiler produces an error if this is not declared explicitly.
|
||||
@Override
|
||||
public abstract BuilderType clone();
|
||||
|
||||
public BuilderType clear() {
|
||||
for (final Map.Entry<FieldDescriptor, Object> entry :
|
||||
getAllFields().entrySet()) {
|
||||
clearField(entry.getKey());
|
||||
}
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
public List<String> findInitializationErrors() {
|
||||
return findMissingFields(this);
|
||||
}
|
||||
|
||||
public String getInitializationErrorString() {
|
||||
return delimitWithCommas(findInitializationErrors());
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(final Message other) {
|
||||
if (other.getDescriptorForType() != getDescriptorForType()) {
|
||||
throw new IllegalArgumentException(
|
||||
"mergeFrom(Message) can only merge messages of the same type.");
|
||||
}
|
||||
|
||||
// Note: We don't attempt to verify that other's fields have valid
|
||||
// types. Doing so would be a losing battle. We'd have to verify
|
||||
// all sub-messages as well, and we'd have to make copies of all of
|
||||
// them to insure that they don't change after verification (since
|
||||
// the Message interface itself cannot enforce immutability of
|
||||
// implementations).
|
||||
// TODO(kenton): Provide a function somewhere called makeDeepCopy()
|
||||
// which allows people to make secure deep copies of messages.
|
||||
|
||||
for (final Map.Entry<FieldDescriptor, Object> entry :
|
||||
other.getAllFields().entrySet()) {
|
||||
final FieldDescriptor field = entry.getKey();
|
||||
if (field.isRepeated()) {
|
||||
for (final Object element : (List)entry.getValue()) {
|
||||
addRepeatedField(field, element);
|
||||
}
|
||||
} else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
final Message existingValue = (Message)getField(field);
|
||||
if (existingValue == existingValue.getDefaultInstanceForType()) {
|
||||
setField(field, entry.getValue());
|
||||
} else {
|
||||
setField(field,
|
||||
existingValue.newBuilderForType()
|
||||
.mergeFrom(existingValue)
|
||||
.mergeFrom((Message)entry.getValue())
|
||||
.build());
|
||||
}
|
||||
} else {
|
||||
setField(field, entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
mergeUnknownFields(other.getUnknownFields());
|
||||
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(final CodedInputStream input)
|
||||
throws IOException {
|
||||
return mergeFrom(input, ExtensionRegistry.getEmptyRegistry());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(
|
||||
final CodedInputStream input,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
final UnknownFieldSet.Builder unknownFields =
|
||||
UnknownFieldSet.newBuilder(getUnknownFields());
|
||||
while (true) {
|
||||
final int tag = input.readTag();
|
||||
if (tag == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!mergeFieldFrom(input, unknownFields, extensionRegistry,
|
||||
getDescriptorForType(), this, null, tag)) {
|
||||
// end group tag
|
||||
break;
|
||||
}
|
||||
}
|
||||
setUnknownFields(unknownFields.build());
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
/** helper method to handle {@code builder} and {@code extensions}. */
|
||||
private static void addRepeatedField(
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions,
|
||||
FieldDescriptor field,
|
||||
Object value) {
|
||||
if (builder != null) {
|
||||
builder.addRepeatedField(field, value);
|
||||
} else {
|
||||
extensions.addRepeatedField(field, value);
|
||||
}
|
||||
}
|
||||
|
||||
/** helper method to handle {@code builder} and {@code extensions}. */
|
||||
private static void setField(
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions,
|
||||
FieldDescriptor field,
|
||||
Object value) {
|
||||
if (builder != null) {
|
||||
builder.setField(field, value);
|
||||
} else {
|
||||
extensions.setField(field, value);
|
||||
}
|
||||
}
|
||||
|
||||
/** helper method to handle {@code builder} and {@code extensions}. */
|
||||
private static boolean hasOriginalMessage(
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions,
|
||||
FieldDescriptor field) {
|
||||
if (builder != null) {
|
||||
return builder.hasField(field);
|
||||
} else {
|
||||
return extensions.hasField(field);
|
||||
}
|
||||
}
|
||||
|
||||
/** helper method to handle {@code builder} and {@code extensions}. */
|
||||
private static Message getOriginalMessage(
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions,
|
||||
FieldDescriptor field) {
|
||||
if (builder != null) {
|
||||
return (Message) builder.getField(field);
|
||||
} else {
|
||||
return (Message) extensions.getField(field);
|
||||
}
|
||||
}
|
||||
|
||||
/** helper method to handle {@code builder} and {@code extensions}. */
|
||||
private static void mergeOriginalMessage(
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions,
|
||||
FieldDescriptor field,
|
||||
Message.Builder subBuilder) {
|
||||
Message originalMessage = getOriginalMessage(builder, extensions, field);
|
||||
if (originalMessage != null) {
|
||||
subBuilder.mergeFrom(originalMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #mergeFrom(CodedInputStream, ExtensionRegistryLite)}, but
|
||||
* parses a single field.
|
||||
*
|
||||
* When {@code builder} is not null, the method will parse and merge the
|
||||
* field into {@code builder}. Otherwise, it will try to parse the field
|
||||
* into {@code extensions}, when it's called by the parsing constructor in
|
||||
* generated classes.
|
||||
*
|
||||
* Package-private because it is used by GeneratedMessage.ExtendableMessage.
|
||||
* @param tag The tag, which should have already been read.
|
||||
* @return {@code true} unless the tag is an end-group tag.
|
||||
*/
|
||||
static boolean mergeFieldFrom(
|
||||
CodedInputStream input,
|
||||
UnknownFieldSet.Builder unknownFields,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
Descriptor type,
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions,
|
||||
int tag) throws IOException {
|
||||
if (type.getOptions().getMessageSetWireFormat() &&
|
||||
tag == WireFormat.MESSAGE_SET_ITEM_TAG) {
|
||||
mergeMessageSetExtensionFromCodedStream(
|
||||
input, unknownFields, extensionRegistry, type, builder, extensions);
|
||||
return true;
|
||||
}
|
||||
|
||||
final int wireType = WireFormat.getTagWireType(tag);
|
||||
final int fieldNumber = WireFormat.getTagFieldNumber(tag);
|
||||
|
||||
final FieldDescriptor field;
|
||||
Message defaultInstance = null;
|
||||
|
||||
if (type.isExtensionNumber(fieldNumber)) {
|
||||
// extensionRegistry may be either ExtensionRegistry or
|
||||
// ExtensionRegistryLite. Since the type we are parsing is a full
|
||||
// message, only a full ExtensionRegistry could possibly contain
|
||||
// extensions of it. Otherwise we will treat the registry as if it
|
||||
// were empty.
|
||||
if (extensionRegistry instanceof ExtensionRegistry) {
|
||||
final ExtensionRegistry.ExtensionInfo extension =
|
||||
((ExtensionRegistry) extensionRegistry)
|
||||
.findExtensionByNumber(type, fieldNumber);
|
||||
if (extension == null) {
|
||||
field = null;
|
||||
} else {
|
||||
field = extension.descriptor;
|
||||
defaultInstance = extension.defaultInstance;
|
||||
if (defaultInstance == null &&
|
||||
field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
throw new IllegalStateException(
|
||||
"Message-typed extension lacked default instance: " +
|
||||
field.getFullName());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
field = null;
|
||||
}
|
||||
} else if (builder != null) {
|
||||
field = type.findFieldByNumber(fieldNumber);
|
||||
} else {
|
||||
field = null;
|
||||
}
|
||||
|
||||
boolean unknown = false;
|
||||
boolean packed = false;
|
||||
if (field == null) {
|
||||
unknown = true; // Unknown field.
|
||||
} else if (wireType == FieldSet.getWireFormatForFieldType(
|
||||
field.getLiteType(),
|
||||
false /* isPacked */)) {
|
||||
packed = false;
|
||||
} else if (field.isPackable() &&
|
||||
wireType == FieldSet.getWireFormatForFieldType(
|
||||
field.getLiteType(),
|
||||
true /* isPacked */)) {
|
||||
packed = true;
|
||||
} else {
|
||||
unknown = true; // Unknown wire type.
|
||||
}
|
||||
|
||||
if (unknown) { // Unknown field or wrong wire type. Skip.
|
||||
return unknownFields.mergeFieldFrom(tag, input);
|
||||
}
|
||||
|
||||
if (packed) {
|
||||
final int length = input.readRawVarint32();
|
||||
final int limit = input.pushLimit(length);
|
||||
if (field.getLiteType() == WireFormat.FieldType.ENUM) {
|
||||
while (input.getBytesUntilLimit() > 0) {
|
||||
final int rawValue = input.readEnum();
|
||||
final Object value = field.getEnumType().findValueByNumber(rawValue);
|
||||
if (value == null) {
|
||||
// If the number isn't recognized as a valid value for this
|
||||
// enum, drop it (don't even add it to unknownFields).
|
||||
return true;
|
||||
}
|
||||
addRepeatedField(builder, extensions, field, value);
|
||||
}
|
||||
} else {
|
||||
while (input.getBytesUntilLimit() > 0) {
|
||||
final Object value =
|
||||
FieldSet.readPrimitiveField(input, field.getLiteType());
|
||||
addRepeatedField(builder, extensions, field, value);
|
||||
}
|
||||
}
|
||||
input.popLimit(limit);
|
||||
} else {
|
||||
final Object value;
|
||||
switch (field.getType()) {
|
||||
case GROUP: {
|
||||
final Message.Builder subBuilder;
|
||||
if (defaultInstance != null) {
|
||||
subBuilder = defaultInstance.newBuilderForType();
|
||||
} else {
|
||||
subBuilder = builder.newBuilderForField(field);
|
||||
}
|
||||
if (!field.isRepeated()) {
|
||||
mergeOriginalMessage(builder, extensions, field, subBuilder);
|
||||
}
|
||||
input.readGroup(field.getNumber(), subBuilder, extensionRegistry);
|
||||
value = subBuilder.buildPartial();
|
||||
break;
|
||||
}
|
||||
case MESSAGE: {
|
||||
final Message.Builder subBuilder;
|
||||
if (defaultInstance != null) {
|
||||
subBuilder = defaultInstance.newBuilderForType();
|
||||
} else {
|
||||
subBuilder = builder.newBuilderForField(field);
|
||||
}
|
||||
if (!field.isRepeated()) {
|
||||
mergeOriginalMessage(builder, extensions, field, subBuilder);
|
||||
}
|
||||
input.readMessage(subBuilder, extensionRegistry);
|
||||
value = subBuilder.buildPartial();
|
||||
break;
|
||||
}
|
||||
case ENUM:
|
||||
final int rawValue = input.readEnum();
|
||||
value = field.getEnumType().findValueByNumber(rawValue);
|
||||
// If the number isn't recognized as a valid value for this enum,
|
||||
// drop it.
|
||||
if (value == null) {
|
||||
unknownFields.mergeVarintField(fieldNumber, rawValue);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
value = FieldSet.readPrimitiveField(input, field.getLiteType());
|
||||
break;
|
||||
}
|
||||
|
||||
if (field.isRepeated()) {
|
||||
addRepeatedField(builder, extensions, field, value);
|
||||
} else {
|
||||
setField(builder, extensions, field, value);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by {@code #mergeFieldFrom()} to parse a MessageSet extension.
|
||||
* If {@code builder} is not null, this method will merge MessageSet into
|
||||
* the builder. Otherwise, it will merge the MessageSet into {@code
|
||||
* extensions}.
|
||||
*/
|
||||
private static void mergeMessageSetExtensionFromCodedStream(
|
||||
CodedInputStream input,
|
||||
UnknownFieldSet.Builder unknownFields,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
Descriptor type,
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions) throws IOException {
|
||||
|
||||
// The wire format for MessageSet is:
|
||||
// message MessageSet {
|
||||
// repeated group Item = 1 {
|
||||
// required int32 typeId = 2;
|
||||
// required bytes message = 3;
|
||||
// }
|
||||
// }
|
||||
// "typeId" is the extension's field number. The extension can only be
|
||||
// a message type, where "message" contains the encoded bytes of that
|
||||
// message.
|
||||
//
|
||||
// In practice, we will probably never see a MessageSet item in which
|
||||
// the message appears before the type ID, or where either field does not
|
||||
// appear exactly once. However, in theory such cases are valid, so we
|
||||
// should be prepared to accept them.
|
||||
|
||||
int typeId = 0;
|
||||
ByteString rawBytes = null; // If we encounter "message" before "typeId"
|
||||
ExtensionRegistry.ExtensionInfo extension = null;
|
||||
|
||||
// Read bytes from input, if we get it's type first then parse it eagerly,
|
||||
// otherwise we store the raw bytes in a local variable.
|
||||
while (true) {
|
||||
final int tag = input.readTag();
|
||||
if (tag == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (tag == WireFormat.MESSAGE_SET_TYPE_ID_TAG) {
|
||||
typeId = input.readUInt32();
|
||||
if (typeId != 0) {
|
||||
// extensionRegistry may be either ExtensionRegistry or
|
||||
// ExtensionRegistryLite. Since the type we are parsing is a full
|
||||
// message, only a full ExtensionRegistry could possibly contain
|
||||
// extensions of it. Otherwise we will treat the registry as if it
|
||||
// were empty.
|
||||
if (extensionRegistry instanceof ExtensionRegistry) {
|
||||
extension = ((ExtensionRegistry) extensionRegistry)
|
||||
.findExtensionByNumber(type, typeId);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (tag == WireFormat.MESSAGE_SET_MESSAGE_TAG) {
|
||||
if (typeId != 0) {
|
||||
if (extension != null && ExtensionRegistryLite.isEagerlyParseMessageSets()) {
|
||||
// We already know the type, so we can parse directly from the
|
||||
// input with no copying. Hooray!
|
||||
eagerlyMergeMessageSetExtension(
|
||||
input, extension, extensionRegistry, builder, extensions);
|
||||
rawBytes = null;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// We haven't seen a type ID yet or we want parse message lazily.
|
||||
rawBytes = input.readBytes();
|
||||
|
||||
} else { // Unknown tag. Skip it.
|
||||
if (!input.skipField(tag)) {
|
||||
break; // End of group
|
||||
}
|
||||
}
|
||||
}
|
||||
input.checkLastTagWas(WireFormat.MESSAGE_SET_ITEM_END_TAG);
|
||||
|
||||
// Process the raw bytes.
|
||||
if (rawBytes != null && typeId != 0) { // Zero is not a valid type ID.
|
||||
if (extension != null) { // We known the type
|
||||
mergeMessageSetExtensionFromBytes(
|
||||
rawBytes, extension, extensionRegistry, builder, extensions);
|
||||
} else { // We don't know how to parse this. Ignore it.
|
||||
if (rawBytes != null) {
|
||||
unknownFields.mergeField(typeId, UnknownFieldSet.Field.newBuilder()
|
||||
.addLengthDelimited(rawBytes).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void eagerlyMergeMessageSetExtension(
|
||||
CodedInputStream input,
|
||||
ExtensionRegistry.ExtensionInfo extension,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions) throws IOException {
|
||||
|
||||
FieldDescriptor field = extension.descriptor;
|
||||
Message value = null;
|
||||
if (hasOriginalMessage(builder, extensions, field)) {
|
||||
Message originalMessage =
|
||||
getOriginalMessage(builder, extensions, field);
|
||||
Message.Builder subBuilder = originalMessage.toBuilder();
|
||||
input.readMessage(subBuilder, extensionRegistry);
|
||||
value = subBuilder.buildPartial();
|
||||
} else {
|
||||
value = input.readMessage(extension.defaultInstance.getParserForType(),
|
||||
extensionRegistry);
|
||||
}
|
||||
|
||||
if (builder != null) {
|
||||
builder.setField(field, value);
|
||||
} else {
|
||||
extensions.setField(field, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void mergeMessageSetExtensionFromBytes(
|
||||
ByteString rawBytes,
|
||||
ExtensionRegistry.ExtensionInfo extension,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions) throws IOException {
|
||||
|
||||
FieldDescriptor field = extension.descriptor;
|
||||
boolean hasOriginalValue = hasOriginalMessage(builder, extensions, field);
|
||||
|
||||
if (hasOriginalValue || ExtensionRegistryLite.isEagerlyParseMessageSets()) {
|
||||
// If the field already exists, we just parse the field.
|
||||
Message value = null;
|
||||
if (hasOriginalValue) {
|
||||
Message originalMessage =
|
||||
getOriginalMessage(builder, extensions, field);
|
||||
Message.Builder subBuilder= originalMessage.toBuilder();
|
||||
subBuilder.mergeFrom(rawBytes, extensionRegistry);
|
||||
value = subBuilder.buildPartial();
|
||||
} else {
|
||||
value = extension.defaultInstance.getParserForType()
|
||||
.parsePartialFrom(rawBytes, extensionRegistry);
|
||||
}
|
||||
setField(builder, extensions, field, value);
|
||||
} else {
|
||||
// Use LazyField to load MessageSet lazily.
|
||||
LazyField lazyField = new LazyField(
|
||||
extension.defaultInstance, extensionRegistry, rawBytes);
|
||||
if (builder != null) {
|
||||
// TODO(xiangl): it looks like this method can only be invoked by
|
||||
// ExtendableBuilder, but I'm not sure. So I double check the type of
|
||||
// builder here. It may be useless and need more investigation.
|
||||
if (builder instanceof ExtendableBuilder) {
|
||||
builder.setField(field, lazyField);
|
||||
} else {
|
||||
builder.setField(field, lazyField.getValue());
|
||||
}
|
||||
} else {
|
||||
extensions.setField(field, lazyField);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BuilderType mergeUnknownFields(final UnknownFieldSet unknownFields) {
|
||||
setUnknownFields(
|
||||
UnknownFieldSet.newBuilder(getUnknownFields())
|
||||
.mergeFrom(unknownFields)
|
||||
.build());
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
public Message.Builder getFieldBuilder(final FieldDescriptor field) {
|
||||
throw new UnsupportedOperationException(
|
||||
"getFieldBuilder() called on an unsupported message type.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an UninitializedMessageException reporting missing fields in
|
||||
* the given message.
|
||||
*/
|
||||
protected static UninitializedMessageException
|
||||
newUninitializedMessageException(Message message) {
|
||||
return new UninitializedMessageException(findMissingFields(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates {@code this.missingFields} with the full "path" of each
|
||||
* missing required field in the given message.
|
||||
*/
|
||||
private static List<String> findMissingFields(
|
||||
final MessageOrBuilder message) {
|
||||
final List<String> results = new ArrayList<String>();
|
||||
findMissingFields(message, "", results);
|
||||
return results;
|
||||
}
|
||||
|
||||
/** Recursive helper implementing {@link #findMissingFields(Message)}. */
|
||||
private static void findMissingFields(final MessageOrBuilder message,
|
||||
final String prefix,
|
||||
final List<String> results) {
|
||||
for (final FieldDescriptor field :
|
||||
message.getDescriptorForType().getFields()) {
|
||||
if (field.isRequired() && !message.hasField(field)) {
|
||||
results.add(prefix + field.getName());
|
||||
}
|
||||
}
|
||||
|
||||
for (final Map.Entry<FieldDescriptor, Object> entry :
|
||||
message.getAllFields().entrySet()) {
|
||||
final FieldDescriptor field = entry.getKey();
|
||||
final Object value = entry.getValue();
|
||||
|
||||
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
if (field.isRepeated()) {
|
||||
int i = 0;
|
||||
for (final Object element : (List) value) {
|
||||
findMissingFields((MessageOrBuilder) element,
|
||||
subMessagePrefix(prefix, field, i++),
|
||||
results);
|
||||
}
|
||||
} else {
|
||||
if (message.hasField(field)) {
|
||||
findMissingFields((MessageOrBuilder) value,
|
||||
subMessagePrefix(prefix, field, -1),
|
||||
results);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String subMessagePrefix(final String prefix,
|
||||
final FieldDescriptor field,
|
||||
final int index) {
|
||||
final StringBuilder result = new StringBuilder(prefix);
|
||||
if (field.isExtension()) {
|
||||
result.append('(')
|
||||
.append(field.getFullName())
|
||||
.append(')');
|
||||
} else {
|
||||
result.append(field.getName());
|
||||
}
|
||||
if (index != -1) {
|
||||
result.append('[')
|
||||
.append(index)
|
||||
.append(']');
|
||||
}
|
||||
result.append('.');
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// The following definitions seem to be required in order to make javac
|
||||
// not produce weird errors like:
|
||||
//
|
||||
// java/com/google/protobuf/DynamicMessage.java:203: types
|
||||
// com.google.protobuf.AbstractMessage.Builder<
|
||||
// com.google.protobuf.DynamicMessage.Builder> and
|
||||
// com.google.protobuf.AbstractMessage.Builder<
|
||||
// com.google.protobuf.DynamicMessage.Builder> are incompatible; both
|
||||
// define mergeFrom(com.google.protobuf.ByteString), but with unrelated
|
||||
// return types.
|
||||
//
|
||||
// Strangely, these lines are only needed if javac is invoked separately
|
||||
// on AbstractMessage.java and AbstractMessageLite.java. If javac is
|
||||
// invoked on both simultaneously, it works. (Or maybe the important
|
||||
// point is whether or not DynamicMessage.java is compiled together with
|
||||
// AbstractMessageLite.java -- not sure.) I suspect this is a compiler
|
||||
// bug.
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(final ByteString data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return super.mergeFrom(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(
|
||||
final ByteString data,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return super.mergeFrom(data, extensionRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(final byte[] data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return super.mergeFrom(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(
|
||||
final byte[] data, final int off, final int len)
|
||||
throws InvalidProtocolBufferException {
|
||||
return super.mergeFrom(data, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(
|
||||
final byte[] data,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return super.mergeFrom(data, extensionRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(
|
||||
final byte[] data, final int off, final int len,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return super.mergeFrom(data, off, len, extensionRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(final InputStream input)
|
||||
throws IOException {
|
||||
return super.mergeFrom(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(
|
||||
final InputStream input,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
return super.mergeFrom(input, extensionRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mergeDelimitedFrom(final InputStream input)
|
||||
throws IOException {
|
||||
return super.mergeDelimitedFrom(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mergeDelimitedFrom(
|
||||
final InputStream input,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
return super.mergeDelimitedFrom(input, extensionRegistry);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,343 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* A partial implementation of the {@link MessageLite} interface which
|
||||
* implements as many methods of that interface as possible in terms of other
|
||||
* methods.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public abstract class AbstractMessageLite implements MessageLite {
|
||||
public ByteString toByteString() {
|
||||
try {
|
||||
final ByteString.CodedBuilder out =
|
||||
ByteString.newCodedBuilder(getSerializedSize());
|
||||
writeTo(out.getCodedOutput());
|
||||
return out.build();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Serializing to a ByteString threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] toByteArray() {
|
||||
try {
|
||||
final byte[] result = new byte[getSerializedSize()];
|
||||
final CodedOutputStream output = CodedOutputStream.newInstance(result);
|
||||
writeTo(output);
|
||||
output.checkNoSpaceLeft();
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Serializing to a byte array threw an IOException " +
|
||||
"(should never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeTo(final OutputStream output) throws IOException {
|
||||
final int bufferSize =
|
||||
CodedOutputStream.computePreferredBufferSize(getSerializedSize());
|
||||
final CodedOutputStream codedOutput =
|
||||
CodedOutputStream.newInstance(output, bufferSize);
|
||||
writeTo(codedOutput);
|
||||
codedOutput.flush();
|
||||
}
|
||||
|
||||
public void writeDelimitedTo(final OutputStream output) throws IOException {
|
||||
final int serialized = getSerializedSize();
|
||||
final int bufferSize = CodedOutputStream.computePreferredBufferSize(
|
||||
CodedOutputStream.computeRawVarint32Size(serialized) + serialized);
|
||||
final CodedOutputStream codedOutput =
|
||||
CodedOutputStream.newInstance(output, bufferSize);
|
||||
codedOutput.writeRawVarint32(serialized);
|
||||
writeTo(codedOutput);
|
||||
codedOutput.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Package private helper method for AbstractParser to create
|
||||
* UninitializedMessageException.
|
||||
*/
|
||||
UninitializedMessageException newUninitializedMessageException() {
|
||||
return new UninitializedMessageException(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* A partial implementation of the {@link Message.Builder} interface which
|
||||
* implements as many methods of that interface as possible in terms of
|
||||
* other methods.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static abstract class Builder<BuilderType extends Builder>
|
||||
implements MessageLite.Builder {
|
||||
// The compiler produces an error if this is not declared explicitly.
|
||||
@Override
|
||||
public abstract BuilderType clone();
|
||||
|
||||
public BuilderType mergeFrom(final CodedInputStream input)
|
||||
throws IOException {
|
||||
return mergeFrom(input, ExtensionRegistryLite.getEmptyRegistry());
|
||||
}
|
||||
|
||||
// Re-defined here for return type covariance.
|
||||
public abstract BuilderType mergeFrom(
|
||||
final CodedInputStream input,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException;
|
||||
|
||||
public BuilderType mergeFrom(final ByteString data)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
final CodedInputStream input = data.newCodedInput();
|
||||
mergeFrom(input);
|
||||
input.checkLastTagWas(0);
|
||||
return (BuilderType) this;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a ByteString threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(
|
||||
final ByteString data,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
final CodedInputStream input = data.newCodedInput();
|
||||
mergeFrom(input, extensionRegistry);
|
||||
input.checkLastTagWas(0);
|
||||
return (BuilderType) this;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a ByteString threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(final byte[] data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return mergeFrom(data, 0, data.length);
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(final byte[] data, final int off,
|
||||
final int len)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
final CodedInputStream input =
|
||||
CodedInputStream.newInstance(data, off, len);
|
||||
mergeFrom(input);
|
||||
input.checkLastTagWas(0);
|
||||
return (BuilderType) this;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a byte array threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(
|
||||
final byte[] data,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return mergeFrom(data, 0, data.length, extensionRegistry);
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(
|
||||
final byte[] data, final int off, final int len,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
final CodedInputStream input =
|
||||
CodedInputStream.newInstance(data, off, len);
|
||||
mergeFrom(input, extensionRegistry);
|
||||
input.checkLastTagWas(0);
|
||||
return (BuilderType) this;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a byte array threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(final InputStream input) throws IOException {
|
||||
final CodedInputStream codedInput = CodedInputStream.newInstance(input);
|
||||
mergeFrom(codedInput);
|
||||
codedInput.checkLastTagWas(0);
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(
|
||||
final InputStream input,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
final CodedInputStream codedInput = CodedInputStream.newInstance(input);
|
||||
mergeFrom(codedInput, extensionRegistry);
|
||||
codedInput.checkLastTagWas(0);
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* An InputStream implementations which reads from some other InputStream
|
||||
* but is limited to a particular number of bytes. Used by
|
||||
* mergeDelimitedFrom(). This is intentionally package-private so that
|
||||
* UnknownFieldSet can share it.
|
||||
*/
|
||||
static final class LimitedInputStream extends FilterInputStream {
|
||||
private int limit;
|
||||
|
||||
LimitedInputStream(InputStream in, int limit) {
|
||||
super(in);
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return Math.min(super.available(), limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (limit <= 0) {
|
||||
return -1;
|
||||
}
|
||||
final int result = super.read();
|
||||
if (result >= 0) {
|
||||
--limit;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(final byte[] b, final int off, int len)
|
||||
throws IOException {
|
||||
if (limit <= 0) {
|
||||
return -1;
|
||||
}
|
||||
len = Math.min(len, limit);
|
||||
final int result = super.read(b, off, len);
|
||||
if (result >= 0) {
|
||||
limit -= result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(final long n) throws IOException {
|
||||
final long result = super.skip(Math.min(n, limit));
|
||||
if (result >= 0) {
|
||||
limit -= result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean mergeDelimitedFrom(
|
||||
final InputStream input,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
final int firstByte = input.read();
|
||||
if (firstByte == -1) {
|
||||
return false;
|
||||
}
|
||||
final int size = CodedInputStream.readRawVarint32(firstByte, input);
|
||||
final InputStream limitedInput = new LimitedInputStream(input, size);
|
||||
mergeFrom(limitedInput, extensionRegistry);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean mergeDelimitedFrom(final InputStream input)
|
||||
throws IOException {
|
||||
return mergeDelimitedFrom(input,
|
||||
ExtensionRegistryLite.getEmptyRegistry());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an UninitializedMessageException reporting missing fields in
|
||||
* the given message.
|
||||
*/
|
||||
protected static UninitializedMessageException
|
||||
newUninitializedMessageException(MessageLite message) {
|
||||
return new UninitializedMessageException(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the {@code values} to the {@code list}. This is a helper method
|
||||
* used by generated code. Users should ignore it.
|
||||
*
|
||||
* @throws NullPointerException if any of the elements of {@code values} is
|
||||
* null.
|
||||
*/
|
||||
protected static <T> void addAll(final Iterable<T> values,
|
||||
final Collection<? super T> list) {
|
||||
if (values instanceof LazyStringList) {
|
||||
// For StringOrByteStringLists, check the underlying elements to avoid
|
||||
// forcing conversions of ByteStrings to Strings.
|
||||
checkForNullValues(((LazyStringList) values).getUnderlyingElements());
|
||||
} else {
|
||||
checkForNullValues(values);
|
||||
}
|
||||
if (values instanceof Collection) {
|
||||
final Collection<T> collection = (Collection<T>) values;
|
||||
list.addAll(collection);
|
||||
} else {
|
||||
for (final T value : values) {
|
||||
list.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkForNullValues(final Iterable<?> values) {
|
||||
for (final Object value : values) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.AbstractMessageLite.Builder.LimitedInputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* A partial implementation of the {@link Parser} interface which implements
|
||||
* as many methods of that interface as possible in terms of other methods.
|
||||
*
|
||||
* Note: This class implements all the convenience methods in the
|
||||
* {@link Parser} interface. See {@link Parser} for related javadocs.
|
||||
* Subclasses need to implement
|
||||
* {@link Parser#parsePartialFrom(CodedInputStream, ExtensionRegistryLite)}
|
||||
*
|
||||
* @author liujisi@google.com (Pherl Liu)
|
||||
*/
|
||||
public abstract class AbstractParser<MessageType extends MessageLite>
|
||||
implements Parser<MessageType> {
|
||||
/**
|
||||
* Creates an UninitializedMessageException for MessageType.
|
||||
*/
|
||||
private UninitializedMessageException
|
||||
newUninitializedMessageException(MessageType message) {
|
||||
if (message instanceof AbstractMessageLite) {
|
||||
return ((AbstractMessageLite) message).newUninitializedMessageException();
|
||||
}
|
||||
return new UninitializedMessageException(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to check if message is initialized.
|
||||
*
|
||||
* @throws InvalidProtocolBufferException if it is not initialized.
|
||||
* @return The message to check.
|
||||
*/
|
||||
private MessageType checkMessageInitialized(MessageType message)
|
||||
throws InvalidProtocolBufferException {
|
||||
if (message != null && !message.isInitialized()) {
|
||||
throw newUninitializedMessageException(message)
|
||||
.asInvalidProtocolBufferException()
|
||||
.setUnfinishedMessage(message);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
private static final ExtensionRegistryLite EMPTY_REGISTRY
|
||||
= ExtensionRegistryLite.getEmptyRegistry();
|
||||
|
||||
public MessageType parsePartialFrom(CodedInputStream input)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parsePartialFrom(input, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parseFrom(CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(
|
||||
parsePartialFrom(input, extensionRegistry));
|
||||
}
|
||||
|
||||
public MessageType parseFrom(CodedInputStream input)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parseFrom(input, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(ByteString data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
MessageType message;
|
||||
try {
|
||||
CodedInputStream input = data.newCodedInput();
|
||||
message = parsePartialFrom(input, extensionRegistry);
|
||||
try {
|
||||
input.checkLastTagWas(0);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e.setUnfinishedMessage(message);
|
||||
}
|
||||
return message;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a ByteString threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(ByteString data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parsePartialFrom(data, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parseFrom(ByteString data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(parsePartialFrom(data, extensionRegistry));
|
||||
}
|
||||
|
||||
public MessageType parseFrom(ByteString data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parseFrom(data, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(byte[] data, int off, int len,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
CodedInputStream input = CodedInputStream.newInstance(data, off, len);
|
||||
MessageType message = parsePartialFrom(input, extensionRegistry);
|
||||
try {
|
||||
input.checkLastTagWas(0);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e.setUnfinishedMessage(message);
|
||||
}
|
||||
return message;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a byte array threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(byte[] data, int off, int len)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parsePartialFrom(data, off, len, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(byte[] data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parsePartialFrom(data, 0, data.length, extensionRegistry);
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(byte[] data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parsePartialFrom(data, 0, data.length, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parseFrom(byte[] data, int off, int len,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(
|
||||
parsePartialFrom(data, off, len, extensionRegistry));
|
||||
}
|
||||
|
||||
public MessageType parseFrom(byte[] data, int off, int len)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parseFrom(data, off, len, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parseFrom(byte[] data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parseFrom(data, 0, data.length, extensionRegistry);
|
||||
}
|
||||
|
||||
public MessageType parseFrom(byte[] data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parseFrom(data, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
CodedInputStream codedInput = CodedInputStream.newInstance(input);
|
||||
MessageType message = parsePartialFrom(codedInput, extensionRegistry);
|
||||
try {
|
||||
codedInput.checkLastTagWas(0);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e.setUnfinishedMessage(message);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parsePartialFrom(input, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parseFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(
|
||||
parsePartialFrom(input, extensionRegistry));
|
||||
}
|
||||
|
||||
public MessageType parseFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parseFrom(input, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parsePartialDelimitedFrom(
|
||||
InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
int size;
|
||||
try {
|
||||
int firstByte = input.read();
|
||||
if (firstByte == -1) {
|
||||
return null;
|
||||
}
|
||||
size = CodedInputStream.readRawVarint32(firstByte, input);
|
||||
} catch (IOException e) {
|
||||
throw new InvalidProtocolBufferException(e.getMessage());
|
||||
}
|
||||
InputStream limitedInput = new LimitedInputStream(input, size);
|
||||
return parsePartialFrom(limitedInput, extensionRegistry);
|
||||
}
|
||||
|
||||
public MessageType parsePartialDelimitedFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parsePartialDelimitedFrom(input, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parseDelimitedFrom(
|
||||
InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(
|
||||
parsePartialDelimitedFrom(input, extensionRegistry));
|
||||
}
|
||||
|
||||
public MessageType parseDelimitedFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parseDelimitedFrom(input, EMPTY_REGISTRY);
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
/**
|
||||
* <p>Abstract interface for a blocking RPC channel. {@code BlockingRpcChannel}
|
||||
* is the blocking equivalent to {@link RpcChannel}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
* @author cpovirk@google.com Chris Povirk
|
||||
*/
|
||||
public interface BlockingRpcChannel {
|
||||
/**
|
||||
* Call the given method of the remote service and blocks until it returns.
|
||||
* {@code callBlockingMethod()} is the blocking equivalent to
|
||||
* {@link RpcChannel#callMethod}.
|
||||
*/
|
||||
Message callBlockingMethod(
|
||||
Descriptors.MethodDescriptor method,
|
||||
RpcController controller,
|
||||
Message request,
|
||||
Message responsePrototype) throws ServiceException;
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
/**
|
||||
* Blocking equivalent to {@link Service}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
* @author cpovirk@google.com Chris Povirk
|
||||
*/
|
||||
public interface BlockingService {
|
||||
/**
|
||||
* Equivalent to {@link Service#getDescriptorForType}.
|
||||
*/
|
||||
Descriptors.ServiceDescriptor getDescriptorForType();
|
||||
|
||||
/**
|
||||
* Equivalent to {@link Service#callMethod}, except that
|
||||
* {@code callBlockingMethod()} returns the result of the RPC or throws a
|
||||
* {@link ServiceException} if there is a failure, rather than passing the
|
||||
* information to a callback.
|
||||
*/
|
||||
Message callBlockingMethod(Descriptors.MethodDescriptor method,
|
||||
RpcController controller,
|
||||
Message request) throws ServiceException;
|
||||
|
||||
/**
|
||||
* Equivalent to {@link Service#getRequestPrototype}.
|
||||
*/
|
||||
Message getRequestPrototype(Descriptors.MethodDescriptor method);
|
||||
|
||||
/**
|
||||
* Equivalent to {@link Service#getResponsePrototype}.
|
||||
*/
|
||||
Message getResponsePrototype(Descriptors.MethodDescriptor method);
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* This class is used to represent the substring of a {@link ByteString} over a
|
||||
* single byte array. In terms of the public API of {@link ByteString}, you end
|
||||
* up here by calling {@link ByteString#copyFrom(byte[])} followed by {@link
|
||||
* ByteString#substring(int, int)}.
|
||||
*
|
||||
* <p>This class contains most of the overhead involved in creating a substring
|
||||
* from a {@link LiteralByteString}. The overhead involves some range-checking
|
||||
* and two extra fields.
|
||||
*
|
||||
* @author carlanton@google.com (Carl Haverl)
|
||||
*/
|
||||
class BoundedByteString extends LiteralByteString {
|
||||
|
||||
private final int bytesOffset;
|
||||
private final int bytesLength;
|
||||
|
||||
/**
|
||||
* Creates a {@code BoundedByteString} backed by the sub-range of given array,
|
||||
* without copying.
|
||||
*
|
||||
* @param bytes array to wrap
|
||||
* @param offset index to first byte to use in bytes
|
||||
* @param length number of bytes to use from bytes
|
||||
* @throws IllegalArgumentException if {@code offset < 0}, {@code length < 0},
|
||||
* or if {@code offset + length >
|
||||
* bytes.length}.
|
||||
*/
|
||||
BoundedByteString(byte[] bytes, int offset, int length) {
|
||||
super(bytes);
|
||||
if (offset < 0) {
|
||||
throw new IllegalArgumentException("Offset too small: " + offset);
|
||||
}
|
||||
if (length < 0) {
|
||||
throw new IllegalArgumentException("Length too small: " + offset);
|
||||
}
|
||||
if ((long) offset + length > bytes.length) {
|
||||
throw new IllegalArgumentException(
|
||||
"Offset+Length too large: " + offset + "+" + length);
|
||||
}
|
||||
|
||||
this.bytesOffset = offset;
|
||||
this.bytesLength = length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the byte at the given index.
|
||||
* Throws {@link ArrayIndexOutOfBoundsException}
|
||||
* for backwards-compatibility reasons although it would more properly be
|
||||
* {@link IndexOutOfBoundsException}.
|
||||
*
|
||||
* @param index index of byte
|
||||
* @return the value
|
||||
* @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or >= size
|
||||
*/
|
||||
@Override
|
||||
public byte byteAt(int index) {
|
||||
// We must check the index ourselves as we cannot rely on Java array index
|
||||
// checking for substrings.
|
||||
if (index < 0) {
|
||||
throw new ArrayIndexOutOfBoundsException("Index too small: " + index);
|
||||
}
|
||||
if (index >= size()) {
|
||||
throw new ArrayIndexOutOfBoundsException(
|
||||
"Index too large: " + index + ", " + size());
|
||||
}
|
||||
|
||||
return bytes[bytesOffset + index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return bytesLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getOffsetIntoBytes() {
|
||||
return bytesOffset;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteString -> byte[]
|
||||
|
||||
@Override
|
||||
protected void copyToInternal(byte[] target, int sourceOffset,
|
||||
int targetOffset, int numberToCopy) {
|
||||
System.arraycopy(bytes, getOffsetIntoBytes() + sourceOffset, target,
|
||||
targetOffset, numberToCopy);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteIterator
|
||||
|
||||
@Override
|
||||
public ByteIterator iterator() {
|
||||
return new BoundedByteIterator();
|
||||
}
|
||||
|
||||
private class BoundedByteIterator implements ByteIterator {
|
||||
|
||||
private int position;
|
||||
private final int limit;
|
||||
|
||||
private BoundedByteIterator() {
|
||||
position = getOffsetIntoBytes();
|
||||
limit = position + size();
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return (position < limit);
|
||||
}
|
||||
|
||||
public Byte next() {
|
||||
// Boxing calls Byte.valueOf(byte), which does not instantiate.
|
||||
return nextByte();
|
||||
}
|
||||
|
||||
public byte nextByte() {
|
||||
if (position >= limit) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
return bytes[position++];
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,970 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* Immutable sequence of bytes. Substring is supported by sharing the reference
|
||||
* to the immutable underlying bytes, as with {@link String}. Concatenation is
|
||||
* likewise supported without copying (long strings) by building a tree of
|
||||
* pieces in {@link RopeByteString}.
|
||||
* <p>
|
||||
* Like {@link String}, the contents of a {@link ByteString} can never be
|
||||
* observed to change, not even in the presence of a data race or incorrect
|
||||
* API usage in the client code.
|
||||
*
|
||||
* @author crazybob@google.com Bob Lee
|
||||
* @author kenton@google.com Kenton Varda
|
||||
* @author carlanton@google.com Carl Haverl
|
||||
* @author martinrb@google.com Martin Buchholz
|
||||
*/
|
||||
public abstract class ByteString implements Iterable<Byte> {
|
||||
|
||||
/**
|
||||
* When two strings to be concatenated have a combined length shorter than
|
||||
* this, we just copy their bytes on {@link #concat(ByteString)}.
|
||||
* The trade-off is copy size versus the overhead of creating tree nodes
|
||||
* in {@link RopeByteString}.
|
||||
*/
|
||||
static final int CONCATENATE_BY_COPY_SIZE = 128;
|
||||
|
||||
/**
|
||||
* When copying an InputStream into a ByteString with .readFrom(),
|
||||
* the chunks in the underlying rope start at 256 bytes, but double
|
||||
* each iteration up to 8192 bytes.
|
||||
*/
|
||||
static final int MIN_READ_FROM_CHUNK_SIZE = 0x100; // 256b
|
||||
static final int MAX_READ_FROM_CHUNK_SIZE = 0x2000; // 8k
|
||||
|
||||
/**
|
||||
* Empty {@code ByteString}.
|
||||
*/
|
||||
public static final ByteString EMPTY = new LiteralByteString(new byte[0]);
|
||||
|
||||
// This constructor is here to prevent subclassing outside of this package,
|
||||
ByteString() {}
|
||||
|
||||
/**
|
||||
* Gets the byte at the given index. This method should be used only for
|
||||
* random access to individual bytes. To access bytes sequentially, use the
|
||||
* {@link ByteIterator} returned by {@link #iterator()}, and call {@link
|
||||
* #substring(int, int)} first if necessary.
|
||||
*
|
||||
* @param index index of byte
|
||||
* @return the value
|
||||
* @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or >= size
|
||||
*/
|
||||
public abstract byte byteAt(int index);
|
||||
|
||||
/**
|
||||
* Return a {@link ByteString.ByteIterator} over the bytes in the ByteString.
|
||||
* To avoid auto-boxing, you may get the iterator manually and call
|
||||
* {@link ByteIterator#nextByte()}.
|
||||
*
|
||||
* @return the iterator
|
||||
*/
|
||||
public abstract ByteIterator iterator();
|
||||
|
||||
/**
|
||||
* This interface extends {@code Iterator<Byte>}, so that we can return an
|
||||
* unboxed {@code byte}.
|
||||
*/
|
||||
public interface ByteIterator extends Iterator<Byte> {
|
||||
/**
|
||||
* An alternative to {@link Iterator#next()} that returns an
|
||||
* unboxed primitive {@code byte}.
|
||||
*
|
||||
* @return the next {@code byte} in the iteration
|
||||
* @throws NoSuchElementException if the iteration has no more elements
|
||||
*/
|
||||
byte nextByte();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of bytes.
|
||||
*
|
||||
* @return size in bytes
|
||||
*/
|
||||
public abstract int size();
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the size is {@code 0}, {@code false} otherwise.
|
||||
*
|
||||
* @return true if this is zero bytes long
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteString -> substring
|
||||
|
||||
/**
|
||||
* Return the substring from {@code beginIndex}, inclusive, to the end of the
|
||||
* string.
|
||||
*
|
||||
* @param beginIndex start at this index
|
||||
* @return substring sharing underlying data
|
||||
* @throws IndexOutOfBoundsException if {@code beginIndex < 0} or
|
||||
* {@code beginIndex > size()}.
|
||||
*/
|
||||
public ByteString substring(int beginIndex) {
|
||||
return substring(beginIndex, size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the substring from {@code beginIndex}, inclusive, to {@code
|
||||
* endIndex}, exclusive.
|
||||
*
|
||||
* @param beginIndex start at this index
|
||||
* @param endIndex the last character is the one before this index
|
||||
* @return substring sharing underlying data
|
||||
* @throws IndexOutOfBoundsException if {@code beginIndex < 0},
|
||||
* {@code endIndex > size()}, or {@code beginIndex > endIndex}.
|
||||
*/
|
||||
public abstract ByteString substring(int beginIndex, int endIndex);
|
||||
|
||||
/**
|
||||
* Tests if this bytestring starts with the specified prefix.
|
||||
* Similar to {@link String#startsWith(String)}
|
||||
*
|
||||
* @param prefix the prefix.
|
||||
* @return <code>true</code> if the byte sequence represented by the
|
||||
* argument is a prefix of the byte sequence represented by
|
||||
* this string; <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean startsWith(ByteString prefix) {
|
||||
return size() >= prefix.size() &&
|
||||
substring(0, prefix.size()).equals(prefix);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// byte[] -> ByteString
|
||||
|
||||
/**
|
||||
* Copies the given bytes into a {@code ByteString}.
|
||||
*
|
||||
* @param bytes source array
|
||||
* @param offset offset in source array
|
||||
* @param size number of bytes to copy
|
||||
* @return new {@code ByteString}
|
||||
*/
|
||||
public static ByteString copyFrom(byte[] bytes, int offset, int size) {
|
||||
byte[] copy = new byte[size];
|
||||
System.arraycopy(bytes, offset, copy, 0, size);
|
||||
return new LiteralByteString(copy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the given bytes into a {@code ByteString}.
|
||||
*
|
||||
* @param bytes to copy
|
||||
* @return new {@code ByteString}
|
||||
*/
|
||||
public static ByteString copyFrom(byte[] bytes) {
|
||||
return copyFrom(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the next {@code size} bytes from a {@code java.nio.ByteBuffer} into
|
||||
* a {@code ByteString}.
|
||||
*
|
||||
* @param bytes source buffer
|
||||
* @param size number of bytes to copy
|
||||
* @return new {@code ByteString}
|
||||
*/
|
||||
public static ByteString copyFrom(ByteBuffer bytes, int size) {
|
||||
byte[] copy = new byte[size];
|
||||
bytes.get(copy);
|
||||
return new LiteralByteString(copy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the remaining bytes from a {@code java.nio.ByteBuffer} into
|
||||
* a {@code ByteString}.
|
||||
*
|
||||
* @param bytes sourceBuffer
|
||||
* @return new {@code ByteString}
|
||||
*/
|
||||
public static ByteString copyFrom(ByteBuffer bytes) {
|
||||
return copyFrom(bytes, bytes.remaining());
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes {@code text} into a sequence of bytes using the named charset
|
||||
* and returns the result as a {@code ByteString}.
|
||||
*
|
||||
* @param text source string
|
||||
* @param charsetName encoding to use
|
||||
* @return new {@code ByteString}
|
||||
* @throws UnsupportedEncodingException if the encoding isn't found
|
||||
*/
|
||||
public static ByteString copyFrom(String text, String charsetName)
|
||||
throws UnsupportedEncodingException {
|
||||
return new LiteralByteString(text.getBytes(charsetName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes {@code text} into a sequence of UTF-8 bytes and returns the
|
||||
* result as a {@code ByteString}.
|
||||
*
|
||||
* @param text source string
|
||||
* @return new {@code ByteString}
|
||||
*/
|
||||
public static ByteString copyFromUtf8(String text) {
|
||||
try {
|
||||
return new LiteralByteString(text.getBytes("UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException("UTF-8 not supported?", e);
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// InputStream -> ByteString
|
||||
|
||||
/**
|
||||
* Completely reads the given stream's bytes into a
|
||||
* {@code ByteString}, blocking if necessary until all bytes are
|
||||
* read through to the end of the stream.
|
||||
*
|
||||
* <b>Performance notes:</b> The returned {@code ByteString} is an
|
||||
* immutable tree of byte arrays ("chunks") of the stream data. The
|
||||
* first chunk is small, with subsequent chunks each being double
|
||||
* the size, up to 8K. If the caller knows the precise length of
|
||||
* the stream and wishes to avoid all unnecessary copies and
|
||||
* allocations, consider using the two-argument version of this
|
||||
* method, below.
|
||||
*
|
||||
* @param streamToDrain The source stream, which is read completely
|
||||
* but not closed.
|
||||
* @return A new {@code ByteString} which is made up of chunks of
|
||||
* various sizes, depending on the behavior of the underlying
|
||||
* stream.
|
||||
* @throws IOException IOException is thrown if there is a problem
|
||||
* reading the underlying stream.
|
||||
*/
|
||||
public static ByteString readFrom(InputStream streamToDrain)
|
||||
throws IOException {
|
||||
return readFrom(
|
||||
streamToDrain, MIN_READ_FROM_CHUNK_SIZE, MAX_READ_FROM_CHUNK_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Completely reads the given stream's bytes into a
|
||||
* {@code ByteString}, blocking if necessary until all bytes are
|
||||
* read through to the end of the stream.
|
||||
*
|
||||
* <b>Performance notes:</b> The returned {@code ByteString} is an
|
||||
* immutable tree of byte arrays ("chunks") of the stream data. The
|
||||
* chunkSize parameter sets the size of these byte arrays. In
|
||||
* particular, if the chunkSize is precisely the same as the length
|
||||
* of the stream, unnecessary allocations and copies will be
|
||||
* avoided. Otherwise, the chunks will be of the given size, except
|
||||
* for the last chunk, which will be resized (via a reallocation and
|
||||
* copy) to contain the remainder of the stream.
|
||||
*
|
||||
* @param streamToDrain The source stream, which is read completely
|
||||
* but not closed.
|
||||
* @param chunkSize The size of the chunks in which to read the
|
||||
* stream.
|
||||
* @return A new {@code ByteString} which is made up of chunks of
|
||||
* the given size.
|
||||
* @throws IOException IOException is thrown if there is a problem
|
||||
* reading the underlying stream.
|
||||
*/
|
||||
public static ByteString readFrom(InputStream streamToDrain, int chunkSize)
|
||||
throws IOException {
|
||||
return readFrom(streamToDrain, chunkSize, chunkSize);
|
||||
}
|
||||
|
||||
// Helper method that takes the chunk size range as a parameter.
|
||||
public static ByteString readFrom(InputStream streamToDrain, int minChunkSize,
|
||||
int maxChunkSize) throws IOException {
|
||||
Collection<ByteString> results = new ArrayList<ByteString>();
|
||||
|
||||
// copy the inbound bytes into a list of chunks; the chunk size
|
||||
// grows exponentially to support both short and long streams.
|
||||
int chunkSize = minChunkSize;
|
||||
while (true) {
|
||||
ByteString chunk = readChunk(streamToDrain, chunkSize);
|
||||
if (chunk == null) {
|
||||
break;
|
||||
}
|
||||
results.add(chunk);
|
||||
chunkSize = Math.min(chunkSize * 2, maxChunkSize);
|
||||
}
|
||||
|
||||
return ByteString.copyFrom(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks until a chunk of the given size can be made from the
|
||||
* stream, or EOF is reached. Calls read() repeatedly in case the
|
||||
* given stream implementation doesn't completely fill the given
|
||||
* buffer in one read() call.
|
||||
*
|
||||
* @return A chunk of the desired size, or else a chunk as large as
|
||||
* was available when end of stream was reached. Returns null if the
|
||||
* given stream had no more data in it.
|
||||
*/
|
||||
private static ByteString readChunk(InputStream in, final int chunkSize)
|
||||
throws IOException {
|
||||
final byte[] buf = new byte[chunkSize];
|
||||
int bytesRead = 0;
|
||||
while (bytesRead < chunkSize) {
|
||||
final int count = in.read(buf, bytesRead, chunkSize - bytesRead);
|
||||
if (count == -1) {
|
||||
break;
|
||||
}
|
||||
bytesRead += count;
|
||||
}
|
||||
|
||||
if (bytesRead == 0) {
|
||||
return null;
|
||||
} else {
|
||||
return ByteString.copyFrom(buf, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Multiple ByteStrings -> One ByteString
|
||||
|
||||
/**
|
||||
* Concatenate the given {@code ByteString} to this one. Short concatenations,
|
||||
* of total size smaller than {@link ByteString#CONCATENATE_BY_COPY_SIZE}, are
|
||||
* produced by copying the underlying bytes (as per Rope.java, <a
|
||||
* href="http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol25/issue12/spe986.pdf">
|
||||
* BAP95 </a>. In general, the concatenate involves no copying.
|
||||
*
|
||||
* @param other string to concatenate
|
||||
* @return a new {@code ByteString} instance
|
||||
*/
|
||||
public ByteString concat(ByteString other) {
|
||||
int thisSize = size();
|
||||
int otherSize = other.size();
|
||||
if ((long) thisSize + otherSize >= Integer.MAX_VALUE) {
|
||||
throw new IllegalArgumentException("ByteString would be too long: " +
|
||||
thisSize + "+" + otherSize);
|
||||
}
|
||||
|
||||
return RopeByteString.concatenate(this, other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates all byte strings in the iterable and returns the result.
|
||||
* This is designed to run in O(list size), not O(total bytes).
|
||||
*
|
||||
* <p>The returned {@code ByteString} is not necessarily a unique object.
|
||||
* If the list is empty, the returned object is the singleton empty
|
||||
* {@code ByteString}. If the list has only one element, that
|
||||
* {@code ByteString} will be returned without copying.
|
||||
*
|
||||
* @param byteStrings strings to be concatenated
|
||||
* @return new {@code ByteString}
|
||||
*/
|
||||
public static ByteString copyFrom(Iterable<ByteString> byteStrings) {
|
||||
Collection<ByteString> collection;
|
||||
if (!(byteStrings instanceof Collection)) {
|
||||
collection = new ArrayList<ByteString>();
|
||||
for (ByteString byteString : byteStrings) {
|
||||
collection.add(byteString);
|
||||
}
|
||||
} else {
|
||||
collection = (Collection<ByteString>) byteStrings;
|
||||
}
|
||||
ByteString result;
|
||||
if (collection.isEmpty()) {
|
||||
result = EMPTY;
|
||||
} else {
|
||||
result = balancedConcat(collection.iterator(), collection.size());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Internal function used by copyFrom(Iterable<ByteString>).
|
||||
// Create a balanced concatenation of the next "length" elements from the
|
||||
// iterable.
|
||||
private static ByteString balancedConcat(Iterator<ByteString> iterator,
|
||||
int length) {
|
||||
assert length >= 1;
|
||||
ByteString result;
|
||||
if (length == 1) {
|
||||
result = iterator.next();
|
||||
} else {
|
||||
int halfLength = length >>> 1;
|
||||
ByteString left = balancedConcat(iterator, halfLength);
|
||||
ByteString right = balancedConcat(iterator, length - halfLength);
|
||||
result = left.concat(right);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteString -> byte[]
|
||||
|
||||
/**
|
||||
* Copies bytes into a buffer at the given offset.
|
||||
*
|
||||
* @param target buffer to copy into
|
||||
* @param offset in the target buffer
|
||||
* @throws IndexOutOfBoundsException if the offset is negative or too large
|
||||
*/
|
||||
public void copyTo(byte[] target, int offset) {
|
||||
copyTo(target, 0, offset, size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies bytes into a buffer.
|
||||
*
|
||||
* @param target buffer to copy into
|
||||
* @param sourceOffset offset within these bytes
|
||||
* @param targetOffset offset within the target buffer
|
||||
* @param numberToCopy number of bytes to copy
|
||||
* @throws IndexOutOfBoundsException if an offset or size is negative or too
|
||||
* large
|
||||
*/
|
||||
public void copyTo(byte[] target, int sourceOffset, int targetOffset,
|
||||
int numberToCopy) {
|
||||
if (sourceOffset < 0) {
|
||||
throw new IndexOutOfBoundsException("Source offset < 0: " + sourceOffset);
|
||||
}
|
||||
if (targetOffset < 0) {
|
||||
throw new IndexOutOfBoundsException("Target offset < 0: " + targetOffset);
|
||||
}
|
||||
if (numberToCopy < 0) {
|
||||
throw new IndexOutOfBoundsException("Length < 0: " + numberToCopy);
|
||||
}
|
||||
if (sourceOffset + numberToCopy > size()) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Source end offset < 0: " + (sourceOffset + numberToCopy));
|
||||
}
|
||||
if (targetOffset + numberToCopy > target.length) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Target end offset < 0: " + (targetOffset + numberToCopy));
|
||||
}
|
||||
if (numberToCopy > 0) {
|
||||
copyToInternal(target, sourceOffset, targetOffset, numberToCopy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal (package private) implementation of
|
||||
* @link{#copyTo(byte[],int,int,int}.
|
||||
* It assumes that all error checking has already been performed and that
|
||||
* @code{numberToCopy > 0}.
|
||||
*/
|
||||
protected abstract void copyToInternal(byte[] target, int sourceOffset,
|
||||
int targetOffset, int numberToCopy);
|
||||
|
||||
/**
|
||||
* Copies bytes into a ByteBuffer.
|
||||
*
|
||||
* @param target ByteBuffer to copy into.
|
||||
* @throws java.nio.ReadOnlyBufferException if the {@code target} is read-only
|
||||
* @throws java.nio.BufferOverflowException if the {@code target}'s
|
||||
* remaining() space is not large enough to hold the data.
|
||||
*/
|
||||
public abstract void copyTo(ByteBuffer target);
|
||||
|
||||
/**
|
||||
* Copies bytes to a {@code byte[]}.
|
||||
*
|
||||
* @return copied bytes
|
||||
*/
|
||||
public byte[] toByteArray() {
|
||||
int size = size();
|
||||
byte[] result = new byte[size];
|
||||
copyToInternal(result, 0, 0, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the complete contents of this byte string to
|
||||
* the specified output stream argument.
|
||||
*
|
||||
* @param out the output stream to which to write the data.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public abstract void writeTo(OutputStream out) throws IOException;
|
||||
|
||||
/**
|
||||
* Constructs a read-only {@code java.nio.ByteBuffer} whose content
|
||||
* is equal to the contents of this byte string.
|
||||
* The result uses the same backing array as the byte string, if possible.
|
||||
*
|
||||
* @return wrapped bytes
|
||||
*/
|
||||
public abstract ByteBuffer asReadOnlyByteBuffer();
|
||||
|
||||
/**
|
||||
* Constructs a list of read-only {@code java.nio.ByteBuffer} objects
|
||||
* such that the concatenation of their contents is equal to the contents
|
||||
* of this byte string. The result uses the same backing arrays as the
|
||||
* byte string.
|
||||
* <p>
|
||||
* By returning a list, implementations of this method may be able to avoid
|
||||
* copying even when there are multiple backing arrays.
|
||||
*
|
||||
* @return a list of wrapped bytes
|
||||
*/
|
||||
public abstract List<ByteBuffer> asReadOnlyByteBufferList();
|
||||
|
||||
/**
|
||||
* Constructs a new {@code String} by decoding the bytes using the
|
||||
* specified charset.
|
||||
*
|
||||
* @param charsetName encode using this charset
|
||||
* @return new string
|
||||
* @throws UnsupportedEncodingException if charset isn't recognized
|
||||
*/
|
||||
public abstract String toString(String charsetName)
|
||||
throws UnsupportedEncodingException;
|
||||
|
||||
// =================================================================
|
||||
// UTF-8 decoding
|
||||
|
||||
/**
|
||||
* Constructs a new {@code String} by decoding the bytes as UTF-8.
|
||||
*
|
||||
* @return new string using UTF-8 encoding
|
||||
*/
|
||||
public String toStringUtf8() {
|
||||
try {
|
||||
return toString("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException("UTF-8 not supported?", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether this {@code ByteString} represents a well-formed UTF-8
|
||||
* byte sequence, such that the original bytes can be converted to a
|
||||
* String object and then round tripped back to bytes without loss.
|
||||
*
|
||||
* <p>More precisely, returns {@code true} whenever: <pre> {@code
|
||||
* Arrays.equals(byteString.toByteArray(),
|
||||
* new String(byteString.toByteArray(), "UTF-8").getBytes("UTF-8"))
|
||||
* }</pre>
|
||||
*
|
||||
* <p>This method returns {@code false} for "overlong" byte sequences,
|
||||
* as well as for 3-byte sequences that would map to a surrogate
|
||||
* character, in accordance with the restricted definition of UTF-8
|
||||
* introduced in Unicode 3.1. Note that the UTF-8 decoder included in
|
||||
* Oracle's JDK has been modified to also reject "overlong" byte
|
||||
* sequences, but (as of 2011) still accepts 3-byte surrogate
|
||||
* character byte sequences.
|
||||
*
|
||||
* <p>See the Unicode Standard,</br>
|
||||
* Table 3-6. <em>UTF-8 Bit Distribution</em>,</br>
|
||||
* Table 3-7. <em>Well Formed UTF-8 Byte Sequences</em>.
|
||||
*
|
||||
* @return whether the bytes in this {@code ByteString} are a
|
||||
* well-formed UTF-8 byte sequence
|
||||
*/
|
||||
public abstract boolean isValidUtf8();
|
||||
|
||||
/**
|
||||
* Tells whether the given byte sequence is a well-formed, malformed, or
|
||||
* incomplete UTF-8 byte sequence. This method accepts and returns a partial
|
||||
* state result, allowing the bytes for a complete UTF-8 byte sequence to be
|
||||
* composed from multiple {@code ByteString} segments.
|
||||
*
|
||||
* @param state either {@code 0} (if this is the initial decoding operation)
|
||||
* or the value returned from a call to a partial decoding method for the
|
||||
* previous bytes
|
||||
* @param offset offset of the first byte to check
|
||||
* @param length number of bytes to check
|
||||
*
|
||||
* @return {@code -1} if the partial byte sequence is definitely malformed,
|
||||
* {@code 0} if it is well-formed (no additional input needed), or, if the
|
||||
* byte sequence is "incomplete", i.e. apparently terminated in the middle of
|
||||
* a character, an opaque integer "state" value containing enough information
|
||||
* to decode the character when passed to a subsequent invocation of a
|
||||
* partial decoding method.
|
||||
*/
|
||||
protected abstract int partialIsValidUtf8(int state, int offset, int length);
|
||||
|
||||
// =================================================================
|
||||
// equals() and hashCode()
|
||||
|
||||
@Override
|
||||
public abstract boolean equals(Object o);
|
||||
|
||||
/**
|
||||
* Return a non-zero hashCode depending only on the sequence of bytes
|
||||
* in this ByteString.
|
||||
*
|
||||
* @return hashCode value for this object
|
||||
*/
|
||||
@Override
|
||||
public abstract int hashCode();
|
||||
|
||||
// =================================================================
|
||||
// Input stream
|
||||
|
||||
/**
|
||||
* Creates an {@code InputStream} which can be used to read the bytes.
|
||||
* <p>
|
||||
* The {@link InputStream} returned by this method is guaranteed to be
|
||||
* completely non-blocking. The method {@link InputStream#available()}
|
||||
* returns the number of bytes remaining in the stream. The methods
|
||||
* {@link InputStream#read(byte[]), {@link InputStream#read(byte[],int,int)}
|
||||
* and {@link InputStream#skip(long)} will read/skip as many bytes as are
|
||||
* available.
|
||||
* <p>
|
||||
* The methods in the returned {@link InputStream} might <b>not</b> be
|
||||
* thread safe.
|
||||
*
|
||||
* @return an input stream that returns the bytes of this byte string.
|
||||
*/
|
||||
public abstract InputStream newInput();
|
||||
|
||||
/**
|
||||
* Creates a {@link CodedInputStream} which can be used to read the bytes.
|
||||
* Using this is often more efficient than creating a {@link CodedInputStream}
|
||||
* that wraps the result of {@link #newInput()}.
|
||||
*
|
||||
* @return stream based on wrapped data
|
||||
*/
|
||||
public abstract CodedInputStream newCodedInput();
|
||||
|
||||
// =================================================================
|
||||
// Output stream
|
||||
|
||||
/**
|
||||
* Creates a new {@link Output} with the given initial capacity. Call {@link
|
||||
* Output#toByteString()} to create the {@code ByteString} instance.
|
||||
* <p>
|
||||
* A {@link ByteString.Output} offers the same functionality as a
|
||||
* {@link ByteArrayOutputStream}, except that it returns a {@link ByteString}
|
||||
* rather than a {@code byte} array.
|
||||
*
|
||||
* @param initialCapacity estimate of number of bytes to be written
|
||||
* @return {@code OutputStream} for building a {@code ByteString}
|
||||
*/
|
||||
public static Output newOutput(int initialCapacity) {
|
||||
return new Output(initialCapacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Output}. Call {@link Output#toByteString()} to create
|
||||
* the {@code ByteString} instance.
|
||||
* <p>
|
||||
* A {@link ByteString.Output} offers the same functionality as a
|
||||
* {@link ByteArrayOutputStream}, except that it returns a {@link ByteString}
|
||||
* rather than a {@code byte array}.
|
||||
*
|
||||
* @return {@code OutputStream} for building a {@code ByteString}
|
||||
*/
|
||||
public static Output newOutput() {
|
||||
return new Output(CONCATENATE_BY_COPY_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs to a {@code ByteString} instance. Call {@link #toByteString()} to
|
||||
* create the {@code ByteString} instance.
|
||||
*/
|
||||
public static final class Output extends OutputStream {
|
||||
// Implementation note.
|
||||
// The public methods of this class must be synchronized. ByteStrings
|
||||
// are guaranteed to be immutable. Without some sort of locking, it could
|
||||
// be possible for one thread to call toByteSring(), while another thread
|
||||
// is still modifying the underlying byte array.
|
||||
|
||||
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
|
||||
// argument passed by user, indicating initial capacity.
|
||||
private final int initialCapacity;
|
||||
// ByteStrings to be concatenated to create the result
|
||||
private final ArrayList<ByteString> flushedBuffers;
|
||||
// Total number of bytes in the ByteStrings of flushedBuffers
|
||||
private int flushedBuffersTotalBytes;
|
||||
// Current buffer to which we are writing
|
||||
private byte[] buffer;
|
||||
// Location in buffer[] to which we write the next byte.
|
||||
private int bufferPos;
|
||||
|
||||
/**
|
||||
* Creates a new ByteString output stream with the specified
|
||||
* initial capacity.
|
||||
*
|
||||
* @param initialCapacity the initial capacity of the output stream.
|
||||
*/
|
||||
Output(int initialCapacity) {
|
||||
if (initialCapacity < 0) {
|
||||
throw new IllegalArgumentException("Buffer size < 0");
|
||||
}
|
||||
this.initialCapacity = initialCapacity;
|
||||
this.flushedBuffers = new ArrayList<ByteString>();
|
||||
this.buffer = new byte[initialCapacity];
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(int b) {
|
||||
if (bufferPos == buffer.length) {
|
||||
flushFullBuffer(1);
|
||||
}
|
||||
buffer[bufferPos++] = (byte)b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(byte[] b, int offset, int length) {
|
||||
if (length <= buffer.length - bufferPos) {
|
||||
// The bytes can fit into the current buffer.
|
||||
System.arraycopy(b, offset, buffer, bufferPos, length);
|
||||
bufferPos += length;
|
||||
} else {
|
||||
// Use up the current buffer
|
||||
int copySize = buffer.length - bufferPos;
|
||||
System.arraycopy(b, offset, buffer, bufferPos, copySize);
|
||||
offset += copySize;
|
||||
length -= copySize;
|
||||
// Flush the buffer, and get a new buffer at least big enough to cover
|
||||
// what we still need to output
|
||||
flushFullBuffer(length);
|
||||
System.arraycopy(b, offset, buffer, 0 /* count */, length);
|
||||
bufferPos = length;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a byte string. Its size is the current size of this output
|
||||
* stream and its output has been copied to it.
|
||||
*
|
||||
* @return the current contents of this output stream, as a byte string.
|
||||
*/
|
||||
public synchronized ByteString toByteString() {
|
||||
flushLastBuffer();
|
||||
return ByteString.copyFrom(flushedBuffers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement java.util.Arrays.copyOf() for jdk 1.5.
|
||||
*/
|
||||
private byte[] copyArray(byte[] buffer, int length) {
|
||||
byte[] result = new byte[length];
|
||||
System.arraycopy(buffer, 0, result, 0, Math.min(buffer.length, length));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the complete contents of this byte array output stream to
|
||||
* the specified output stream argument.
|
||||
*
|
||||
* @param out the output stream to which to write the data.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public void writeTo(OutputStream out) throws IOException {
|
||||
ByteString[] cachedFlushBuffers;
|
||||
byte[] cachedBuffer;
|
||||
int cachedBufferPos;
|
||||
synchronized (this) {
|
||||
// Copy the information we need into local variables so as to hold
|
||||
// the lock for as short a time as possible.
|
||||
cachedFlushBuffers =
|
||||
flushedBuffers.toArray(new ByteString[flushedBuffers.size()]);
|
||||
cachedBuffer = buffer;
|
||||
cachedBufferPos = bufferPos;
|
||||
}
|
||||
for (ByteString byteString : cachedFlushBuffers) {
|
||||
byteString.writeTo(out);
|
||||
}
|
||||
|
||||
out.write(copyArray(cachedBuffer, cachedBufferPos));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current size of the output stream.
|
||||
*
|
||||
* @return the current size of the output stream
|
||||
*/
|
||||
public synchronized int size() {
|
||||
return flushedBuffersTotalBytes + bufferPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets this stream, so that all currently accumulated output in the
|
||||
* output stream is discarded. The output stream can be used again,
|
||||
* reusing the already allocated buffer space.
|
||||
*/
|
||||
public synchronized void reset() {
|
||||
flushedBuffers.clear();
|
||||
flushedBuffersTotalBytes = 0;
|
||||
bufferPos = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("<ByteString.Output@%s size=%d>",
|
||||
Integer.toHexString(System.identityHashCode(this)), size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function used by writers. The current buffer is full, and the
|
||||
* writer needs a new buffer whose size is at least the specified minimum
|
||||
* size.
|
||||
*/
|
||||
private void flushFullBuffer(int minSize) {
|
||||
flushedBuffers.add(new LiteralByteString(buffer));
|
||||
flushedBuffersTotalBytes += buffer.length;
|
||||
// We want to increase our total capacity by 50%, but as a minimum,
|
||||
// the new buffer should also at least be >= minSize and
|
||||
// >= initial Capacity.
|
||||
int newSize = Math.max(initialCapacity,
|
||||
Math.max(minSize, flushedBuffersTotalBytes >>> 1));
|
||||
buffer = new byte[newSize];
|
||||
bufferPos = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function used by {@link #toByteString()}. The current buffer may
|
||||
* or may not be full, but it needs to be flushed.
|
||||
*/
|
||||
private void flushLastBuffer() {
|
||||
if (bufferPos < buffer.length) {
|
||||
if (bufferPos > 0) {
|
||||
byte[] bufferCopy = copyArray(buffer, bufferPos);
|
||||
flushedBuffers.add(new LiteralByteString(bufferCopy));
|
||||
}
|
||||
// We reuse this buffer for further writes.
|
||||
} else {
|
||||
// Buffer is completely full. Huzzah.
|
||||
flushedBuffers.add(new LiteralByteString(buffer));
|
||||
// 99% of the time, we're not going to use this OutputStream again.
|
||||
// We set buffer to an empty byte stream so that we're handling this
|
||||
// case without wasting space. In the rare case that more writes
|
||||
// *do* occur, this empty buffer will be flushed and an appropriately
|
||||
// sized new buffer will be created.
|
||||
buffer = EMPTY_BYTE_ARRAY;
|
||||
}
|
||||
flushedBuffersTotalBytes += bufferPos;
|
||||
bufferPos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code ByteString} builder, which allows you to
|
||||
* efficiently construct a {@code ByteString} by writing to a {@link
|
||||
* CodedOutputStream}. Using this is much more efficient than calling {@code
|
||||
* newOutput()} and wrapping that in a {@code CodedOutputStream}.
|
||||
*
|
||||
* <p>This is package-private because it's a somewhat confusing interface.
|
||||
* Users can call {@link Message#toByteString()} instead of calling this
|
||||
* directly.
|
||||
*
|
||||
* @param size The target byte size of the {@code ByteString}. You must write
|
||||
* exactly this many bytes before building the result.
|
||||
* @return the builder
|
||||
*/
|
||||
static CodedBuilder newCodedBuilder(int size) {
|
||||
return new CodedBuilder(size);
|
||||
}
|
||||
|
||||
/** See {@link ByteString#newCodedBuilder(int)}. */
|
||||
static final class CodedBuilder {
|
||||
private final CodedOutputStream output;
|
||||
private final byte[] buffer;
|
||||
|
||||
private CodedBuilder(int size) {
|
||||
buffer = new byte[size];
|
||||
output = CodedOutputStream.newInstance(buffer);
|
||||
}
|
||||
|
||||
public ByteString build() {
|
||||
output.checkNoSpaceLeft();
|
||||
|
||||
// We can be confident that the CodedOutputStream will not modify the
|
||||
// underlying bytes anymore because it already wrote all of them. So,
|
||||
// no need to make a copy.
|
||||
return new LiteralByteString(buffer);
|
||||
}
|
||||
|
||||
public CodedOutputStream getCodedOutput() {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Methods {@link RopeByteString} needs on instances, which aren't part of the
|
||||
// public API.
|
||||
|
||||
/**
|
||||
* Return the depth of the tree representing this {@code ByteString}, if any,
|
||||
* whose root is this node. If this is a leaf node, return 0.
|
||||
*
|
||||
* @return tree depth or zero
|
||||
*/
|
||||
protected abstract int getTreeDepth();
|
||||
|
||||
/**
|
||||
* Return {@code true} if this ByteString is literal (a leaf node) or a
|
||||
* flat-enough tree in the sense of {@link RopeByteString}.
|
||||
*
|
||||
* @return true if the tree is flat enough
|
||||
*/
|
||||
protected abstract boolean isBalanced();
|
||||
|
||||
/**
|
||||
* Return the cached hash code if available.
|
||||
*
|
||||
* @return value of cached hash code or 0 if not computed yet
|
||||
*/
|
||||
protected abstract int peekCachedHashCode();
|
||||
|
||||
/**
|
||||
* Compute the hash across the value bytes starting with the given hash, and
|
||||
* return the result. This is used to compute the hash across strings
|
||||
* represented as a set of pieces by allowing the hash computation to be
|
||||
* continued from piece to piece.
|
||||
*
|
||||
* @param h starting hash value
|
||||
* @param offset offset into this value to start looking at data values
|
||||
* @param length number of data values to include in the hash computation
|
||||
* @return ending hash value
|
||||
*/
|
||||
protected abstract int partialHash(int h, int offset, int length);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("<ByteString@%s size=%d>",
|
||||
Integer.toHexString(System.identityHashCode(this)), size());
|
||||
}
|
||||
}
|
||||
@@ -1,920 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Reads and decodes protocol message fields.
|
||||
*
|
||||
* This class contains two kinds of methods: methods that read specific
|
||||
* protocol message constructs and field types (e.g. {@link #readTag()} and
|
||||
* {@link #readInt32()}) and methods that read low-level values (e.g.
|
||||
* {@link #readRawVarint32()} and {@link #readRawBytes}). If you are reading
|
||||
* encoded protocol messages, you should use the former methods, but if you are
|
||||
* reading some other format of your own design, use the latter.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public final class CodedInputStream {
|
||||
/**
|
||||
* Create a new CodedInputStream wrapping the given InputStream.
|
||||
*/
|
||||
public static CodedInputStream newInstance(final InputStream input) {
|
||||
return new CodedInputStream(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new CodedInputStream wrapping the given byte array.
|
||||
*/
|
||||
public static CodedInputStream newInstance(final byte[] buf) {
|
||||
return newInstance(buf, 0, buf.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new CodedInputStream wrapping the given byte array slice.
|
||||
*/
|
||||
public static CodedInputStream newInstance(final byte[] buf, final int off,
|
||||
final int 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;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Attempt to read a field tag, returning zero if we have reached EOF.
|
||||
* Protocol message parsers use this to read tags, since a protocol message
|
||||
* may legally end wherever a tag occurs, and zero is not a valid tag number.
|
||||
*/
|
||||
public int readTag() throws IOException {
|
||||
if (isAtEnd()) {
|
||||
lastTag = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
lastTag = readRawVarint32();
|
||||
if (WireFormat.getTagFieldNumber(lastTag) == 0) {
|
||||
// If we actually read zero (or any tag number corresponding to field
|
||||
// number zero), that's not a valid tag.
|
||||
throw InvalidProtocolBufferException.invalidTag();
|
||||
}
|
||||
return lastTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the last call to readTag() returned the given tag value.
|
||||
* This is used to verify that a nested group ended with the correct
|
||||
* end tag.
|
||||
*
|
||||
* @throws InvalidProtocolBufferException {@code value} does not match the
|
||||
* last tag.
|
||||
*/
|
||||
public void checkLastTagWas(final int value)
|
||||
throws InvalidProtocolBufferException {
|
||||
if (lastTag != value) {
|
||||
throw InvalidProtocolBufferException.invalidEndTag();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and discards a single field, given its tag value.
|
||||
*
|
||||
* @return {@code false} if the tag is an endgroup tag, in which case
|
||||
* nothing is skipped. Otherwise, returns {@code true}.
|
||||
*/
|
||||
public boolean skipField(final int tag) throws IOException {
|
||||
switch (WireFormat.getTagWireType(tag)) {
|
||||
case WireFormat.WIRETYPE_VARINT:
|
||||
readInt32();
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_FIXED64:
|
||||
readRawLittleEndian64();
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_LENGTH_DELIMITED:
|
||||
skipRawBytes(readRawVarint32());
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_START_GROUP:
|
||||
skipMessage();
|
||||
checkLastTagWas(
|
||||
WireFormat.makeTag(WireFormat.getTagFieldNumber(tag),
|
||||
WireFormat.WIRETYPE_END_GROUP));
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_END_GROUP:
|
||||
return false;
|
||||
case WireFormat.WIRETYPE_FIXED32:
|
||||
readRawLittleEndian32();
|
||||
return true;
|
||||
default:
|
||||
throw InvalidProtocolBufferException.invalidWireType();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and discards an entire message. This will read either until EOF
|
||||
* or until an endgroup tag, whichever comes first.
|
||||
*/
|
||||
public void skipMessage() throws IOException {
|
||||
while (true) {
|
||||
final int tag = readTag();
|
||||
if (tag == 0 || !skipField(tag)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
/** Read a {@code double} field value from the stream. */
|
||||
public double readDouble() throws IOException {
|
||||
return Double.longBitsToDouble(readRawLittleEndian64());
|
||||
}
|
||||
|
||||
/** Read a {@code float} field value from the stream. */
|
||||
public float readFloat() throws IOException {
|
||||
return Float.intBitsToFloat(readRawLittleEndian32());
|
||||
}
|
||||
|
||||
/** Read a {@code uint64} field value from the stream. */
|
||||
public long readUInt64() throws IOException {
|
||||
return readRawVarint64();
|
||||
}
|
||||
|
||||
/** Read an {@code int64} field value from the stream. */
|
||||
public long readInt64() throws IOException {
|
||||
return readRawVarint64();
|
||||
}
|
||||
|
||||
/** Read an {@code int32} field value from the stream. */
|
||||
public int readInt32() throws IOException {
|
||||
return readRawVarint32();
|
||||
}
|
||||
|
||||
/** Read a {@code fixed64} field value from the stream. */
|
||||
public long readFixed64() throws IOException {
|
||||
return readRawLittleEndian64();
|
||||
}
|
||||
|
||||
/** Read a {@code fixed32} field value from the stream. */
|
||||
public int readFixed32() throws IOException {
|
||||
return readRawLittleEndian32();
|
||||
}
|
||||
|
||||
/** Read a {@code bool} field value from the stream. */
|
||||
public boolean readBool() throws IOException {
|
||||
return readRawVarint32() != 0;
|
||||
}
|
||||
|
||||
/** Read a {@code string} field value from the stream. */
|
||||
public String readString() throws IOException {
|
||||
final int size = readRawVarint32();
|
||||
if (size <= (bufferSize - bufferPos) && size > 0) {
|
||||
// Fast path: We already have the bytes in a contiguous buffer, so
|
||||
// just copy directly from it.
|
||||
final String result = new String(buffer, bufferPos, size, "UTF-8");
|
||||
bufferPos += size;
|
||||
return result;
|
||||
} else {
|
||||
// Slow path: Build a byte array first then copy it.
|
||||
return new String(readRawBytes(size), "UTF-8");
|
||||
}
|
||||
}
|
||||
|
||||
/** Read a {@code group} field value from the stream. */
|
||||
public void readGroup(final int fieldNumber,
|
||||
final MessageLite.Builder builder,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
if (recursionDepth >= recursionLimit) {
|
||||
throw InvalidProtocolBufferException.recursionLimitExceeded();
|
||||
}
|
||||
++recursionDepth;
|
||||
builder.mergeFrom(this, extensionRegistry);
|
||||
checkLastTagWas(
|
||||
WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP));
|
||||
--recursionDepth;
|
||||
}
|
||||
|
||||
/** Read a {@code group} field value from the stream. */
|
||||
public <T extends MessageLite> T readGroup(
|
||||
final int fieldNumber,
|
||||
final Parser<T> parser,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
if (recursionDepth >= recursionLimit) {
|
||||
throw InvalidProtocolBufferException.recursionLimitExceeded();
|
||||
}
|
||||
++recursionDepth;
|
||||
T result = parser.parsePartialFrom(this, extensionRegistry);
|
||||
checkLastTagWas(
|
||||
WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP));
|
||||
--recursionDepth;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a {@code group} field value from the stream and merges it into the
|
||||
* given {@link UnknownFieldSet}.
|
||||
*
|
||||
* @deprecated UnknownFieldSet.Builder now implements MessageLite.Builder, so
|
||||
* you can just call {@link #readGroup}.
|
||||
*/
|
||||
@Deprecated
|
||||
public void readUnknownGroup(final int fieldNumber,
|
||||
final MessageLite.Builder builder)
|
||||
throws IOException {
|
||||
// We know that UnknownFieldSet will ignore any ExtensionRegistry so it
|
||||
// is safe to pass null here. (We can't call
|
||||
// ExtensionRegistry.getEmptyRegistry() because that would make this
|
||||
// class depend on ExtensionRegistry, which is not part of the lite
|
||||
// library.)
|
||||
readGroup(fieldNumber, builder, null);
|
||||
}
|
||||
|
||||
/** Read an embedded message field value from the stream. */
|
||||
public void readMessage(final MessageLite.Builder builder,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
final int length = readRawVarint32();
|
||||
if (recursionDepth >= recursionLimit) {
|
||||
throw InvalidProtocolBufferException.recursionLimitExceeded();
|
||||
}
|
||||
final int oldLimit = pushLimit(length);
|
||||
++recursionDepth;
|
||||
builder.mergeFrom(this, extensionRegistry);
|
||||
checkLastTagWas(0);
|
||||
--recursionDepth;
|
||||
popLimit(oldLimit);
|
||||
}
|
||||
|
||||
/** Read an embedded message field value from the stream. */
|
||||
public <T extends MessageLite> T readMessage(
|
||||
final Parser<T> parser,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
int length = readRawVarint32();
|
||||
if (recursionDepth >= recursionLimit) {
|
||||
throw InvalidProtocolBufferException.recursionLimitExceeded();
|
||||
}
|
||||
final int oldLimit = pushLimit(length);
|
||||
++recursionDepth;
|
||||
T result = parser.parsePartialFrom(this, extensionRegistry);
|
||||
checkLastTagWas(0);
|
||||
--recursionDepth;
|
||||
popLimit(oldLimit);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Read a {@code bytes} field value from the stream. */
|
||||
public ByteString readBytes() throws IOException {
|
||||
final int size = readRawVarint32();
|
||||
if (size == 0) {
|
||||
return ByteString.EMPTY;
|
||||
} else if (size <= (bufferSize - bufferPos) && size > 0) {
|
||||
// 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);
|
||||
bufferPos += size;
|
||||
return result;
|
||||
} else {
|
||||
// Slow path: Build a byte array first then copy it.
|
||||
return ByteString.copyFrom(readRawBytes(size));
|
||||
}
|
||||
}
|
||||
|
||||
/** Read a {@code uint32} field value from the stream. */
|
||||
public int readUInt32() throws IOException {
|
||||
return readRawVarint32();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an enum field value from the stream. Caller is responsible
|
||||
* for converting the numeric value to an actual enum.
|
||||
*/
|
||||
public int readEnum() throws IOException {
|
||||
return readRawVarint32();
|
||||
}
|
||||
|
||||
/** Read an {@code sfixed32} field value from the stream. */
|
||||
public int readSFixed32() throws IOException {
|
||||
return readRawLittleEndian32();
|
||||
}
|
||||
|
||||
/** Read an {@code sfixed64} field value from the stream. */
|
||||
public long readSFixed64() throws IOException {
|
||||
return readRawLittleEndian64();
|
||||
}
|
||||
|
||||
/** Read an {@code sint32} field value from the stream. */
|
||||
public int readSInt32() throws IOException {
|
||||
return decodeZigZag32(readRawVarint32());
|
||||
}
|
||||
|
||||
/** Read an {@code sint64} field value from the stream. */
|
||||
public long readSInt64() throws IOException {
|
||||
return decodeZigZag64(readRawVarint64());
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
/**
|
||||
* Read a raw Varint from the stream. If larger than 32 bits, discard the
|
||||
* upper bits.
|
||||
*/
|
||||
public int readRawVarint32() throws IOException {
|
||||
byte tmp = readRawByte();
|
||||
if (tmp >= 0) {
|
||||
return tmp;
|
||||
}
|
||||
int result = tmp & 0x7f;
|
||||
if ((tmp = readRawByte()) >= 0) {
|
||||
result |= tmp << 7;
|
||||
} else {
|
||||
result |= (tmp & 0x7f) << 7;
|
||||
if ((tmp = readRawByte()) >= 0) {
|
||||
result |= tmp << 14;
|
||||
} else {
|
||||
result |= (tmp & 0x7f) << 14;
|
||||
if ((tmp = readRawByte()) >= 0) {
|
||||
result |= tmp << 21;
|
||||
} else {
|
||||
result |= (tmp & 0x7f) << 21;
|
||||
result |= (tmp = readRawByte()) << 28;
|
||||
if (tmp < 0) {
|
||||
// Discard upper 32 bits.
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (readRawByte() >= 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
throw InvalidProtocolBufferException.malformedVarint();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a varint from the input one byte at a time, so that it does not
|
||||
* read any bytes after the end of the varint. If you simply wrapped the
|
||||
* stream in a CodedInputStream and used {@link #readRawVarint32(InputStream)}
|
||||
* then you would probably end up reading past the end of the varint since
|
||||
* CodedInputStream buffers its input.
|
||||
*/
|
||||
static int readRawVarint32(final InputStream input) throws IOException {
|
||||
final int firstByte = input.read();
|
||||
if (firstByte == -1) {
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
}
|
||||
return readRawVarint32(firstByte, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #readRawVarint32(InputStream)}, but expects that the caller
|
||||
* has already read one byte. This allows the caller to determine if EOF
|
||||
* has been reached before attempting to read.
|
||||
*/
|
||||
public static int readRawVarint32(
|
||||
final int firstByte, final InputStream input) throws IOException {
|
||||
if ((firstByte & 0x80) == 0) {
|
||||
return firstByte;
|
||||
}
|
||||
|
||||
int result = firstByte & 0x7f;
|
||||
int offset = 7;
|
||||
for (; offset < 32; offset += 7) {
|
||||
final int b = input.read();
|
||||
if (b == -1) {
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
}
|
||||
result |= (b & 0x7f) << offset;
|
||||
if ((b & 0x80) == 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// Keep reading up to 64 bits.
|
||||
for (; offset < 64; offset += 7) {
|
||||
final int b = input.read();
|
||||
if (b == -1) {
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
}
|
||||
if ((b & 0x80) == 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
throw InvalidProtocolBufferException.malformedVarint();
|
||||
}
|
||||
|
||||
/** Read a raw Varint from the stream. */
|
||||
public long readRawVarint64() throws IOException {
|
||||
int shift = 0;
|
||||
long result = 0;
|
||||
while (shift < 64) {
|
||||
final byte b = readRawByte();
|
||||
result |= (long)(b & 0x7F) << shift;
|
||||
if ((b & 0x80) == 0) {
|
||||
return result;
|
||||
}
|
||||
shift += 7;
|
||||
}
|
||||
throw InvalidProtocolBufferException.malformedVarint();
|
||||
}
|
||||
|
||||
/** Read a 32-bit little-endian integer from the stream. */
|
||||
public int readRawLittleEndian32() throws IOException {
|
||||
final byte b1 = readRawByte();
|
||||
final byte b2 = readRawByte();
|
||||
final byte b3 = readRawByte();
|
||||
final byte b4 = readRawByte();
|
||||
return (((int)b1 & 0xff) ) |
|
||||
(((int)b2 & 0xff) << 8) |
|
||||
(((int)b3 & 0xff) << 16) |
|
||||
(((int)b4 & 0xff) << 24);
|
||||
}
|
||||
|
||||
/** Read a 64-bit little-endian integer from the stream. */
|
||||
public long readRawLittleEndian64() throws IOException {
|
||||
final byte b1 = readRawByte();
|
||||
final byte b2 = readRawByte();
|
||||
final byte b3 = readRawByte();
|
||||
final byte b4 = readRawByte();
|
||||
final byte b5 = readRawByte();
|
||||
final byte b6 = readRawByte();
|
||||
final byte b7 = readRawByte();
|
||||
final byte b8 = readRawByte();
|
||||
return (((long)b1 & 0xff) ) |
|
||||
(((long)b2 & 0xff) << 8) |
|
||||
(((long)b3 & 0xff) << 16) |
|
||||
(((long)b4 & 0xff) << 24) |
|
||||
(((long)b5 & 0xff) << 32) |
|
||||
(((long)b6 & 0xff) << 40) |
|
||||
(((long)b7 & 0xff) << 48) |
|
||||
(((long)b8 & 0xff) << 56);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers
|
||||
* into values that can be efficiently encoded with varint. (Otherwise,
|
||||
* negative values must be sign-extended to 64 bits to be varint encoded,
|
||||
* thus always taking 10 bytes on the wire.)
|
||||
*
|
||||
* @param n An unsigned 32-bit integer, stored in a signed int because
|
||||
* Java has no explicit unsigned support.
|
||||
* @return A signed 32-bit integer.
|
||||
*/
|
||||
public static int decodeZigZag32(final int n) {
|
||||
return (n >>> 1) ^ -(n & 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers
|
||||
* into values that can be efficiently encoded with varint. (Otherwise,
|
||||
* negative values must be sign-extended to 64 bits to be varint encoded,
|
||||
* thus always taking 10 bytes on the wire.)
|
||||
*
|
||||
* @param n An unsigned 64-bit integer, stored in a signed int because
|
||||
* Java has no explicit unsigned support.
|
||||
* @return A signed 64-bit integer.
|
||||
*/
|
||||
public static long decodeZigZag64(final long n) {
|
||||
return (n >>> 1) ^ -(n & 1);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
private final byte[] buffer;
|
||||
private int bufferSize;
|
||||
private int bufferSizeAfterLimit;
|
||||
private int bufferPos;
|
||||
private final InputStream input;
|
||||
private int lastTag;
|
||||
|
||||
/**
|
||||
* The total number of bytes read before the current buffer. The total
|
||||
* bytes read up to the current position can be computed as
|
||||
* {@code totalBytesRetired + bufferPos}. This value may be negative if
|
||||
* reading started in the middle of the current buffer (e.g. if the
|
||||
* constructor that takes a byte array and an offset was used).
|
||||
*/
|
||||
private int totalBytesRetired;
|
||||
|
||||
/** The absolute position of the end of the current message. */
|
||||
private int currentLimit = Integer.MAX_VALUE;
|
||||
|
||||
/** See setRecursionLimit() */
|
||||
private int recursionDepth;
|
||||
private int recursionLimit = DEFAULT_RECURSION_LIMIT;
|
||||
|
||||
/** See setSizeLimit() */
|
||||
private int sizeLimit = DEFAULT_SIZE_LIMIT;
|
||||
|
||||
private static final int DEFAULT_RECURSION_LIMIT = 64;
|
||||
private static final int DEFAULT_SIZE_LIMIT = 64 << 20; // 64MB
|
||||
private static final int BUFFER_SIZE = 4096;
|
||||
|
||||
private CodedInputStream(final byte[] buffer, final int off, final int len) {
|
||||
this.buffer = buffer;
|
||||
bufferSize = off + len;
|
||||
bufferPos = off;
|
||||
totalBytesRetired = -off;
|
||||
input = null;
|
||||
}
|
||||
|
||||
private CodedInputStream(final InputStream input) {
|
||||
buffer = new byte[BUFFER_SIZE];
|
||||
bufferSize = 0;
|
||||
bufferPos = 0;
|
||||
totalBytesRetired = 0;
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum message recursion depth. In order to prevent malicious
|
||||
* messages from causing stack overflows, {@code CodedInputStream} limits
|
||||
* how deeply messages may be nested. The default limit is 64.
|
||||
*
|
||||
* @return the old limit.
|
||||
*/
|
||||
public int setRecursionLimit(final int limit) {
|
||||
if (limit < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Recursion limit cannot be negative: " + limit);
|
||||
}
|
||||
final int oldLimit = recursionLimit;
|
||||
recursionLimit = limit;
|
||||
return oldLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum message size. In order to prevent malicious
|
||||
* messages from exhausting memory or causing integer overflows,
|
||||
* {@code CodedInputStream} limits how large a message may be.
|
||||
* The default limit is 64MB. You should set this limit as small
|
||||
* as you can without harming your app's functionality. Note that
|
||||
* size limits only apply when reading from an {@code InputStream}, not
|
||||
* when constructed around a raw byte array (nor with
|
||||
* {@link ByteString#newCodedInput}).
|
||||
* <p>
|
||||
* If you want to read several messages from a single CodedInputStream, you
|
||||
* could call {@link #resetSizeCounter()} after each one to avoid hitting the
|
||||
* size limit.
|
||||
*
|
||||
* @return the old limit.
|
||||
*/
|
||||
public int setSizeLimit(final int limit) {
|
||||
if (limit < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Size limit cannot be negative: " + limit);
|
||||
}
|
||||
final int oldLimit = sizeLimit;
|
||||
sizeLimit = limit;
|
||||
return oldLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the current size counter to zero (see {@link #setSizeLimit(int)}).
|
||||
*/
|
||||
public void resetSizeCounter() {
|
||||
totalBytesRetired = -bufferPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@code currentLimit} to (current position) + {@code byteLimit}. This
|
||||
* is called when descending into a length-delimited embedded message.
|
||||
*
|
||||
* <p>Note that {@code pushLimit()} does NOT affect how many bytes the
|
||||
* {@code CodedInputStream} reads from an underlying {@code InputStream} when
|
||||
* refreshing its buffer. If you need to prevent reading past a certain
|
||||
* point in the underlying {@code InputStream} (e.g. because you expect it to
|
||||
* contain more data after the end of the message which you need to handle
|
||||
* differently) then you must place a wrapper around your {@code InputStream}
|
||||
* which limits the amount of data that can be read from it.
|
||||
*
|
||||
* @return the old limit.
|
||||
*/
|
||||
public int pushLimit(int byteLimit) throws InvalidProtocolBufferException {
|
||||
if (byteLimit < 0) {
|
||||
throw InvalidProtocolBufferException.negativeSize();
|
||||
}
|
||||
byteLimit += totalBytesRetired + bufferPos;
|
||||
final int oldLimit = currentLimit;
|
||||
if (byteLimit > oldLimit) {
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
}
|
||||
currentLimit = byteLimit;
|
||||
|
||||
recomputeBufferSizeAfterLimit();
|
||||
|
||||
return oldLimit;
|
||||
}
|
||||
|
||||
private void recomputeBufferSizeAfterLimit() {
|
||||
bufferSize += bufferSizeAfterLimit;
|
||||
final int bufferEnd = totalBytesRetired + bufferSize;
|
||||
if (bufferEnd > currentLimit) {
|
||||
// Limit is in current buffer.
|
||||
bufferSizeAfterLimit = bufferEnd - currentLimit;
|
||||
bufferSize -= bufferSizeAfterLimit;
|
||||
} else {
|
||||
bufferSizeAfterLimit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Discards the current limit, returning to the previous limit.
|
||||
*
|
||||
* @param oldLimit The old limit, as returned by {@code pushLimit}.
|
||||
*/
|
||||
public void popLimit(final int oldLimit) {
|
||||
currentLimit = oldLimit;
|
||||
recomputeBufferSizeAfterLimit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes to be read before the current limit.
|
||||
* If no limit is set, returns -1.
|
||||
*/
|
||||
public int getBytesUntilLimit() {
|
||||
if (currentLimit == Integer.MAX_VALUE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
final int currentAbsolutePosition = totalBytesRetired + bufferPos;
|
||||
return currentLimit - currentAbsolutePosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the stream has reached the end of the input. This is the
|
||||
* case if either the end of the underlying input source has been reached or
|
||||
* if the stream has reached a limit created using {@link #pushLimit(int)}.
|
||||
*/
|
||||
public boolean isAtEnd() throws IOException {
|
||||
return bufferPos == bufferSize && !refillBuffer(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* The total bytes read up to the current position. Calling
|
||||
* {@link #resetSizeCounter()} resets this value to zero.
|
||||
*/
|
||||
public int getTotalBytesRead() {
|
||||
return totalBytesRetired + bufferPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called with {@code this.buffer} is empty to read more bytes from the
|
||||
* input. If {@code mustSucceed} is true, refillBuffer() guarantees that
|
||||
* either there will be at least one byte in the buffer when it returns
|
||||
* or it will throw an exception. If {@code mustSucceed} is false,
|
||||
* refillBuffer() returns false if no more bytes were available.
|
||||
*/
|
||||
private boolean refillBuffer(final boolean mustSucceed) throws IOException {
|
||||
if (bufferPos < bufferSize) {
|
||||
throw new IllegalStateException(
|
||||
"refillBuffer() called when buffer wasn't empty.");
|
||||
}
|
||||
|
||||
if (totalBytesRetired + bufferSize == currentLimit) {
|
||||
// Oops, we hit a limit.
|
||||
if (mustSucceed) {
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
totalBytesRetired += bufferSize;
|
||||
|
||||
bufferPos = 0;
|
||||
bufferSize = (input == null) ? -1 : input.read(buffer);
|
||||
if (bufferSize == 0 || bufferSize < -1) {
|
||||
throw new IllegalStateException(
|
||||
"InputStream#read(byte[]) returned invalid result: " + bufferSize +
|
||||
"\nThe InputStream implementation is buggy.");
|
||||
}
|
||||
if (bufferSize == -1) {
|
||||
bufferSize = 0;
|
||||
if (mustSucceed) {
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
recomputeBufferSizeAfterLimit();
|
||||
final int totalBytesRead =
|
||||
totalBytesRetired + bufferSize + bufferSizeAfterLimit;
|
||||
if (totalBytesRead > sizeLimit || totalBytesRead < 0) {
|
||||
throw InvalidProtocolBufferException.sizeLimitExceeded();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read one byte from the input.
|
||||
*
|
||||
* @throws InvalidProtocolBufferException The end of the stream or the current
|
||||
* limit was reached.
|
||||
*/
|
||||
public byte readRawByte() throws IOException {
|
||||
if (bufferPos == bufferSize) {
|
||||
refillBuffer(true);
|
||||
}
|
||||
return buffer[bufferPos++];
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a fixed size of bytes from the input.
|
||||
*
|
||||
* @throws InvalidProtocolBufferException The end of the stream or the current
|
||||
* limit was reached.
|
||||
*/
|
||||
public byte[] readRawBytes(final int size) throws IOException {
|
||||
if (size < 0) {
|
||||
throw InvalidProtocolBufferException.negativeSize();
|
||||
}
|
||||
|
||||
if (totalBytesRetired + bufferPos + size > currentLimit) {
|
||||
// Read to the end of the stream anyway.
|
||||
skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
|
||||
// Then fail.
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
}
|
||||
|
||||
if (size <= bufferSize - bufferPos) {
|
||||
// We have all the bytes we need already.
|
||||
final byte[] bytes = new byte[size];
|
||||
System.arraycopy(buffer, bufferPos, bytes, 0, size);
|
||||
bufferPos += size;
|
||||
return bytes;
|
||||
} else if (size < BUFFER_SIZE) {
|
||||
// Reading more bytes than are in the buffer, but not an excessive number
|
||||
// of bytes. We can safely allocate the resulting array ahead of time.
|
||||
|
||||
// First copy what we have.
|
||||
final byte[] bytes = new byte[size];
|
||||
int pos = bufferSize - bufferPos;
|
||||
System.arraycopy(buffer, bufferPos, bytes, 0, pos);
|
||||
bufferPos = bufferSize;
|
||||
|
||||
// We want to use refillBuffer() and then copy from the buffer into our
|
||||
// byte array rather than reading directly into our byte array because
|
||||
// the input may be unbuffered.
|
||||
refillBuffer(true);
|
||||
|
||||
while (size - pos > bufferSize) {
|
||||
System.arraycopy(buffer, 0, bytes, pos, bufferSize);
|
||||
pos += bufferSize;
|
||||
bufferPos = bufferSize;
|
||||
refillBuffer(true);
|
||||
}
|
||||
|
||||
System.arraycopy(buffer, 0, bytes, pos, size - pos);
|
||||
bufferPos = size - pos;
|
||||
|
||||
return bytes;
|
||||
} else {
|
||||
// The size is very large. For security reasons, we can't allocate the
|
||||
// entire byte array yet. The size comes directly from the input, so a
|
||||
// maliciously-crafted message could provide a bogus very large size in
|
||||
// order to trick the app into allocating a lot of memory. We avoid this
|
||||
// by allocating and reading only a small chunk at a time, so that the
|
||||
// malicious message must actually *be* extremely large to cause
|
||||
// problems. Meanwhile, we limit the allowed size of a message elsewhere.
|
||||
|
||||
// Remember the buffer markers since we'll have to copy the bytes out of
|
||||
// it later.
|
||||
final int originalBufferPos = bufferPos;
|
||||
final int originalBufferSize = bufferSize;
|
||||
|
||||
// Mark the current buffer consumed.
|
||||
totalBytesRetired += bufferSize;
|
||||
bufferPos = 0;
|
||||
bufferSize = 0;
|
||||
|
||||
// Read all the rest of the bytes we need.
|
||||
int sizeLeft = size - (originalBufferSize - originalBufferPos);
|
||||
final List<byte[]> chunks = new ArrayList<byte[]>();
|
||||
|
||||
while (sizeLeft > 0) {
|
||||
final byte[] chunk = new byte[Math.min(sizeLeft, BUFFER_SIZE)];
|
||||
int pos = 0;
|
||||
while (pos < chunk.length) {
|
||||
final int n = (input == null) ? -1 :
|
||||
input.read(chunk, pos, chunk.length - pos);
|
||||
if (n == -1) {
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
}
|
||||
totalBytesRetired += n;
|
||||
pos += n;
|
||||
}
|
||||
sizeLeft -= chunk.length;
|
||||
chunks.add(chunk);
|
||||
}
|
||||
|
||||
// OK, got everything. Now concatenate it all into one buffer.
|
||||
final byte[] bytes = new byte[size];
|
||||
|
||||
// Start by copying the leftover bytes from this.buffer.
|
||||
int pos = originalBufferSize - originalBufferPos;
|
||||
System.arraycopy(buffer, originalBufferPos, bytes, 0, pos);
|
||||
|
||||
// And now all the chunks.
|
||||
for (final byte[] chunk : chunks) {
|
||||
System.arraycopy(chunk, 0, bytes, pos, chunk.length);
|
||||
pos += chunk.length;
|
||||
}
|
||||
|
||||
// Done.
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and discards {@code size} bytes.
|
||||
*
|
||||
* @throws InvalidProtocolBufferException The end of the stream or the current
|
||||
* limit was reached.
|
||||
*/
|
||||
public void skipRawBytes(final int size) throws IOException {
|
||||
if (size < 0) {
|
||||
throw InvalidProtocolBufferException.negativeSize();
|
||||
}
|
||||
|
||||
if (totalBytesRetired + bufferPos + size > currentLimit) {
|
||||
// Read to the end of the stream anyway.
|
||||
skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
|
||||
// Then fail.
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
}
|
||||
|
||||
if (size <= bufferSize - bufferPos) {
|
||||
// We have all the bytes we need already.
|
||||
bufferPos += size;
|
||||
} else {
|
||||
// Skipping more bytes than are in the buffer. First skip what we have.
|
||||
int pos = bufferSize - bufferPos;
|
||||
bufferPos = bufferSize;
|
||||
|
||||
// Keep refilling the buffer until we get to the point we wanted to skip
|
||||
// to. This has the side effect of ensuring the limits are updated
|
||||
// correctly.
|
||||
refillBuffer(true);
|
||||
while (size - pos > bufferSize) {
|
||||
pos += bufferSize;
|
||||
bufferPos = bufferSize;
|
||||
refillBuffer(true);
|
||||
}
|
||||
|
||||
bufferPos = size - pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,482 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.Descriptors.Descriptor;
|
||||
import com.google.protobuf.Descriptors.FieldDescriptor;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An implementation of {@link Message} that can represent arbitrary types,
|
||||
* given a {@link Descriptors.Descriptor}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public final class DynamicMessage extends AbstractMessage {
|
||||
private final Descriptor type;
|
||||
private final FieldSet<FieldDescriptor> fields;
|
||||
private final UnknownFieldSet unknownFields;
|
||||
private int memoizedSize = -1;
|
||||
|
||||
/**
|
||||
* Construct a {@code DynamicMessage} using the given {@code FieldSet}.
|
||||
*/
|
||||
private DynamicMessage(Descriptor type, FieldSet<FieldDescriptor> fields,
|
||||
UnknownFieldSet unknownFields) {
|
||||
this.type = type;
|
||||
this.fields = fields;
|
||||
this.unknownFields = unknownFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@code DynamicMessage} representing the default instance of the
|
||||
* given type.
|
||||
*/
|
||||
public static DynamicMessage getDefaultInstance(Descriptor type) {
|
||||
return new DynamicMessage(type, FieldSet.<FieldDescriptor>emptySet(),
|
||||
UnknownFieldSet.getDefaultInstance());
|
||||
}
|
||||
|
||||
/** Parse a message of the given type from the given input stream. */
|
||||
public static DynamicMessage parseFrom(Descriptor type,
|
||||
CodedInputStream input)
|
||||
throws IOException {
|
||||
return newBuilder(type).mergeFrom(input).buildParsed();
|
||||
}
|
||||
|
||||
/** Parse a message of the given type from the given input stream. */
|
||||
public static DynamicMessage parseFrom(
|
||||
Descriptor type,
|
||||
CodedInputStream input,
|
||||
ExtensionRegistry extensionRegistry)
|
||||
throws IOException {
|
||||
return newBuilder(type).mergeFrom(input, extensionRegistry).buildParsed();
|
||||
}
|
||||
|
||||
/** Parse {@code data} as a message of the given type and return it. */
|
||||
public static DynamicMessage parseFrom(Descriptor type, ByteString data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return newBuilder(type).mergeFrom(data).buildParsed();
|
||||
}
|
||||
|
||||
/** Parse {@code data} as a message of the given type and return it. */
|
||||
public static DynamicMessage parseFrom(Descriptor type, ByteString data,
|
||||
ExtensionRegistry extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return newBuilder(type).mergeFrom(data, extensionRegistry).buildParsed();
|
||||
}
|
||||
|
||||
/** Parse {@code data} as a message of the given type and return it. */
|
||||
public static DynamicMessage parseFrom(Descriptor type, byte[] data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return newBuilder(type).mergeFrom(data).buildParsed();
|
||||
}
|
||||
|
||||
/** Parse {@code data} as a message of the given type and return it. */
|
||||
public static DynamicMessage parseFrom(Descriptor type, byte[] data,
|
||||
ExtensionRegistry extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return newBuilder(type).mergeFrom(data, extensionRegistry).buildParsed();
|
||||
}
|
||||
|
||||
/** Parse a message of the given type from {@code input} and return it. */
|
||||
public static DynamicMessage parseFrom(Descriptor type, InputStream input)
|
||||
throws IOException {
|
||||
return newBuilder(type).mergeFrom(input).buildParsed();
|
||||
}
|
||||
|
||||
/** Parse a message of the given type from {@code input} and return it. */
|
||||
public static DynamicMessage parseFrom(Descriptor type, InputStream input,
|
||||
ExtensionRegistry extensionRegistry)
|
||||
throws IOException {
|
||||
return newBuilder(type).mergeFrom(input, extensionRegistry).buildParsed();
|
||||
}
|
||||
|
||||
/** Construct a {@link Message.Builder} for the given type. */
|
||||
public static Builder newBuilder(Descriptor type) {
|
||||
return new Builder(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a {@link Message.Builder} for a message of the same type as
|
||||
* {@code prototype}, and initialize it with {@code prototype}'s contents.
|
||||
*/
|
||||
public static Builder newBuilder(Message prototype) {
|
||||
return new Builder(prototype.getDescriptorForType()).mergeFrom(prototype);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Implementation of Message interface.
|
||||
|
||||
public Descriptor getDescriptorForType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public DynamicMessage getDefaultInstanceForType() {
|
||||
return getDefaultInstance(type);
|
||||
}
|
||||
|
||||
public Map<FieldDescriptor, Object> getAllFields() {
|
||||
return fields.getAllFields();
|
||||
}
|
||||
|
||||
public boolean hasField(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
return fields.hasField(field);
|
||||
}
|
||||
|
||||
public Object getField(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
Object result = fields.getField(field);
|
||||
if (result == null) {
|
||||
if (field.isRepeated()) {
|
||||
result = Collections.emptyList();
|
||||
} else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
result = getDefaultInstance(field.getMessageType());
|
||||
} else {
|
||||
result = field.getDefaultValue();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getRepeatedFieldCount(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
return fields.getRepeatedFieldCount(field);
|
||||
}
|
||||
|
||||
public Object getRepeatedField(FieldDescriptor field, int index) {
|
||||
verifyContainingType(field);
|
||||
return fields.getRepeatedField(field, index);
|
||||
}
|
||||
|
||||
public UnknownFieldSet getUnknownFields() {
|
||||
return unknownFields;
|
||||
}
|
||||
|
||||
private static boolean isInitialized(Descriptor type,
|
||||
FieldSet<FieldDescriptor> fields) {
|
||||
// Check that all required fields are present.
|
||||
for (final FieldDescriptor field : type.getFields()) {
|
||||
if (field.isRequired()) {
|
||||
if (!fields.hasField(field)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that embedded messages are initialized.
|
||||
return fields.isInitialized();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return isInitialized(type, fields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(CodedOutputStream output) throws IOException {
|
||||
if (type.getOptions().getMessageSetWireFormat()) {
|
||||
fields.writeMessageSetTo(output);
|
||||
unknownFields.writeAsMessageSetTo(output);
|
||||
} else {
|
||||
fields.writeTo(output);
|
||||
unknownFields.writeTo(output);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSerializedSize() {
|
||||
int size = memoizedSize;
|
||||
if (size != -1) return size;
|
||||
|
||||
if (type.getOptions().getMessageSetWireFormat()) {
|
||||
size = fields.getMessageSetSerializedSize();
|
||||
size += unknownFields.getSerializedSizeAsMessageSet();
|
||||
} else {
|
||||
size = fields.getSerializedSize();
|
||||
size += unknownFields.getSerializedSize();
|
||||
}
|
||||
|
||||
memoizedSize = size;
|
||||
return size;
|
||||
}
|
||||
|
||||
public Builder newBuilderForType() {
|
||||
return new Builder(type);
|
||||
}
|
||||
|
||||
public Builder toBuilder() {
|
||||
return newBuilderForType().mergeFrom(this);
|
||||
}
|
||||
|
||||
public Parser<DynamicMessage> getParserForType() {
|
||||
return new AbstractParser<DynamicMessage>() {
|
||||
public DynamicMessage parsePartialFrom(
|
||||
CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
Builder builder = newBuilder(type);
|
||||
try {
|
||||
builder.mergeFrom(input, extensionRegistry);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e.setUnfinishedMessage(builder.buildPartial());
|
||||
} catch (IOException e) {
|
||||
throw new InvalidProtocolBufferException(e.getMessage())
|
||||
.setUnfinishedMessage(builder.buildPartial());
|
||||
}
|
||||
return builder.buildPartial();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** Verifies that the field is a field of this message. */
|
||||
private void verifyContainingType(FieldDescriptor field) {
|
||||
if (field.getContainingType() != type) {
|
||||
throw new IllegalArgumentException(
|
||||
"FieldDescriptor does not match message type.");
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
/**
|
||||
* Builder for {@link DynamicMessage}s.
|
||||
*/
|
||||
public static final class Builder extends AbstractMessage.Builder<Builder> {
|
||||
private final Descriptor type;
|
||||
private FieldSet<FieldDescriptor> fields;
|
||||
private UnknownFieldSet unknownFields;
|
||||
|
||||
/** Construct a {@code Builder} for the given type. */
|
||||
private Builder(Descriptor type) {
|
||||
this.type = type;
|
||||
this.fields = FieldSet.newFieldSet();
|
||||
this.unknownFields = UnknownFieldSet.getDefaultInstance();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Implementation of Message.Builder interface.
|
||||
|
||||
@Override
|
||||
public Builder clear() {
|
||||
if (fields.isImmutable()) {
|
||||
fields = FieldSet.newFieldSet();
|
||||
} else {
|
||||
fields.clear();
|
||||
}
|
||||
unknownFields = UnknownFieldSet.getDefaultInstance();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder mergeFrom(Message other) {
|
||||
if (other instanceof DynamicMessage) {
|
||||
// This should be somewhat faster than calling super.mergeFrom().
|
||||
DynamicMessage otherDynamicMessage = (DynamicMessage) other;
|
||||
if (otherDynamicMessage.type != type) {
|
||||
throw new IllegalArgumentException(
|
||||
"mergeFrom(Message) can only merge messages of the same type.");
|
||||
}
|
||||
ensureIsMutable();
|
||||
fields.mergeFrom(otherDynamicMessage.fields);
|
||||
mergeUnknownFields(otherDynamicMessage.unknownFields);
|
||||
return this;
|
||||
} else {
|
||||
return super.mergeFrom(other);
|
||||
}
|
||||
}
|
||||
|
||||
public DynamicMessage build() {
|
||||
if (!isInitialized()) {
|
||||
throw newUninitializedMessageException(
|
||||
new DynamicMessage(type, fields, unknownFields));
|
||||
}
|
||||
return buildPartial();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for DynamicMessage.parseFrom() methods to call. Throws
|
||||
* {@link InvalidProtocolBufferException} instead of
|
||||
* {@link UninitializedMessageException}.
|
||||
*/
|
||||
private DynamicMessage buildParsed() throws InvalidProtocolBufferException {
|
||||
if (!isInitialized()) {
|
||||
throw newUninitializedMessageException(
|
||||
new DynamicMessage(type, fields, unknownFields))
|
||||
.asInvalidProtocolBufferException();
|
||||
}
|
||||
return buildPartial();
|
||||
}
|
||||
|
||||
public DynamicMessage buildPartial() {
|
||||
fields.makeImmutable();
|
||||
DynamicMessage result =
|
||||
new DynamicMessage(type, fields, unknownFields);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder clone() {
|
||||
Builder result = new Builder(type);
|
||||
result.fields.mergeFrom(fields);
|
||||
result.mergeUnknownFields(unknownFields);
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
return DynamicMessage.isInitialized(type, fields);
|
||||
}
|
||||
|
||||
public Descriptor getDescriptorForType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public DynamicMessage getDefaultInstanceForType() {
|
||||
return getDefaultInstance(type);
|
||||
}
|
||||
|
||||
public Map<FieldDescriptor, Object> getAllFields() {
|
||||
return fields.getAllFields();
|
||||
}
|
||||
|
||||
public Builder newBuilderForField(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
|
||||
if (field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
|
||||
throw new IllegalArgumentException(
|
||||
"newBuilderForField is only valid for fields with message type.");
|
||||
}
|
||||
|
||||
return new Builder(field.getMessageType());
|
||||
}
|
||||
|
||||
public boolean hasField(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
return fields.hasField(field);
|
||||
}
|
||||
|
||||
public Object getField(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
Object result = fields.getField(field);
|
||||
if (result == null) {
|
||||
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
result = getDefaultInstance(field.getMessageType());
|
||||
} else {
|
||||
result = field.getDefaultValue();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Builder setField(FieldDescriptor field, Object value) {
|
||||
verifyContainingType(field);
|
||||
ensureIsMutable();
|
||||
fields.setField(field, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder clearField(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
ensureIsMutable();
|
||||
fields.clearField(field);
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getRepeatedFieldCount(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
return fields.getRepeatedFieldCount(field);
|
||||
}
|
||||
|
||||
public Object getRepeatedField(FieldDescriptor field, int index) {
|
||||
verifyContainingType(field);
|
||||
return fields.getRepeatedField(field, index);
|
||||
}
|
||||
|
||||
public Builder setRepeatedField(FieldDescriptor field,
|
||||
int index, Object value) {
|
||||
verifyContainingType(field);
|
||||
ensureIsMutable();
|
||||
fields.setRepeatedField(field, index, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addRepeatedField(FieldDescriptor field, Object value) {
|
||||
verifyContainingType(field);
|
||||
ensureIsMutable();
|
||||
fields.addRepeatedField(field, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public UnknownFieldSet getUnknownFields() {
|
||||
return unknownFields;
|
||||
}
|
||||
|
||||
public Builder setUnknownFields(UnknownFieldSet unknownFields) {
|
||||
this.unknownFields = unknownFields;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder mergeUnknownFields(UnknownFieldSet unknownFields) {
|
||||
this.unknownFields =
|
||||
UnknownFieldSet.newBuilder(this.unknownFields)
|
||||
.mergeFrom(unknownFields)
|
||||
.build();
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Verifies that the field is a field of this message. */
|
||||
private void verifyContainingType(FieldDescriptor field) {
|
||||
if (field.getContainingType() != type) {
|
||||
throw new IllegalArgumentException(
|
||||
"FieldDescriptor does not match message type.");
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureIsMutable() {
|
||||
if (fields.isImmutable()) {
|
||||
fields = fields.clone();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.google.protobuf.Message.Builder getFieldBuilder(FieldDescriptor field) {
|
||||
// TODO(xiangl): need implementation for dynamic message
|
||||
throw new UnsupportedOperationException(
|
||||
"getFieldBuilder() called on a dynamic message type.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,266 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.Descriptors.Descriptor;
|
||||
import com.google.protobuf.Descriptors.FieldDescriptor;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A table of known extensions, searchable by name or field number. When
|
||||
* parsing a protocol message that might have extensions, you must provide
|
||||
* an {@code ExtensionRegistry} in which you have registered any extensions
|
||||
* that you want to be able to parse. Otherwise, those extensions will just
|
||||
* be treated like unknown fields.
|
||||
*
|
||||
* <p>For example, if you had the {@code .proto} file:
|
||||
*
|
||||
* <pre>
|
||||
* option java_class = "MyProto";
|
||||
*
|
||||
* message Foo {
|
||||
* extensions 1000 to max;
|
||||
* }
|
||||
*
|
||||
* extend Foo {
|
||||
* optional int32 bar;
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Then you might write code like:
|
||||
*
|
||||
* <pre>
|
||||
* ExtensionRegistry registry = ExtensionRegistry.newInstance();
|
||||
* registry.add(MyProto.bar);
|
||||
* MyProto.Foo message = MyProto.Foo.parseFrom(input, registry);
|
||||
* </pre>
|
||||
*
|
||||
* <p>Background:
|
||||
*
|
||||
* <p>You might wonder why this is necessary. Two alternatives might come to
|
||||
* mind. First, you might imagine a system where generated extensions are
|
||||
* automatically registered when their containing classes are loaded. This
|
||||
* is a popular technique, but is bad design; among other things, it creates a
|
||||
* situation where behavior can change depending on what classes happen to be
|
||||
* loaded. It also introduces a security vulnerability, because an
|
||||
* unprivileged class could cause its code to be called unexpectedly from a
|
||||
* privileged class by registering itself as an extension of the right type.
|
||||
*
|
||||
* <p>Another option you might consider is lazy parsing: do not parse an
|
||||
* extension until it is first requested, at which point the caller must
|
||||
* provide a type to use. This introduces a different set of problems. First,
|
||||
* it would require a mutex lock any time an extension was accessed, which
|
||||
* would be slow. Second, corrupt data would not be detected until first
|
||||
* access, at which point it would be much harder to deal with it. Third, it
|
||||
* could violate the expectation that message objects are immutable, since the
|
||||
* type provided could be any arbitrary message class. An unprivileged user
|
||||
* could take advantage of this to inject a mutable object into a message
|
||||
* belonging to privileged code and create mischief.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public final class ExtensionRegistry extends ExtensionRegistryLite {
|
||||
/** Construct a new, empty instance. */
|
||||
public static ExtensionRegistry newInstance() {
|
||||
return new ExtensionRegistry();
|
||||
}
|
||||
|
||||
/** Get the unmodifiable singleton empty instance. */
|
||||
public static ExtensionRegistry getEmptyRegistry() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
/** Returns an unmodifiable view of the registry. */
|
||||
@Override
|
||||
public ExtensionRegistry getUnmodifiable() {
|
||||
return new ExtensionRegistry(this);
|
||||
}
|
||||
|
||||
/** A (Descriptor, Message) pair, returned by lookup methods. */
|
||||
public static final class ExtensionInfo {
|
||||
/** The extension's descriptor. */
|
||||
public final FieldDescriptor descriptor;
|
||||
|
||||
/**
|
||||
* A default instance of the extension's type, if it has a message type.
|
||||
* Otherwise, {@code null}.
|
||||
*/
|
||||
public final Message defaultInstance;
|
||||
|
||||
private ExtensionInfo(final FieldDescriptor descriptor) {
|
||||
this.descriptor = descriptor;
|
||||
defaultInstance = null;
|
||||
}
|
||||
private ExtensionInfo(final FieldDescriptor descriptor,
|
||||
final Message defaultInstance) {
|
||||
this.descriptor = descriptor;
|
||||
this.defaultInstance = defaultInstance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an extension by fully-qualified field name, in the proto namespace.
|
||||
* I.e. {@code result.descriptor.fullName()} will match {@code fullName} if
|
||||
* a match is found.
|
||||
*
|
||||
* @return Information about the extension if found, or {@code null}
|
||||
* otherwise.
|
||||
*/
|
||||
public ExtensionInfo findExtensionByName(final String fullName) {
|
||||
return extensionsByName.get(fullName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an extension by containing type and field number.
|
||||
*
|
||||
* @return Information about the extension if found, or {@code null}
|
||||
* otherwise.
|
||||
*/
|
||||
public ExtensionInfo findExtensionByNumber(final Descriptor containingType,
|
||||
final int fieldNumber) {
|
||||
return extensionsByNumber.get(
|
||||
new DescriptorIntPair(containingType, fieldNumber));
|
||||
}
|
||||
|
||||
/** Add an extension from a generated file to the registry. */
|
||||
public void add(final GeneratedMessage.GeneratedExtension<?, ?> extension) {
|
||||
if (extension.getDescriptor().getJavaType() ==
|
||||
FieldDescriptor.JavaType.MESSAGE) {
|
||||
if (extension.getMessageDefaultInstance() == null) {
|
||||
throw new IllegalStateException(
|
||||
"Registered message-type extension had null default instance: " +
|
||||
extension.getDescriptor().getFullName());
|
||||
}
|
||||
add(new ExtensionInfo(extension.getDescriptor(),
|
||||
extension.getMessageDefaultInstance()));
|
||||
} else {
|
||||
add(new ExtensionInfo(extension.getDescriptor(), null));
|
||||
}
|
||||
}
|
||||
|
||||
/** Add a non-message-type extension to the registry by descriptor. */
|
||||
public void add(final FieldDescriptor type) {
|
||||
if (type.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
throw new IllegalArgumentException(
|
||||
"ExtensionRegistry.add() must be provided a default instance when " +
|
||||
"adding an embedded message extension.");
|
||||
}
|
||||
add(new ExtensionInfo(type, null));
|
||||
}
|
||||
|
||||
/** Add a message-type extension to the registry by descriptor. */
|
||||
public void add(final FieldDescriptor type, final Message defaultInstance) {
|
||||
if (type.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
|
||||
throw new IllegalArgumentException(
|
||||
"ExtensionRegistry.add() provided a default instance for a " +
|
||||
"non-message extension.");
|
||||
}
|
||||
add(new ExtensionInfo(type, defaultInstance));
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Private stuff.
|
||||
|
||||
private ExtensionRegistry() {
|
||||
this.extensionsByName = new HashMap<String, ExtensionInfo>();
|
||||
this.extensionsByNumber = new HashMap<DescriptorIntPair, ExtensionInfo>();
|
||||
}
|
||||
|
||||
private ExtensionRegistry(ExtensionRegistry other) {
|
||||
super(other);
|
||||
this.extensionsByName = Collections.unmodifiableMap(other.extensionsByName);
|
||||
this.extensionsByNumber =
|
||||
Collections.unmodifiableMap(other.extensionsByNumber);
|
||||
}
|
||||
|
||||
private final Map<String, ExtensionInfo> extensionsByName;
|
||||
private final Map<DescriptorIntPair, ExtensionInfo> extensionsByNumber;
|
||||
|
||||
private ExtensionRegistry(boolean empty) {
|
||||
super(ExtensionRegistryLite.getEmptyRegistry());
|
||||
this.extensionsByName = Collections.<String, ExtensionInfo>emptyMap();
|
||||
this.extensionsByNumber =
|
||||
Collections.<DescriptorIntPair, ExtensionInfo>emptyMap();
|
||||
}
|
||||
private static final ExtensionRegistry EMPTY = new ExtensionRegistry(true);
|
||||
|
||||
private void add(final ExtensionInfo extension) {
|
||||
if (!extension.descriptor.isExtension()) {
|
||||
throw new IllegalArgumentException(
|
||||
"ExtensionRegistry.add() was given a FieldDescriptor for a regular " +
|
||||
"(non-extension) field.");
|
||||
}
|
||||
|
||||
extensionsByName.put(extension.descriptor.getFullName(), extension);
|
||||
extensionsByNumber.put(
|
||||
new DescriptorIntPair(extension.descriptor.getContainingType(),
|
||||
extension.descriptor.getNumber()),
|
||||
extension);
|
||||
|
||||
final FieldDescriptor field = extension.descriptor;
|
||||
if (field.getContainingType().getOptions().getMessageSetWireFormat() &&
|
||||
field.getType() == FieldDescriptor.Type.MESSAGE &&
|
||||
field.isOptional() &&
|
||||
field.getExtensionScope() == field.getMessageType()) {
|
||||
// This is an extension of a MessageSet type defined within the extension
|
||||
// type's own scope. For backwards-compatibility, allow it to be looked
|
||||
// up by type name.
|
||||
extensionsByName.put(field.getMessageType().getFullName(), extension);
|
||||
}
|
||||
}
|
||||
|
||||
/** A (GenericDescriptor, int) pair, used as a map key. */
|
||||
private static final class DescriptorIntPair {
|
||||
private final Descriptor descriptor;
|
||||
private final int number;
|
||||
|
||||
DescriptorIntPair(final Descriptor descriptor, final int number) {
|
||||
this.descriptor = descriptor;
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return descriptor.hashCode() * ((1 << 16) - 1) + number;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (!(obj instanceof DescriptorIntPair)) {
|
||||
return false;
|
||||
}
|
||||
final DescriptorIntPair other = (DescriptorIntPair)obj;
|
||||
return descriptor == other.descriptor && number == other.number;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Equivalent to {@link ExtensionRegistry} but supports only "lite" types.
|
||||
* <p>
|
||||
* If all of your types are lite types, then you only need to use
|
||||
* {@code ExtensionRegistryLite}. Similarly, if all your types are regular
|
||||
* types, then you only need {@link ExtensionRegistry}. Typically it does not
|
||||
* make sense to mix the two, since if you have any regular types in your
|
||||
* program, you then require the full runtime and lose all the benefits of
|
||||
* the lite runtime, so you might as well make all your types be regular types.
|
||||
* However, in some cases (e.g. when depending on multiple third-party libraries
|
||||
* where one uses lite types and one uses regular), you may find yourself
|
||||
* wanting to mix the two. In this case things get more complicated.
|
||||
* <p>
|
||||
* There are three factors to consider: Whether the type being extended is
|
||||
* lite, whether the embedded type (in the case of a message-typed extension)
|
||||
* is lite, and whether the extension itself is lite. Since all three are
|
||||
* declared in different files, they could all be different. Here are all
|
||||
* the combinations and which type of registry to use:
|
||||
* <pre>
|
||||
* Extended type Inner type Extension Use registry
|
||||
* =======================================================================
|
||||
* lite lite lite ExtensionRegistryLite
|
||||
* lite regular lite ExtensionRegistry
|
||||
* regular regular regular ExtensionRegistry
|
||||
* all other combinations not supported
|
||||
* </pre>
|
||||
* <p>
|
||||
* Note that just as regular types are not allowed to contain lite-type fields,
|
||||
* they are also not allowed to contain lite-type extensions. This is because
|
||||
* regular types must be fully accessible via reflection, which in turn means
|
||||
* that all the inner messages must also support reflection. On the other hand,
|
||||
* since regular types implement the entire lite interface, there is no problem
|
||||
* with embedding regular types inside lite types.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class ExtensionRegistryLite {
|
||||
|
||||
// Set true to enable lazy parsing feature for MessageSet.
|
||||
//
|
||||
// TODO(xiangl): Now we use a global flag to control whether enable lazy
|
||||
// parsing feature for MessageSet, which may be too crude for some
|
||||
// applications. Need to support this feature on smaller granularity.
|
||||
private static volatile boolean eagerlyParseMessageSets = false;
|
||||
|
||||
public static boolean isEagerlyParseMessageSets() {
|
||||
return eagerlyParseMessageSets;
|
||||
}
|
||||
|
||||
public static void setEagerlyParseMessageSets(boolean isEagerlyParse) {
|
||||
eagerlyParseMessageSets = isEagerlyParse;
|
||||
}
|
||||
|
||||
/** Construct a new, empty instance. */
|
||||
public static ExtensionRegistryLite newInstance() {
|
||||
return new ExtensionRegistryLite();
|
||||
}
|
||||
|
||||
/** Get the unmodifiable singleton empty instance. */
|
||||
public static ExtensionRegistryLite getEmptyRegistry() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
/** Returns an unmodifiable view of the registry. */
|
||||
public ExtensionRegistryLite getUnmodifiable() {
|
||||
return new ExtensionRegistryLite(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an extension by containing type and field number.
|
||||
*
|
||||
* @return Information about the extension if found, or {@code null}
|
||||
* otherwise.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <ContainingType extends MessageLite>
|
||||
GeneratedMessageLite.GeneratedExtension<ContainingType, ?>
|
||||
findLiteExtensionByNumber(
|
||||
final ContainingType containingTypeDefaultInstance,
|
||||
final int fieldNumber) {
|
||||
return (GeneratedMessageLite.GeneratedExtension<ContainingType, ?>)
|
||||
extensionsByNumber.get(
|
||||
new ObjectIntPair(containingTypeDefaultInstance, fieldNumber));
|
||||
}
|
||||
|
||||
/** Add an extension from a lite generated file to the registry. */
|
||||
public final void add(
|
||||
final GeneratedMessageLite.GeneratedExtension<?, ?> extension) {
|
||||
extensionsByNumber.put(
|
||||
new ObjectIntPair(extension.getContainingTypeDefaultInstance(),
|
||||
extension.getNumber()),
|
||||
extension);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Private stuff.
|
||||
|
||||
// Constructors are package-private so that ExtensionRegistry can subclass
|
||||
// this.
|
||||
|
||||
ExtensionRegistryLite() {
|
||||
this.extensionsByNumber =
|
||||
new HashMap<ObjectIntPair,
|
||||
GeneratedMessageLite.GeneratedExtension<?, ?>>();
|
||||
}
|
||||
|
||||
ExtensionRegistryLite(ExtensionRegistryLite other) {
|
||||
if (other == EMPTY) {
|
||||
this.extensionsByNumber = Collections.emptyMap();
|
||||
} else {
|
||||
this.extensionsByNumber =
|
||||
Collections.unmodifiableMap(other.extensionsByNumber);
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<ObjectIntPair,
|
||||
GeneratedMessageLite.GeneratedExtension<?, ?>>
|
||||
extensionsByNumber;
|
||||
|
||||
private ExtensionRegistryLite(boolean empty) {
|
||||
this.extensionsByNumber = Collections.emptyMap();
|
||||
}
|
||||
private static final ExtensionRegistryLite EMPTY =
|
||||
new ExtensionRegistryLite(true);
|
||||
|
||||
/** A (Object, int) pair, used as a map key. */
|
||||
private static final class ObjectIntPair {
|
||||
private final Object object;
|
||||
private final int number;
|
||||
|
||||
ObjectIntPair(final Object object, final int number) {
|
||||
this.object = object;
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return System.identityHashCode(object) * ((1 << 16) - 1) + number;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (!(obj instanceof ObjectIntPair)) {
|
||||
return false;
|
||||
}
|
||||
final ObjectIntPair other = (ObjectIntPair)obj;
|
||||
return object == other.object && number == other.number;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,861 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.LazyField.LazyIterator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A class which represents an arbitrary set of fields of some message type.
|
||||
* This is used to implement {@link DynamicMessage}, and also to represent
|
||||
* extensions in {@link GeneratedMessage}. This class is package-private,
|
||||
* since outside users should probably be using {@link DynamicMessage}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
final class FieldSet<FieldDescriptorType extends
|
||||
FieldSet.FieldDescriptorLite<FieldDescriptorType>> {
|
||||
/**
|
||||
* Interface for a FieldDescriptor or lite extension descriptor. This
|
||||
* prevents FieldSet from depending on {@link Descriptors.FieldDescriptor}.
|
||||
*/
|
||||
public interface FieldDescriptorLite<T extends FieldDescriptorLite<T>>
|
||||
extends Comparable<T> {
|
||||
int getNumber();
|
||||
WireFormat.FieldType getLiteType();
|
||||
WireFormat.JavaType getLiteJavaType();
|
||||
boolean isRepeated();
|
||||
boolean isPacked();
|
||||
Internal.EnumLiteMap<?> getEnumType();
|
||||
|
||||
// If getLiteJavaType() == MESSAGE, this merges a message object of the
|
||||
// type into a builder of the type. Returns {@code to}.
|
||||
MessageLite.Builder internalMergeFrom(
|
||||
MessageLite.Builder to, MessageLite from);
|
||||
}
|
||||
|
||||
private final SmallSortedMap<FieldDescriptorType, Object> fields;
|
||||
private boolean isImmutable;
|
||||
private boolean hasLazyField = false;
|
||||
|
||||
/** Construct a new FieldSet. */
|
||||
private FieldSet() {
|
||||
this.fields = SmallSortedMap.newFieldMap(16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an empty FieldSet. This is only used to initialize
|
||||
* DEFAULT_INSTANCE.
|
||||
*/
|
||||
private FieldSet(final boolean dummy) {
|
||||
this.fields = SmallSortedMap.newFieldMap(0);
|
||||
makeImmutable();
|
||||
}
|
||||
|
||||
/** Construct a new FieldSet. */
|
||||
public static <T extends FieldSet.FieldDescriptorLite<T>>
|
||||
FieldSet<T> newFieldSet() {
|
||||
return new FieldSet<T>();
|
||||
}
|
||||
|
||||
/** Get an immutable empty FieldSet. */
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends FieldSet.FieldDescriptorLite<T>>
|
||||
FieldSet<T> emptySet() {
|
||||
return DEFAULT_INSTANCE;
|
||||
}
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static final FieldSet DEFAULT_INSTANCE = new FieldSet(true);
|
||||
|
||||
/** Make this FieldSet immutable from this point forward. */
|
||||
@SuppressWarnings("unchecked")
|
||||
public void makeImmutable() {
|
||||
if (isImmutable) {
|
||||
return;
|
||||
}
|
||||
fields.makeImmutable();
|
||||
isImmutable = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 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());
|
||||
}
|
||||
clone.hasLazyField = hasLazyField;
|
||||
return clone;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
/** See {@link Message.Builder#clear()}. */
|
||||
public void clear() {
|
||||
fields.clear();
|
||||
hasLazyField = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a simple map containing all the fields.
|
||||
*/
|
||||
public Map<FieldDescriptorType, Object> getAllFields() {
|
||||
if (hasLazyField) {
|
||||
SmallSortedMap<FieldDescriptorType, Object> result =
|
||||
SmallSortedMap.newFieldMap(16);
|
||||
for (int i = 0; i < fields.getNumArrayEntries(); i++) {
|
||||
cloneFieldEntry(result, fields.getArrayEntryAt(i));
|
||||
}
|
||||
for (Map.Entry<FieldDescriptorType, Object> entry :
|
||||
fields.getOverflowEntries()) {
|
||||
cloneFieldEntry(result, entry);
|
||||
}
|
||||
if (fields.isImmutable()) {
|
||||
result.makeImmutable();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return fields.isImmutable() ? fields : Collections.unmodifiableMap(fields);
|
||||
}
|
||||
|
||||
private void cloneFieldEntry(Map<FieldDescriptorType, Object> map,
|
||||
Map.Entry<FieldDescriptorType, Object> entry) {
|
||||
FieldDescriptorType key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof LazyField) {
|
||||
map.put(key, ((LazyField) value).getValue());
|
||||
} else {
|
||||
map.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
if (hasLazyField) {
|
||||
return new LazyIterator<FieldDescriptorType>(
|
||||
fields.entrySet().iterator());
|
||||
}
|
||||
return fields.entrySet().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message#hasField(Descriptors.FieldDescriptor)}.
|
||||
*/
|
||||
public boolean hasField(final FieldDescriptorType descriptor) {
|
||||
if (descriptor.isRepeated()) {
|
||||
throw new IllegalArgumentException(
|
||||
"hasField() can only be called on non-repeated fields.");
|
||||
}
|
||||
|
||||
return fields.get(descriptor) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message#getField(Descriptors.FieldDescriptor)}. This method
|
||||
* returns {@code null} if the field is not set; in this case it is up
|
||||
* to the caller to fetch the field's default value.
|
||||
*/
|
||||
public Object getField(final FieldDescriptorType descriptor) {
|
||||
Object o = fields.get(descriptor);
|
||||
if (o instanceof LazyField) {
|
||||
return ((LazyField) o).getValue();
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message.Builder#setField(Descriptors.FieldDescriptor,Object)}.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void setField(final FieldDescriptorType descriptor,
|
||||
Object value) {
|
||||
if (descriptor.isRepeated()) {
|
||||
if (!(value instanceof List)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Wrong object type used with protocol message reflection.");
|
||||
}
|
||||
|
||||
// Wrap the contents in a new list so that the caller cannot change
|
||||
// the list's contents after setting it.
|
||||
final List newList = new ArrayList();
|
||||
newList.addAll((List) value);
|
||||
for (final Object element : newList) {
|
||||
verifyType(descriptor.getLiteType(), element);
|
||||
}
|
||||
value = newList;
|
||||
} else {
|
||||
verifyType(descriptor.getLiteType(), value);
|
||||
}
|
||||
|
||||
if (value instanceof LazyField) {
|
||||
hasLazyField = true;
|
||||
}
|
||||
fields.put(descriptor, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}.
|
||||
*/
|
||||
public void clearField(final FieldDescriptorType descriptor) {
|
||||
fields.remove(descriptor);
|
||||
if (fields.isEmpty()) {
|
||||
hasLazyField = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}.
|
||||
*/
|
||||
public int getRepeatedFieldCount(final FieldDescriptorType descriptor) {
|
||||
if (!descriptor.isRepeated()) {
|
||||
throw new IllegalArgumentException(
|
||||
"getRepeatedField() can only be called on repeated fields.");
|
||||
}
|
||||
|
||||
final Object value = getField(descriptor);
|
||||
if (value == null) {
|
||||
return 0;
|
||||
} else {
|
||||
return ((List<?>) value).size();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)}.
|
||||
*/
|
||||
public Object getRepeatedField(final FieldDescriptorType descriptor,
|
||||
final int index) {
|
||||
if (!descriptor.isRepeated()) {
|
||||
throw new IllegalArgumentException(
|
||||
"getRepeatedField() can only be called on repeated fields.");
|
||||
}
|
||||
|
||||
final Object value = getField(descriptor);
|
||||
|
||||
if (value == null) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else {
|
||||
return ((List<?>) value).get(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message.Builder#setRepeatedField(Descriptors.FieldDescriptor,int,Object)}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setRepeatedField(final FieldDescriptorType descriptor,
|
||||
final int index,
|
||||
final Object value) {
|
||||
if (!descriptor.isRepeated()) {
|
||||
throw new IllegalArgumentException(
|
||||
"getRepeatedField() can only be called on repeated fields.");
|
||||
}
|
||||
|
||||
final Object list = getField(descriptor);
|
||||
if (list == null) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
verifyType(descriptor.getLiteType(), value);
|
||||
((List<Object>) list).set(index, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message.Builder#addRepeatedField(Descriptors.FieldDescriptor,Object)}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void addRepeatedField(final FieldDescriptorType descriptor,
|
||||
final Object value) {
|
||||
if (!descriptor.isRepeated()) {
|
||||
throw new IllegalArgumentException(
|
||||
"addRepeatedField() can only be called on repeated fields.");
|
||||
}
|
||||
|
||||
verifyType(descriptor.getLiteType(), value);
|
||||
|
||||
final Object existingValue = getField(descriptor);
|
||||
List<Object> list;
|
||||
if (existingValue == null) {
|
||||
list = new ArrayList<Object>();
|
||||
fields.put(descriptor, list);
|
||||
} else {
|
||||
list = (List<Object>) existingValue;
|
||||
}
|
||||
|
||||
list.add(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the given object is of the correct type to be a valid
|
||||
* value for the given field. (For repeated fields, this checks if the
|
||||
* object is the right type to be one element of the field.)
|
||||
*
|
||||
* @throws IllegalArgumentException The value is not of the right type.
|
||||
*/
|
||||
private static void verifyType(final WireFormat.FieldType type,
|
||||
final Object value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
boolean isValid = false;
|
||||
switch (type.getJavaType()) {
|
||||
case INT: isValid = value instanceof Integer ; break;
|
||||
case LONG: isValid = value instanceof Long ; break;
|
||||
case FLOAT: isValid = value instanceof Float ; break;
|
||||
case DOUBLE: isValid = value instanceof Double ; break;
|
||||
case BOOLEAN: isValid = value instanceof Boolean ; break;
|
||||
case STRING: isValid = value instanceof String ; break;
|
||||
case BYTE_STRING: isValid = value instanceof ByteString; break;
|
||||
case ENUM:
|
||||
// TODO(kenton): Caller must do type checking here, I guess.
|
||||
isValid = value instanceof Internal.EnumLite;
|
||||
break;
|
||||
case MESSAGE:
|
||||
// TODO(kenton): Caller must do type checking here, I guess.
|
||||
isValid =
|
||||
(value instanceof MessageLite) || (value instanceof LazyField);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
// TODO(kenton): When chaining calls to setField(), it can be hard to
|
||||
// tell from the stack trace which exact call failed, since the whole
|
||||
// chain is considered one line of code. It would be nice to print
|
||||
// more information here, e.g. naming the field. We used to do that.
|
||||
// But we can't now that FieldSet doesn't use descriptors. Maybe this
|
||||
// isn't a big deal, though, since it would only really apply when using
|
||||
// reflection and generally people don't chain reflection setters.
|
||||
throw new IllegalArgumentException(
|
||||
"Wrong object type used with protocol message reflection.");
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Parsing and serialization
|
||||
|
||||
/**
|
||||
* See {@link Message#isInitialized()}. Note: Since {@code FieldSet}
|
||||
* itself does not have any way of knowing about required fields that
|
||||
* aren't actually present in the set, it is up to the caller to check
|
||||
* that all required fields are present.
|
||||
*/
|
||||
public boolean 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 {
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof MessageLite) {
|
||||
if (!((MessageLite) value).isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
} else if (value instanceof LazyField) {
|
||||
return true;
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Wrong object type used with protocol message reflection.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a field type, return the wire type.
|
||||
*
|
||||
* @returns One of the {@code WIRETYPE_} constants defined in
|
||||
* {@link WireFormat}.
|
||||
*/
|
||||
static int getWireFormatForFieldType(final WireFormat.FieldType type,
|
||||
boolean isPacked) {
|
||||
if (isPacked) {
|
||||
return WireFormat.WIRETYPE_LENGTH_DELIMITED;
|
||||
} else {
|
||||
return type.getWireType();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link Message.Builder#mergeFrom(Message)}, but merges from another
|
||||
* {@link FieldSet}.
|
||||
*/
|
||||
public void mergeFrom(final FieldSet<FieldDescriptorType> other) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private void mergeFromField(
|
||||
final Map.Entry<FieldDescriptorType, Object> entry) {
|
||||
final FieldDescriptorType descriptor = entry.getKey();
|
||||
Object otherValue = entry.getValue();
|
||||
if (otherValue instanceof LazyField) {
|
||||
otherValue = ((LazyField) otherValue).getValue();
|
||||
}
|
||||
|
||||
if (descriptor.isRepeated()) {
|
||||
Object value = getField(descriptor);
|
||||
if (value == null) {
|
||||
// Our list is empty, but we still need to make a defensive copy of
|
||||
// the other list since we don't know if the other FieldSet is still
|
||||
// mutable.
|
||||
fields.put(descriptor, new ArrayList((List) otherValue));
|
||||
} else {
|
||||
// Concatenate the lists.
|
||||
((List) value).addAll((List) otherValue);
|
||||
}
|
||||
} else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
|
||||
Object value = getField(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);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(kenton): Move static parsing and serialization methods into some
|
||||
// other class. Probably WireFormat.
|
||||
|
||||
/**
|
||||
* Read a field of any primitive type from a CodedInputStream. Enums,
|
||||
* groups, and embedded messages are not handled by this method.
|
||||
*
|
||||
* @param input The stream from which to read.
|
||||
* @param type Declared type of the field.
|
||||
* @return An object representing the field's value, of the exact
|
||||
* type which would be returned by
|
||||
* {@link Message#getField(Descriptors.FieldDescriptor)} for
|
||||
* this field.
|
||||
*/
|
||||
public static Object readPrimitiveField(
|
||||
CodedInputStream input,
|
||||
final WireFormat.FieldType type) throws IOException {
|
||||
switch (type) {
|
||||
case DOUBLE : return input.readDouble ();
|
||||
case FLOAT : return input.readFloat ();
|
||||
case INT64 : return input.readInt64 ();
|
||||
case UINT64 : return input.readUInt64 ();
|
||||
case INT32 : return input.readInt32 ();
|
||||
case FIXED64 : return input.readFixed64 ();
|
||||
case FIXED32 : return input.readFixed32 ();
|
||||
case BOOL : return input.readBool ();
|
||||
case STRING : return input.readString ();
|
||||
case BYTES : return input.readBytes ();
|
||||
case UINT32 : return input.readUInt32 ();
|
||||
case SFIXED32: return input.readSFixed32();
|
||||
case SFIXED64: return input.readSFixed64();
|
||||
case SINT32 : return input.readSInt32 ();
|
||||
case SINT64 : return input.readSInt64 ();
|
||||
|
||||
case GROUP:
|
||||
throw new IllegalArgumentException(
|
||||
"readPrimitiveField() cannot handle nested groups.");
|
||||
case MESSAGE:
|
||||
throw new IllegalArgumentException(
|
||||
"readPrimitiveField() cannot handle embedded messages.");
|
||||
case ENUM:
|
||||
// We don't handle enums because we don't know what to do if the
|
||||
// value is not recognized.
|
||||
throw new IllegalArgumentException(
|
||||
"readPrimitiveField() cannot handle enums.");
|
||||
}
|
||||
|
||||
throw new RuntimeException(
|
||||
"There is no way to get here, but the compiler thinks otherwise.");
|
||||
}
|
||||
|
||||
/** See {@link Message#writeTo(CodedOutputStream)}. */
|
||||
public void writeTo(final CodedOutputStream output)
|
||||
throws IOException {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #writeTo} but uses MessageSet wire format.
|
||||
*/
|
||||
public void writeMessageSetTo(final CodedOutputStream output)
|
||||
throws IOException {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a single tag-value pair to the stream.
|
||||
*
|
||||
* @param output The output stream.
|
||||
* @param type The field's type.
|
||||
* @param number The field's number.
|
||||
* @param value Object representing the field's value. Must be of the exact
|
||||
* type which would be returned by
|
||||
* {@link Message#getField(Descriptors.FieldDescriptor)} for
|
||||
* this field.
|
||||
*/
|
||||
private static void writeElement(final CodedOutputStream output,
|
||||
final WireFormat.FieldType type,
|
||||
final int number,
|
||||
final Object value) throws IOException {
|
||||
// Special case for groups, which need a start and end tag; other fields
|
||||
// can just use writeTag() and writeFieldNoTag().
|
||||
if (type == WireFormat.FieldType.GROUP) {
|
||||
output.writeGroup(number, (MessageLite) value);
|
||||
} else {
|
||||
output.writeTag(number, getWireFormatForFieldType(type, false));
|
||||
writeElementNoTag(output, type, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a field of arbitrary type, without its tag, to the stream.
|
||||
*
|
||||
* @param output The output stream.
|
||||
* @param type The field's type.
|
||||
* @param value Object representing the field's value. Must be of the exact
|
||||
* type which would be returned by
|
||||
* {@link Message#getField(Descriptors.FieldDescriptor)} for
|
||||
* this field.
|
||||
*/
|
||||
private static void writeElementNoTag(
|
||||
final CodedOutputStream output,
|
||||
final WireFormat.FieldType type,
|
||||
final Object value) throws IOException {
|
||||
switch (type) {
|
||||
case DOUBLE : output.writeDoubleNoTag ((Double ) value); break;
|
||||
case FLOAT : output.writeFloatNoTag ((Float ) value); break;
|
||||
case INT64 : output.writeInt64NoTag ((Long ) value); break;
|
||||
case UINT64 : output.writeUInt64NoTag ((Long ) value); break;
|
||||
case INT32 : output.writeInt32NoTag ((Integer ) value); break;
|
||||
case FIXED64 : output.writeFixed64NoTag ((Long ) value); break;
|
||||
case FIXED32 : output.writeFixed32NoTag ((Integer ) value); break;
|
||||
case BOOL : output.writeBoolNoTag ((Boolean ) value); break;
|
||||
case STRING : output.writeStringNoTag ((String ) value); break;
|
||||
case GROUP : output.writeGroupNoTag ((MessageLite) value); break;
|
||||
case MESSAGE : output.writeMessageNoTag ((MessageLite) value); break;
|
||||
case BYTES : output.writeBytesNoTag ((ByteString ) value); break;
|
||||
case UINT32 : output.writeUInt32NoTag ((Integer ) value); break;
|
||||
case SFIXED32: output.writeSFixed32NoTag((Integer ) value); break;
|
||||
case SFIXED64: output.writeSFixed64NoTag((Long ) value); break;
|
||||
case SINT32 : output.writeSInt32NoTag ((Integer ) value); break;
|
||||
case SINT64 : output.writeSInt64NoTag ((Long ) value); break;
|
||||
|
||||
case ENUM:
|
||||
output.writeEnumNoTag(((Internal.EnumLite) value).getNumber());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** Write a single field. */
|
||||
public static void writeField(final FieldDescriptorLite<?> descriptor,
|
||||
final Object value,
|
||||
final CodedOutputStream output)
|
||||
throws IOException {
|
||||
WireFormat.FieldType type = descriptor.getLiteType();
|
||||
int number = descriptor.getNumber();
|
||||
if (descriptor.isRepeated()) {
|
||||
final List<?> valueList = (List<?>)value;
|
||||
if (descriptor.isPacked()) {
|
||||
output.writeTag(number, WireFormat.WIRETYPE_LENGTH_DELIMITED);
|
||||
// Compute the total data size so the length can be written.
|
||||
int dataSize = 0;
|
||||
for (final Object element : valueList) {
|
||||
dataSize += computeElementSizeNoTag(type, element);
|
||||
}
|
||||
output.writeRawVarint32(dataSize);
|
||||
// Write the data itself, without any tags.
|
||||
for (final Object element : valueList) {
|
||||
writeElementNoTag(output, type, element);
|
||||
}
|
||||
} else {
|
||||
for (final Object element : valueList) {
|
||||
writeElement(output, type, number, element);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (value instanceof LazyField) {
|
||||
writeElement(output, type, number, ((LazyField) value).getValue());
|
||||
} else {
|
||||
writeElement(output, type, number, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link Message#getSerializedSize()}. It's up to the caller to cache
|
||||
* the resulting size if desired.
|
||||
*/
|
||||
public int getSerializedSize() {
|
||||
int size = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #getSerializedSize} but uses MessageSet wire format.
|
||||
*/
|
||||
public int getMessageSetSerializedSize() {
|
||||
int size = 0;
|
||||
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();
|
||||
Object value = entry.getValue();
|
||||
if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE
|
||||
&& !descriptor.isRepeated() && !descriptor.isPacked()) {
|
||||
if (value instanceof LazyField) {
|
||||
return CodedOutputStream.computeLazyFieldMessageSetExtensionSize(
|
||||
entry.getKey().getNumber(), (LazyField) value);
|
||||
} else {
|
||||
return CodedOutputStream.computeMessageSetExtensionSize(
|
||||
entry.getKey().getNumber(), (MessageLite) value);
|
||||
}
|
||||
} else {
|
||||
return computeFieldSize(descriptor, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the number of bytes that would be needed to encode a
|
||||
* single tag/value pair of arbitrary type.
|
||||
*
|
||||
* @param type The field's type.
|
||||
* @param number The field's number.
|
||||
* @param value Object representing the field's value. Must be of the exact
|
||||
* type which would be returned by
|
||||
* {@link Message#getField(Descriptors.FieldDescriptor)} for
|
||||
* this field.
|
||||
*/
|
||||
private static int computeElementSize(
|
||||
final WireFormat.FieldType type,
|
||||
final int number, final Object value) {
|
||||
int tagSize = CodedOutputStream.computeTagSize(number);
|
||||
if (type == WireFormat.FieldType.GROUP) {
|
||||
tagSize *= 2;
|
||||
}
|
||||
return tagSize + computeElementSizeNoTag(type, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the number of bytes that would be needed to encode a
|
||||
* particular value of arbitrary type, excluding tag.
|
||||
*
|
||||
* @param type The field's type.
|
||||
* @param value Object representing the field's value. Must be of the exact
|
||||
* type which would be returned by
|
||||
* {@link Message#getField(Descriptors.FieldDescriptor)} for
|
||||
* this field.
|
||||
*/
|
||||
private static int computeElementSizeNoTag(
|
||||
final WireFormat.FieldType type, final Object value) {
|
||||
switch (type) {
|
||||
// Note: Minor violation of 80-char limit rule here because this would
|
||||
// actually be harder to read if we wrapped the lines.
|
||||
case DOUBLE : return CodedOutputStream.computeDoubleSizeNoTag ((Double )value);
|
||||
case FLOAT : return CodedOutputStream.computeFloatSizeNoTag ((Float )value);
|
||||
case INT64 : return CodedOutputStream.computeInt64SizeNoTag ((Long )value);
|
||||
case UINT64 : return CodedOutputStream.computeUInt64SizeNoTag ((Long )value);
|
||||
case INT32 : return CodedOutputStream.computeInt32SizeNoTag ((Integer )value);
|
||||
case FIXED64 : return CodedOutputStream.computeFixed64SizeNoTag ((Long )value);
|
||||
case FIXED32 : return CodedOutputStream.computeFixed32SizeNoTag ((Integer )value);
|
||||
case BOOL : return CodedOutputStream.computeBoolSizeNoTag ((Boolean )value);
|
||||
case STRING : return CodedOutputStream.computeStringSizeNoTag ((String )value);
|
||||
case GROUP : return CodedOutputStream.computeGroupSizeNoTag ((MessageLite)value);
|
||||
case BYTES : return CodedOutputStream.computeBytesSizeNoTag ((ByteString )value);
|
||||
case UINT32 : return CodedOutputStream.computeUInt32SizeNoTag ((Integer )value);
|
||||
case SFIXED32: return CodedOutputStream.computeSFixed32SizeNoTag((Integer )value);
|
||||
case SFIXED64: return CodedOutputStream.computeSFixed64SizeNoTag((Long )value);
|
||||
case SINT32 : return CodedOutputStream.computeSInt32SizeNoTag ((Integer )value);
|
||||
case SINT64 : return CodedOutputStream.computeSInt64SizeNoTag ((Long )value);
|
||||
|
||||
case MESSAGE:
|
||||
if (value instanceof LazyField) {
|
||||
return CodedOutputStream.computeLazyFieldSizeNoTag((LazyField) value);
|
||||
} else {
|
||||
return CodedOutputStream.computeMessageSizeNoTag((MessageLite) value);
|
||||
}
|
||||
|
||||
case ENUM:
|
||||
return CodedOutputStream.computeEnumSizeNoTag(
|
||||
((Internal.EnumLite) value).getNumber());
|
||||
}
|
||||
|
||||
throw new RuntimeException(
|
||||
"There is no way to get here, but the compiler thinks otherwise.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the number of bytes needed to encode a particular field.
|
||||
*/
|
||||
public static int computeFieldSize(final FieldDescriptorLite<?> descriptor,
|
||||
final Object value) {
|
||||
WireFormat.FieldType type = descriptor.getLiteType();
|
||||
int number = descriptor.getNumber();
|
||||
if (descriptor.isRepeated()) {
|
||||
if (descriptor.isPacked()) {
|
||||
int dataSize = 0;
|
||||
for (final Object element : (List<?>)value) {
|
||||
dataSize += computeElementSizeNoTag(type, element);
|
||||
}
|
||||
return dataSize +
|
||||
CodedOutputStream.computeTagSize(number) +
|
||||
CodedOutputStream.computeRawVarint32Size(dataSize);
|
||||
} else {
|
||||
int size = 0;
|
||||
for (final Object element : (List<?>)value) {
|
||||
size += computeElementSize(type, number, element);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
} else {
|
||||
return computeElementSize(type, number, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,797 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
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;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Lite version of {@link GeneratedMessage}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public abstract class GeneratedMessageLite extends AbstractMessageLite
|
||||
implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
protected GeneratedMessageLite() {
|
||||
}
|
||||
|
||||
protected GeneratedMessageLite(Builder builder) {
|
||||
}
|
||||
|
||||
public Parser<? extends MessageLite> getParserForType() {
|
||||
throw new UnsupportedOperationException(
|
||||
"This is supposed to be overridden by subclasses.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by subclasses to parse an unknown field.
|
||||
* @return {@code true} unless the tag is an end-group tag.
|
||||
*/
|
||||
protected boolean parseUnknownField(
|
||||
CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
int tag) throws IOException {
|
||||
return input.skipField(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by parsing constructors in generated classes.
|
||||
*/
|
||||
protected void makeExtensionsImmutable() {
|
||||
// Noop for messages without extensions.
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public abstract static class Builder<MessageType extends GeneratedMessageLite,
|
||||
BuilderType extends Builder>
|
||||
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.
|
||||
@Override
|
||||
public BuilderType clone() {
|
||||
throw new UnsupportedOperationException(
|
||||
"This is supposed to be overridden by subclasses.");
|
||||
}
|
||||
|
||||
/** All subclasses implement this. */
|
||||
public abstract BuilderType mergeFrom(MessageType message);
|
||||
|
||||
// Defined here for return type covariance.
|
||||
public abstract MessageType getDefaultInstanceForType();
|
||||
|
||||
/**
|
||||
* Called by subclasses to parse an unknown field.
|
||||
* @return {@code true} unless the tag is an end-group tag.
|
||||
*/
|
||||
protected boolean parseUnknownField(
|
||||
CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
int tag) throws IOException {
|
||||
return input.skipField(tag);
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// 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
|
||||
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) {
|
||||
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. */
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
@SuppressWarnings("unchecked")
|
||||
public final <Type> Type getExtension(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension,
|
||||
final int index) {
|
||||
verifyExtensionContainingType(extension);
|
||||
return (Type) extensions.getRepeatedField(extension.descriptor, index);
|
||||
}
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
@Override
|
||||
protected boolean parseUnknownField(
|
||||
CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
int tag) throws IOException {
|
||||
return GeneratedMessageLite.parseUnknownField(
|
||||
extensions,
|
||||
getDefaultInstanceForType(),
|
||||
input,
|
||||
extensionRegistry,
|
||||
tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by parsing constructors in generated classes.
|
||||
*/
|
||||
@Override
|
||||
protected void makeExtensionsImmutable() {
|
||||
extensions.makeImmutable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by subclasses to serialize extensions. Extension ranges may be
|
||||
* interleaved with field numbers, but we must write them in canonical
|
||||
* (sorted by field number) order. ExtensionWriter helps us write
|
||||
* individual ranges of extensions at once.
|
||||
*/
|
||||
protected class ExtensionWriter {
|
||||
// Imagine how much simpler this code would be if Java iterators had
|
||||
// a way to get the next element without advancing the iterator.
|
||||
|
||||
private final Iterator<Map.Entry<ExtensionDescriptor, Object>> iter =
|
||||
extensions.iterator();
|
||||
private Map.Entry<ExtensionDescriptor, Object> next;
|
||||
private final boolean messageSetWireFormat;
|
||||
|
||||
private ExtensionWriter(boolean messageSetWireFormat) {
|
||||
if (iter.hasNext()) {
|
||||
next = iter.next();
|
||||
}
|
||||
this.messageSetWireFormat = messageSetWireFormat;
|
||||
}
|
||||
|
||||
public void writeUntil(final int end, final CodedOutputStream output)
|
||||
throws IOException {
|
||||
while (next != null && next.getKey().getNumber() < end) {
|
||||
ExtensionDescriptor extension = next.getKey();
|
||||
if (messageSetWireFormat && extension.getLiteJavaType() ==
|
||||
WireFormat.JavaType.MESSAGE &&
|
||||
!extension.isRepeated()) {
|
||||
output.writeMessageSetExtension(extension.getNumber(),
|
||||
(MessageLite) next.getValue());
|
||||
} else {
|
||||
FieldSet.writeField(extension, next.getValue(), output);
|
||||
}
|
||||
if (iter.hasNext()) {
|
||||
next = iter.next();
|
||||
} else {
|
||||
next = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected ExtensionWriter newExtensionWriter() {
|
||||
return new ExtensionWriter(false);
|
||||
}
|
||||
protected ExtensionWriter newMessageSetExtensionWriter() {
|
||||
return new ExtensionWriter(true);
|
||||
}
|
||||
|
||||
/** Called by subclasses to compute the size of extensions. */
|
||||
protected int extensionsSerializedSize() {
|
||||
return extensions.getSerializedSize();
|
||||
}
|
||||
protected int extensionsSerializedSizeAsMessageSet() {
|
||||
return extensions.getMessageSetSerializedSize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lite equivalent of {@link GeneratedMessage.ExtendableBuilder}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public abstract static class ExtendableBuilder<
|
||||
MessageType extends ExtendableMessage<MessageType>,
|
||||
BuilderType extends ExtendableBuilder<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.
|
||||
@Override
|
||||
public BuilderType clone() {
|
||||
throw new UnsupportedOperationException(
|
||||
"This is supposed to be overridden by subclasses.");
|
||||
}
|
||||
|
||||
/** Set the value of an extension. */
|
||||
public final <Type> BuilderType setExtension(
|
||||
final GeneratedExtension<MessageType, Type> extension,
|
||||
final Type value) {
|
||||
verifyExtensionContainingType(extension);
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.setField(extension.descriptor, value);
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
/** Set the value of one element of a repeated extension. */
|
||||
public final <Type> BuilderType setExtension(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension,
|
||||
final int index, final Type value) {
|
||||
verifyExtensionContainingType(extension);
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.setRepeatedField(extension.descriptor, index, value);
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
/** Append a value to a repeated extension. */
|
||||
public final <Type> BuilderType addExtension(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension,
|
||||
final Type value) {
|
||||
verifyExtensionContainingType(extension);
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.addRepeatedField(extension.descriptor, value);
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
/** Clear an extension. */
|
||||
public final <Type> BuilderType clearExtension(
|
||||
final GeneratedExtension<MessageType, ?> extension) {
|
||||
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.
|
||||
*/
|
||||
@Override
|
||||
protected boolean parseUnknownField(
|
||||
CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
int tag) throws IOException {
|
||||
ensureExtensionsIsMutable();
|
||||
return GeneratedMessageLite.parseUnknownField(
|
||||
extensions,
|
||||
getDefaultInstanceForType(),
|
||||
input,
|
||||
extensionRegistry,
|
||||
tag);
|
||||
}
|
||||
|
||||
protected final void mergeExtensionFields(final MessageType other) {
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.mergeFrom(((ExtendableMessage) other).extensions);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Parse an unknown field or an extension.
|
||||
* @return {@code true} unless the tag is an end-group tag.
|
||||
*/
|
||||
private static <MessageType extends MessageLite>
|
||||
boolean parseUnknownField(
|
||||
FieldSet<ExtensionDescriptor> extensions,
|
||||
MessageType defaultInstance,
|
||||
CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
int tag) throws IOException {
|
||||
int wireType = WireFormat.getTagWireType(tag);
|
||||
int fieldNumber = WireFormat.getTagFieldNumber(tag);
|
||||
|
||||
GeneratedExtension<MessageType, ?> extension =
|
||||
extensionRegistry.findLiteExtensionByNumber(
|
||||
defaultInstance, fieldNumber);
|
||||
|
||||
boolean unknown = false;
|
||||
boolean packed = false;
|
||||
if (extension == null) {
|
||||
unknown = true; // Unknown field.
|
||||
} else if (wireType == FieldSet.getWireFormatForFieldType(
|
||||
extension.descriptor.getLiteType(),
|
||||
false /* isPacked */)) {
|
||||
packed = false; // Normal, unpacked value.
|
||||
} else if (extension.descriptor.isRepeated &&
|
||||
extension.descriptor.type.isPackable() &&
|
||||
wireType == FieldSet.getWireFormatForFieldType(
|
||||
extension.descriptor.getLiteType(),
|
||||
true /* isPacked */)) {
|
||||
packed = true; // Packed value.
|
||||
} else {
|
||||
unknown = true; // Wrong wire type.
|
||||
}
|
||||
|
||||
if (unknown) { // Unknown field or wrong wire type. Skip.
|
||||
return input.skipField(tag);
|
||||
}
|
||||
|
||||
if (packed) {
|
||||
int length = input.readRawVarint32();
|
||||
int limit = input.pushLimit(length);
|
||||
if (extension.descriptor.getLiteType() == WireFormat.FieldType.ENUM) {
|
||||
while (input.getBytesUntilLimit() > 0) {
|
||||
int rawValue = input.readEnum();
|
||||
Object value =
|
||||
extension.descriptor.getEnumType().findValueByNumber(rawValue);
|
||||
if (value == null) {
|
||||
// If the number isn't recognized as a valid value for this
|
||||
// enum, drop it (don't even add it to unknownFields).
|
||||
return true;
|
||||
}
|
||||
extensions.addRepeatedField(extension.descriptor, value);
|
||||
}
|
||||
} else {
|
||||
while (input.getBytesUntilLimit() > 0) {
|
||||
Object value =
|
||||
FieldSet.readPrimitiveField(input,
|
||||
extension.descriptor.getLiteType());
|
||||
extensions.addRepeatedField(extension.descriptor, value);
|
||||
}
|
||||
}
|
||||
input.popLimit(limit);
|
||||
} else {
|
||||
Object value;
|
||||
switch (extension.descriptor.getLiteJavaType()) {
|
||||
case MESSAGE: {
|
||||
MessageLite.Builder subBuilder = null;
|
||||
if (!extension.descriptor.isRepeated()) {
|
||||
MessageLite existingValue =
|
||||
(MessageLite) extensions.getField(extension.descriptor);
|
||||
if (existingValue != null) {
|
||||
subBuilder = existingValue.toBuilder();
|
||||
}
|
||||
}
|
||||
if (subBuilder == null) {
|
||||
subBuilder = extension.messageDefaultInstance.newBuilderForType();
|
||||
}
|
||||
if (extension.descriptor.getLiteType() ==
|
||||
WireFormat.FieldType.GROUP) {
|
||||
input.readGroup(extension.getNumber(),
|
||||
subBuilder, extensionRegistry);
|
||||
} else {
|
||||
input.readMessage(subBuilder, extensionRegistry);
|
||||
}
|
||||
value = subBuilder.build();
|
||||
break;
|
||||
}
|
||||
case ENUM:
|
||||
int rawValue = input.readEnum();
|
||||
value = extension.descriptor.getEnumType()
|
||||
.findValueByNumber(rawValue);
|
||||
// If the number isn't recognized as a valid value for this enum,
|
||||
// drop it.
|
||||
if (value == null) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
value = FieldSet.readPrimitiveField(input,
|
||||
extension.descriptor.getLiteType());
|
||||
break;
|
||||
}
|
||||
|
||||
if (extension.descriptor.isRepeated()) {
|
||||
extensions.addRepeatedField(extension.descriptor, value);
|
||||
} else {
|
||||
extensions.setField(extension.descriptor, value);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
/** For use by generated code only. */
|
||||
public static <ContainingType extends MessageLite, Type>
|
||||
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
|
||||
implements FieldSet.FieldDescriptorLite<
|
||||
ExtensionDescriptor> {
|
||||
private ExtensionDescriptor(
|
||||
final Internal.EnumLiteMap<?> enumTypeMap,
|
||||
final int number,
|
||||
final WireFormat.FieldType type,
|
||||
final boolean isRepeated,
|
||||
final boolean isPacked) {
|
||||
this.enumTypeMap = enumTypeMap;
|
||||
this.number = number;
|
||||
this.type = type;
|
||||
this.isRepeated = isRepeated;
|
||||
this.isPacked = isPacked;
|
||||
}
|
||||
|
||||
private final Internal.EnumLiteMap<?> enumTypeMap;
|
||||
private final int number;
|
||||
private final WireFormat.FieldType type;
|
||||
private final boolean isRepeated;
|
||||
private final boolean isPacked;
|
||||
|
||||
public int getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public WireFormat.FieldType getLiteType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public WireFormat.JavaType getLiteJavaType() {
|
||||
return type.getJavaType();
|
||||
}
|
||||
|
||||
public boolean isRepeated() {
|
||||
return isRepeated;
|
||||
}
|
||||
|
||||
public boolean isPacked() {
|
||||
return isPacked;
|
||||
}
|
||||
|
||||
public Internal.EnumLiteMap<?> getEnumType() {
|
||||
return enumTypeMap;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public MessageLite.Builder internalMergeFrom(
|
||||
MessageLite.Builder to, MessageLite from) {
|
||||
return ((Builder) to).mergeFrom((GeneratedMessageLite) from);
|
||||
}
|
||||
|
||||
public int compareTo(ExtensionDescriptor other) {
|
||||
return number - other.number;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lite equivalent to {@link GeneratedMessage.GeneratedExtension}.
|
||||
*
|
||||
* Users should ignore the contents of this class and only use objects of
|
||||
* this type as parameters to extension accessors and ExtensionRegistry.add().
|
||||
*/
|
||||
public static final class GeneratedExtension<
|
||||
ContainingType extends MessageLite, Type> {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
public ContainingType getContainingTypeDefaultInstance() {
|
||||
return containingTypeDefaultInstance;
|
||||
}
|
||||
|
||||
/** Get the field number. */
|
||||
public int getNumber() {
|
||||
return descriptor.getNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* If the extension is an embedded message, this is the default instance of
|
||||
* that type.
|
||||
*/
|
||||
public MessageLite getMessageDefaultInstance() {
|
||||
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();}
|
||||
* @return a SerializedForm of this message
|
||||
*/
|
||||
protected Object writeReplace() throws ObjectStreamException {
|
||||
return new SerializedForm(this);
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* The classes contained within are used internally by the Protocol Buffer
|
||||
* library and generated message implementations. They are public only because
|
||||
* those generated messages do not reside in the {@code protobuf} package.
|
||||
* Others should not use this class directly.
|
||||
*
|
||||
* @author kenton@google.com (Kenton Varda)
|
||||
*/
|
||||
public class Internal {
|
||||
/**
|
||||
* Helper called by generated code to construct default values for string
|
||||
* fields.
|
||||
* <p>
|
||||
* The protocol compiler does not actually contain a UTF-8 decoder -- it
|
||||
* just pushes UTF-8-encoded text around without touching it. The one place
|
||||
* where this presents a problem is when generating Java string literals.
|
||||
* Unicode characters in the string literal would normally need to be encoded
|
||||
* using a Unicode escape sequence, which would require decoding them.
|
||||
* To get around this, protoc instead embeds the UTF-8 bytes into the
|
||||
* generated code and leaves it to the runtime library to decode them.
|
||||
* <p>
|
||||
* It gets worse, though. If protoc just generated a byte array, like:
|
||||
* new byte[] {0x12, 0x34, 0x56, 0x78}
|
||||
* Java actually generates *code* which allocates an array and then fills
|
||||
* in each value. This is much less efficient than just embedding the bytes
|
||||
* directly into the bytecode. To get around this, we need another
|
||||
* work-around. String literals are embedded directly, so protoc actually
|
||||
* generates a string literal corresponding to the bytes. The easiest way
|
||||
* to do this is to use the ISO-8859-1 character set, which corresponds to
|
||||
* the first 256 characters of the Unicode range. Protoc can then use
|
||||
* good old CEscape to generate the string.
|
||||
* <p>
|
||||
* So we have a string literal which represents a set of bytes which
|
||||
* represents another string. This function -- stringDefaultValue --
|
||||
* converts from the generated string to the string we actually want. The
|
||||
* generated code calls this automatically.
|
||||
*/
|
||||
public static String stringDefaultValue(String bytes) {
|
||||
try {
|
||||
return new String(bytes.getBytes("ISO-8859-1"), "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// This should never happen since all JVMs are required to implement
|
||||
// both of the above character sets.
|
||||
throw new IllegalStateException(
|
||||
"Java VM does not support a standard character set.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper called by generated code to construct default values for bytes
|
||||
* fields.
|
||||
* <p>
|
||||
* This is a lot like {@link #stringDefaultValue}, but for bytes fields.
|
||||
* In this case we only need the second of the two hacks -- allowing us to
|
||||
* embed raw bytes as a string literal with ISO-8859-1 encoding.
|
||||
*/
|
||||
public static ByteString bytesDefaultValue(String bytes) {
|
||||
try {
|
||||
return ByteString.copyFrom(bytes.getBytes("ISO-8859-1"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// This should never happen since all JVMs are required to implement
|
||||
// ISO-8859-1.
|
||||
throw new IllegalStateException(
|
||||
"Java VM does not support a standard character set.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper called by generated code to 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. More precisely, returns {@code true} whenever:
|
||||
* <pre> {@code
|
||||
* Arrays.equals(byteString.toByteArray(),
|
||||
* new String(byteString.toByteArray(), "UTF-8").getBytes("UTF-8"))
|
||||
* }</pre>
|
||||
*
|
||||
* <p>This method rejects "overlong" byte sequences, as well as
|
||||
* 3-byte sequences that would map to a surrogate character, in
|
||||
* accordance with the restricted definition of UTF-8 introduced in
|
||||
* Unicode 3.1. Note that the UTF-8 decoder included in Oracle's
|
||||
* JDK has been modified to also reject "overlong" byte sequences,
|
||||
* but currently (2011) still accepts 3-byte surrogate character
|
||||
* byte sequences.
|
||||
*
|
||||
* <p>See the Unicode Standard,</br>
|
||||
* Table 3-6. <em>UTF-8 Bit Distribution</em>,</br>
|
||||
* Table 3-7. <em>Well Formed UTF-8 Byte Sequences</em>.
|
||||
*
|
||||
* <p>As of 2011-02, this method simply returns the result of {@link
|
||||
* ByteString#isValidUtf8()}. Calling that method directly is preferred.
|
||||
*
|
||||
* @param byteString the string to check
|
||||
* @return whether the byte array is round trippable
|
||||
*/
|
||||
public static boolean isValidUtf8(ByteString byteString) {
|
||||
return byteString.isValidUtf8();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* library stores EnumValueDescriptors in order to better support reflection.
|
||||
*/
|
||||
public interface EnumLite {
|
||||
int getNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for an object which maps integers to {@link EnumLite}s.
|
||||
* {@link Descriptors.EnumDescriptor} implements this interface by mapping
|
||||
* numbers to {@link Descriptors.EnumValueDescriptor}s. Additionally,
|
||||
* every generated enum type has a static method internalGetValueMap() which
|
||||
* returns an implementation of this type that maps numbers to enum values.
|
||||
*/
|
||||
public interface EnumLiteMap<T extends EnumLite> {
|
||||
T findValueByNumber(int number);
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Thrown when a protocol message being parsed is invalid in some way,
|
||||
* e.g. it contains a malformed varint or a negative byte length.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class InvalidProtocolBufferException extends IOException {
|
||||
private static final long serialVersionUID = -1616151763072450476L;
|
||||
private MessageLite unfinishedMessage = null;
|
||||
|
||||
public InvalidProtocolBufferException(final String description) {
|
||||
super(description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches an unfinished message to the exception to support best-effort
|
||||
* parsing in {@code Parser} interface.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public InvalidProtocolBufferException setUnfinishedMessage(
|
||||
MessageLite unfinishedMessage) {
|
||||
this.unfinishedMessage = unfinishedMessage;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unfinished message attached to the exception, or null if
|
||||
* no message is attached.
|
||||
*/
|
||||
public MessageLite getUnfinishedMessage() {
|
||||
return unfinishedMessage;
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException truncatedMessage() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"While parsing a protocol message, the input ended unexpectedly " +
|
||||
"in the middle of a field. This could mean either than the " +
|
||||
"input has been truncated or that an embedded message " +
|
||||
"misreported its own length.");
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException negativeSize() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"CodedInputStream encountered an embedded string or message " +
|
||||
"which claimed to have negative size.");
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException malformedVarint() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"CodedInputStream encountered a malformed varint.");
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException invalidTag() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"Protocol message contained an invalid tag (zero).");
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException invalidEndTag() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"Protocol message end-group tag did not match expected tag.");
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException invalidWireType() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"Protocol message tag had invalid wire type.");
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException recursionLimitExceeded() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"Protocol message had too many levels of nesting. May be malicious. " +
|
||||
"Use CodedInputStream.setRecursionLimit() to increase the depth limit.");
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException sizeLimitExceeded() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"Protocol message was too large. May be malicious. " +
|
||||
"Use CodedInputStream.setSizeLimit() to increase the size limit.");
|
||||
}
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* LazyField encapsulates the logic of lazily parsing message fields. It stores
|
||||
* the message in a ByteString initially and then parse it on-demand.
|
||||
*
|
||||
* LazyField is thread-compatible e.g. concurrent read are safe, however,
|
||||
* synchronizations are needed under read/write situations.
|
||||
*
|
||||
* Now LazyField is only used to lazily load MessageSet.
|
||||
* TODO(xiangl): Use LazyField to lazily load all messages.
|
||||
*
|
||||
* @author xiangl@google.com (Xiang Li)
|
||||
*/
|
||||
class LazyField {
|
||||
|
||||
final private MessageLite defaultInstance;
|
||||
final private ExtensionRegistryLite extensionRegistry;
|
||||
|
||||
// Mutable because it is initialized lazily.
|
||||
private ByteString bytes;
|
||||
private volatile MessageLite value;
|
||||
private volatile boolean isDirty = false;
|
||||
|
||||
public LazyField(MessageLite defaultInstance,
|
||||
ExtensionRegistryLite extensionRegistry, ByteString bytes) {
|
||||
this.defaultInstance = defaultInstance;
|
||||
this.extensionRegistry = extensionRegistry;
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
public MessageLite getValue() {
|
||||
ensureInitialized();
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* LazyField is not thread-safe for write access. Synchronizations are needed
|
||||
* under read/write situations.
|
||||
*/
|
||||
public MessageLite setValue(MessageLite value) {
|
||||
MessageLite originalValue = this.value;
|
||||
this.value = value;
|
||||
bytes = null;
|
||||
isDirty = true;
|
||||
return originalValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Due to the optional field can be duplicated at the end of serialized
|
||||
* bytes, which will make the serialized size changed after LazyField
|
||||
* parsed. Be careful when using this method.
|
||||
*/
|
||||
public int getSerializedSize() {
|
||||
if (isDirty) {
|
||||
return value.getSerializedSize();
|
||||
}
|
||||
return bytes.size();
|
||||
}
|
||||
|
||||
public ByteString toByteString() {
|
||||
if (!isDirty) {
|
||||
return bytes;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (!isDirty) {
|
||||
return bytes;
|
||||
}
|
||||
bytes = value.toByteString();
|
||||
isDirty = false;
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
ensureInitialized();
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
ensureInitialized();
|
||||
return value.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ensureInitialized();
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
private void ensureInitialized() {
|
||||
if (value != null) {
|
||||
return;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (value != null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (bytes != null) {
|
||||
value = defaultInstance.getParserForType()
|
||||
.parseFrom(bytes, extensionRegistry);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// TODO(xiangl): Refactory the API to support the exception thrown from
|
||||
// lazily load messages.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================
|
||||
|
||||
/**
|
||||
* LazyEntry and LazyIterator are used to encapsulate the LazyField, when
|
||||
* users iterate all fields from FieldSet.
|
||||
*/
|
||||
static class LazyEntry<K> implements Entry<K, Object> {
|
||||
private Entry<K, LazyField> entry;
|
||||
|
||||
private LazyEntry(Entry<K, LazyField> entry) {
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
public K getKey() {
|
||||
return entry.getKey();
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
LazyField field = entry.getValue();
|
||||
if (field == null) {
|
||||
return null;
|
||||
}
|
||||
return field.getValue();
|
||||
}
|
||||
|
||||
public LazyField getField() {
|
||||
return entry.getValue();
|
||||
}
|
||||
|
||||
public Object setValue(Object value) {
|
||||
if (!(value instanceof MessageLite)) {
|
||||
throw new IllegalArgumentException(
|
||||
"LazyField now only used for MessageSet, "
|
||||
+ "and the value of MessageSet must be an instance of MessageLite");
|
||||
}
|
||||
return entry.getValue().setValue((MessageLite) value);
|
||||
}
|
||||
}
|
||||
|
||||
static class LazyIterator<K> implements Iterator<Entry<K, Object>> {
|
||||
private Iterator<Entry<K, Object>> iterator;
|
||||
|
||||
public LazyIterator(Iterator<Entry<K, Object>> iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Entry<K, Object> next() {
|
||||
Entry<K, ?> entry = iterator.next();
|
||||
if (entry.getValue() instanceof LazyField) {
|
||||
return new LazyEntry<K>((Entry<K, LazyField>) entry);
|
||||
}
|
||||
return (Entry<K, Object>) entry;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.RandomAccess;
|
||||
|
||||
/**
|
||||
* An implementation of {@link LazyStringList} that wraps an ArrayList. Each
|
||||
* element is either a ByteString or a String. It caches the last one requested
|
||||
* which is most likely the one needed next. This minimizes memory usage while
|
||||
* satisfying the most common use cases.
|
||||
* <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(LazyStringList from) {
|
||||
list = new ArrayList<Object>(from.size());
|
||||
addAll(from);
|
||||
}
|
||||
|
||||
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 (bs.isValidUtf8()) {
|
||||
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(Collection<? extends String> c) {
|
||||
// The default implementation of AbstractCollection.addAll(Collection)
|
||||
// delegates to add(Object). This implementation instead delegates to
|
||||
// addAll(int, Collection), which makes a special case for Collections
|
||||
// which are instances of LazyStringList.
|
||||
return addAll(size(), c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends String> c) {
|
||||
// When copying from another LazyStringList, directly copy the underlying
|
||||
// elements rather than forcing each element to be decoded to a String.
|
||||
Collection<?> collection = c instanceof LazyStringList
|
||||
? ((LazyStringList) c).getUnderlyingElements() : c;
|
||||
boolean ret = list.addAll(index, collection);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
public List<?> getUnderlyingElements() {
|
||||
return Collections.unmodifiableList(list);
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An interface extending {@code List<String>} that also provides access to the
|
||||
* items of the list as UTF8-encoded ByteString objects. This is used by the
|
||||
* protocol buffer implementation to support lazily converting bytes parsed
|
||||
* over the wire to String objects until needed and also increases the
|
||||
* efficiency of serialization if the String was never requested as the
|
||||
* ByteString is already cached.
|
||||
* <p>
|
||||
* This only adds additional methods that are required for the use in the
|
||||
* protocol buffer code in order to be able successfully round trip byte arrays
|
||||
* through parsing and serialization without conversion to strings. It's not
|
||||
* attempting to support the functionality of say {@code List<ByteString>}, hence
|
||||
* why only these two very specific methods are added.
|
||||
*
|
||||
* @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
|
||||
* ({@code index < 0 || index >= size()})
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable List of the underlying elements, each of
|
||||
* which is either a {@code String} or its equivalent UTF-8 encoded
|
||||
* {@code ByteString}. It is an error for the caller to modify the returned
|
||||
* List, and attempting to do so will result in an
|
||||
* {@link UnsupportedOperationException}.
|
||||
*/
|
||||
List<?> getUnderlyingElements();
|
||||
}
|
||||
@@ -1,349 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* This class implements a {@link com.google.protobuf.ByteString} backed by a
|
||||
* single array of bytes, contiguous in memory. It supports substring by
|
||||
* pointing to only a sub-range of the underlying byte array, meaning that a
|
||||
* substring will reference the full byte-array of the string it's made from,
|
||||
* exactly as with {@link String}.
|
||||
*
|
||||
* @author carlanton@google.com (Carl Haverl)
|
||||
*/
|
||||
class LiteralByteString extends ByteString {
|
||||
|
||||
protected final byte[] bytes;
|
||||
|
||||
/**
|
||||
* Creates a {@code LiteralByteString} backed by the given array, without
|
||||
* copying.
|
||||
*
|
||||
* @param bytes array to wrap
|
||||
*/
|
||||
LiteralByteString(byte[] bytes) {
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte byteAt(int index) {
|
||||
// Unlike most methods in this class, this one is a direct implementation
|
||||
// ignoring the potential offset because we need to do range-checking in the
|
||||
// substring case anyway.
|
||||
return bytes[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return bytes.length;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteString -> substring
|
||||
|
||||
@Override
|
||||
public ByteString substring(int beginIndex, int endIndex) {
|
||||
if (beginIndex < 0) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Beginning index: " + beginIndex + " < 0");
|
||||
}
|
||||
if (endIndex > size()) {
|
||||
throw new IndexOutOfBoundsException("End index: " + endIndex + " > " +
|
||||
size());
|
||||
}
|
||||
int substringLength = endIndex - beginIndex;
|
||||
if (substringLength < 0) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Beginning index larger than ending index: " + beginIndex + ", "
|
||||
+ endIndex);
|
||||
}
|
||||
|
||||
ByteString result;
|
||||
if (substringLength == 0) {
|
||||
result = ByteString.EMPTY;
|
||||
} else {
|
||||
result = new BoundedByteString(bytes, getOffsetIntoBytes() + beginIndex,
|
||||
substringLength);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteString -> byte[]
|
||||
|
||||
@Override
|
||||
protected void copyToInternal(byte[] target, int sourceOffset,
|
||||
int targetOffset, int numberToCopy) {
|
||||
// Optimized form, not for subclasses, since we don't call
|
||||
// getOffsetIntoBytes() or check the 'numberToCopy' parameter.
|
||||
System.arraycopy(bytes, sourceOffset, target, targetOffset, numberToCopy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyTo(ByteBuffer target) {
|
||||
target.put(bytes, getOffsetIntoBytes(), size()); // Copies bytes
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer asReadOnlyByteBuffer() {
|
||||
ByteBuffer byteBuffer =
|
||||
ByteBuffer.wrap(bytes, getOffsetIntoBytes(), size());
|
||||
return byteBuffer.asReadOnlyBuffer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ByteBuffer> asReadOnlyByteBufferList() {
|
||||
// Return the ByteBuffer generated by asReadOnlyByteBuffer() as a singleton
|
||||
List<ByteBuffer> result = new ArrayList<ByteBuffer>(1);
|
||||
result.add(asReadOnlyByteBuffer());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream outputStream) throws IOException {
|
||||
outputStream.write(toByteArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(String charsetName)
|
||||
throws UnsupportedEncodingException {
|
||||
return new String(bytes, getOffsetIntoBytes(), size(), charsetName);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// UTF-8 decoding
|
||||
|
||||
@Override
|
||||
public boolean isValidUtf8() {
|
||||
int offset = getOffsetIntoBytes();
|
||||
return Utf8.isValidUtf8(bytes, offset, offset + size());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int partialIsValidUtf8(int state, int offset, int length) {
|
||||
int index = getOffsetIntoBytes() + offset;
|
||||
return Utf8.partialIsValidUtf8(state, bytes, index, index + length);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// equals() and hashCode()
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof ByteString)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (size() != ((ByteString) other).size()) {
|
||||
return false;
|
||||
}
|
||||
if (size() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (other instanceof LiteralByteString) {
|
||||
return equalsRange((LiteralByteString) other, 0, size());
|
||||
} else if (other instanceof RopeByteString) {
|
||||
return other.equals(this);
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Has a new type of ByteString been created? Found "
|
||||
+ other.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check equality of the substring of given length of this object starting at
|
||||
* zero with another {@code LiteralByteString} substring starting at offset.
|
||||
*
|
||||
* @param other what to compare a substring in
|
||||
* @param offset offset into other
|
||||
* @param length number of bytes to compare
|
||||
* @return true for equality of substrings, else false.
|
||||
*/
|
||||
boolean equalsRange(LiteralByteString other, int offset, int length) {
|
||||
if (length > other.size()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Length too large: " + length + size());
|
||||
}
|
||||
if (offset + length > other.size()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Ran off end of other: " + offset + ", " + length + ", " +
|
||||
other.size());
|
||||
}
|
||||
|
||||
byte[] thisBytes = bytes;
|
||||
byte[] otherBytes = other.bytes;
|
||||
int thisLimit = getOffsetIntoBytes() + length;
|
||||
for (int thisIndex = getOffsetIntoBytes(), otherIndex =
|
||||
other.getOffsetIntoBytes() + offset;
|
||||
(thisIndex < thisLimit); ++thisIndex, ++otherIndex) {
|
||||
if (thisBytes[thisIndex] != otherBytes[otherIndex]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cached hash value. Intentionally accessed via a data race, which
|
||||
* is safe because of the Java Memory Model's "no out-of-thin-air values"
|
||||
* guarantees for ints.
|
||||
*/
|
||||
private int hash = 0;
|
||||
|
||||
/**
|
||||
* Compute the hashCode using the traditional algorithm from {@link
|
||||
* ByteString}.
|
||||
*
|
||||
* @return hashCode value
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = hash;
|
||||
|
||||
if (h == 0) {
|
||||
int size = size();
|
||||
h = partialHash(size, 0, size);
|
||||
if (h == 0) {
|
||||
h = 1;
|
||||
}
|
||||
hash = h;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int peekCachedHashCode() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int partialHash(int h, int offset, int length) {
|
||||
byte[] thisBytes = bytes;
|
||||
for (int i = getOffsetIntoBytes() + offset, limit = i + length; i < limit;
|
||||
i++) {
|
||||
h = h * 31 + thisBytes[i];
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Input stream
|
||||
|
||||
@Override
|
||||
public InputStream newInput() {
|
||||
return new ByteArrayInputStream(bytes, getOffsetIntoBytes(),
|
||||
size()); // No copy
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodedInputStream newCodedInput() {
|
||||
// We trust CodedInputStream not to modify the bytes, or to give anyone
|
||||
// else access to them.
|
||||
return CodedInputStream
|
||||
.newInstance(bytes, getOffsetIntoBytes(), size()); // No copy
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteIterator
|
||||
|
||||
@Override
|
||||
public ByteIterator iterator() {
|
||||
return new LiteralByteIterator();
|
||||
}
|
||||
|
||||
private class LiteralByteIterator implements ByteIterator {
|
||||
private int position;
|
||||
private final int limit;
|
||||
|
||||
private LiteralByteIterator() {
|
||||
position = 0;
|
||||
limit = size();
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return (position < limit);
|
||||
}
|
||||
|
||||
public Byte next() {
|
||||
// Boxing calls Byte.valueOf(byte), which does not instantiate.
|
||||
return nextByte();
|
||||
}
|
||||
|
||||
public byte nextByte() {
|
||||
try {
|
||||
return bytes[position++];
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
throw new NoSuchElementException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Internal methods
|
||||
|
||||
@Override
|
||||
protected int getTreeDepth() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isBalanced() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset into {@code bytes[]} to use, non-zero for substrings.
|
||||
*
|
||||
* @return always 0 for this class
|
||||
*/
|
||||
protected int getOffsetIntoBytes() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// TODO(kenton): Use generics? E.g. Builder<BuilderType extends Builder>, then
|
||||
// mergeFrom*() could return BuilderType for better type-safety.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Abstract interface implemented by Protocol Message objects.
|
||||
* <p>
|
||||
* See also {@link MessageLite}, which defines most of the methods that typical
|
||||
* users care about. {@link Message} adds to it methods that are not available
|
||||
* in the "lite" runtime. The biggest added features are introspection and
|
||||
* reflection -- i.e., getting descriptors for the message type and accessing
|
||||
* the field values dynamically.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public interface Message extends MessageLite, MessageOrBuilder {
|
||||
|
||||
// (From MessageLite, re-declared here only for return type covariance.)
|
||||
Parser<? extends Message> getParserForType();
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Comparison and hashing
|
||||
|
||||
/**
|
||||
* Compares the specified object with this message for equality. Returns
|
||||
* {@code true} 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. Subclasses must implement this; inheriting
|
||||
* {@code Object.equals()} is incorrect.
|
||||
*
|
||||
* @param other object to be compared for equality with this message
|
||||
* @return {@code true} if the specified object is equal to this message
|
||||
*/
|
||||
@Override
|
||||
boolean equals(Object other);
|
||||
|
||||
/**
|
||||
* Returns the hash code value for this message. The hash code of a message
|
||||
* should mix the message's type (object identity of the descriptor) 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()
|
||||
*/
|
||||
@Override
|
||||
int hashCode();
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Convenience methods.
|
||||
|
||||
/**
|
||||
* Converts the message to a string in protocol buffer text format. This is
|
||||
* just a trivial wrapper around {@link
|
||||
* TextFormat#printToString(MessageOrBuilder)}.
|
||||
*/
|
||||
@Override
|
||||
String toString();
|
||||
|
||||
// =================================================================
|
||||
// Builders
|
||||
|
||||
// (From MessageLite, re-declared here only for return type covariance.)
|
||||
Builder newBuilderForType();
|
||||
Builder toBuilder();
|
||||
|
||||
/**
|
||||
* Abstract interface implemented by Protocol Message builders.
|
||||
*/
|
||||
interface Builder extends MessageLite.Builder, MessageOrBuilder {
|
||||
// (From MessageLite.Builder, re-declared here only for return type
|
||||
// covariance.)
|
||||
Builder clear();
|
||||
|
||||
/**
|
||||
* Merge {@code other} into the message being built. {@code other} must
|
||||
* have the exact same type as {@code this} (i.e.
|
||||
* {@code getDescriptorForType() == other.getDescriptorForType()}).
|
||||
*
|
||||
* Merging occurs as follows. For each field:<br>
|
||||
* * For singular primitive fields, if the field is set in {@code other},
|
||||
* then {@code other}'s value overwrites the value in this message.<br>
|
||||
* * For singular message fields, if the field is set in {@code other},
|
||||
* it is merged into the corresponding sub-message of this message
|
||||
* using the same merging rules.<br>
|
||||
* * For repeated fields, the elements in {@code other} are concatenated
|
||||
* with the elements in this message.
|
||||
*
|
||||
* This is equivalent to the {@code Message::MergeFrom} method in C++.
|
||||
*/
|
||||
Builder mergeFrom(Message other);
|
||||
|
||||
// (From MessageLite.Builder, re-declared here only for return type
|
||||
// covariance.)
|
||||
Message build();
|
||||
Message buildPartial();
|
||||
Builder clone();
|
||||
Builder mergeFrom(CodedInputStream input) throws IOException;
|
||||
Builder mergeFrom(CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Get the message's type's descriptor.
|
||||
* See {@link Message#getDescriptorForType()}.
|
||||
*/
|
||||
Descriptors.Descriptor getDescriptorForType();
|
||||
|
||||
/**
|
||||
* Create a Builder for messages of the appropriate type for the given
|
||||
* field. Messages built with this can then be passed to setField(),
|
||||
* setRepeatedField(), or addRepeatedField().
|
||||
*/
|
||||
Builder newBuilderForField(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Get a nested builder instance for the given field.
|
||||
* <p>
|
||||
* Normally, we hold a reference to the immutable message object for the
|
||||
* message type field. Some implementations(the generated message builders),
|
||||
* however, can also hold a reference to the builder object (a nested
|
||||
* builder) for the field.
|
||||
* <p>
|
||||
* If the field is already backed up by a nested builder, the nested builder
|
||||
* will be returned. Otherwise, a new field builder will be created and
|
||||
* returned. The original message field (if exist) will be merged into the
|
||||
* field builder, which will then be nested into its parent builder.
|
||||
* <p>
|
||||
* NOTE: implementations that do not support nested builders will throw
|
||||
* <code>UnsupportedException</code>.
|
||||
*/
|
||||
Builder getFieldBuilder(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Sets a field to the given value. The value must be of the correct type
|
||||
* for this field, i.e. the same type that
|
||||
* {@link Message#getField(Descriptors.FieldDescriptor)} would return.
|
||||
*/
|
||||
Builder setField(Descriptors.FieldDescriptor field, Object value);
|
||||
|
||||
/**
|
||||
* Clears the field. This is exactly equivalent to calling the generated
|
||||
* "clear" accessor method corresponding to the field.
|
||||
*/
|
||||
Builder clearField(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Sets an element of a repeated field to the given value. The value must
|
||||
* be of the correct type for this field, i.e. the same type that
|
||||
* {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)} would
|
||||
* return.
|
||||
* @throws IllegalArgumentException The field is not a repeated field, or
|
||||
* {@code field.getContainingType() != getDescriptorForType()}.
|
||||
*/
|
||||
Builder setRepeatedField(Descriptors.FieldDescriptor field,
|
||||
int index, Object value);
|
||||
|
||||
/**
|
||||
* Like {@code setRepeatedField}, but appends the value as a new element.
|
||||
* @throws IllegalArgumentException The field is not a repeated field, or
|
||||
* {@code field.getContainingType() != getDescriptorForType()}.
|
||||
*/
|
||||
Builder addRepeatedField(Descriptors.FieldDescriptor field, Object value);
|
||||
|
||||
/** Set the {@link UnknownFieldSet} for this message. */
|
||||
Builder setUnknownFields(UnknownFieldSet unknownFields);
|
||||
|
||||
/**
|
||||
* Merge some unknown fields into the {@link UnknownFieldSet} for this
|
||||
* message.
|
||||
*/
|
||||
Builder mergeUnknownFields(UnknownFieldSet unknownFields);
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Convenience methods.
|
||||
|
||||
// (From MessageLite.Builder, re-declared here only for return type
|
||||
// covariance.)
|
||||
Builder mergeFrom(ByteString data) throws InvalidProtocolBufferException;
|
||||
Builder mergeFrom(ByteString data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
Builder mergeFrom(byte[] data) throws InvalidProtocolBufferException;
|
||||
Builder mergeFrom(byte[] data, int off, int len)
|
||||
throws InvalidProtocolBufferException;
|
||||
Builder mergeFrom(byte[] data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
Builder mergeFrom(byte[] data, int off, int len,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
Builder mergeFrom(InputStream input) throws IOException;
|
||||
Builder mergeFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException;
|
||||
boolean mergeDelimitedFrom(InputStream input)
|
||||
throws IOException;
|
||||
boolean mergeDelimitedFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException;
|
||||
}
|
||||
}
|
||||
@@ -1,319 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// TODO(kenton): Use generics? E.g. Builder<BuilderType extends Builder>, then
|
||||
// mergeFrom*() could return BuilderType for better type-safety.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Abstract interface implemented by Protocol Message objects.
|
||||
*
|
||||
* <p>This interface is implemented by all protocol message objects. Non-lite
|
||||
* messages additionally implement the Message interface, which is a subclass
|
||||
* of MessageLite. Use MessageLite instead when you only need the subset of
|
||||
* features which it supports -- namely, nothing that uses descriptors or
|
||||
* reflection. You can instruct the protocol compiler to generate classes
|
||||
* which implement only MessageLite, not the full Message interface, by adding
|
||||
* the follow line to the .proto file:
|
||||
* <pre>
|
||||
* option optimize_for = LITE_RUNTIME;
|
||||
* </pre>
|
||||
*
|
||||
* <p>This is particularly useful on resource-constrained systems where the
|
||||
* full protocol buffers runtime library is too big.
|
||||
*
|
||||
* <p>Note that on non-constrained systems (e.g. servers) when you need to link
|
||||
* in lots of protocol definitions, a better way to reduce total code footprint
|
||||
* is to use {@code optimize_for = CODE_SIZE}. This will make the generated
|
||||
* code smaller while still supporting all the same features (at the expense of
|
||||
* speed). {@code optimize_for = LITE_RUNTIME} is best when you only have a
|
||||
* small number of message types linked into your binary, in which case the
|
||||
* size of the protocol buffers runtime itself is the biggest problem.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public interface MessageLite extends MessageLiteOrBuilder {
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message and writes it to {@code output}. This does not
|
||||
* flush or close the stream.
|
||||
*/
|
||||
void writeTo(CodedOutputStream output) throws IOException;
|
||||
|
||||
/**
|
||||
* Get the number of bytes required to encode this message. The result
|
||||
* is only computed on the first call and memoized after that.
|
||||
*/
|
||||
int getSerializedSize();
|
||||
|
||||
|
||||
/**
|
||||
* Gets the parser for a message of the same type as this message.
|
||||
*/
|
||||
Parser<? extends MessageLite> getParserForType();
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Convenience methods.
|
||||
|
||||
/**
|
||||
* Serializes the message to a {@code ByteString} and returns it. This is
|
||||
* just a trivial wrapper around
|
||||
* {@link #writeTo(CodedOutputStream)}.
|
||||
*/
|
||||
ByteString toByteString();
|
||||
|
||||
/**
|
||||
* Serializes the message to a {@code byte} array and returns it. This is
|
||||
* just a trivial wrapper around
|
||||
* {@link #writeTo(CodedOutputStream)}.
|
||||
*/
|
||||
byte[] toByteArray();
|
||||
|
||||
/**
|
||||
* Serializes the message and writes it to {@code output}. This is just a
|
||||
* trivial wrapper around {@link #writeTo(CodedOutputStream)}. This does
|
||||
* not flush or close the stream.
|
||||
* <p>
|
||||
* NOTE: Protocol Buffers are not self-delimiting. Therefore, if you write
|
||||
* any more data to the stream after the message, you must somehow ensure
|
||||
* that the parser on the receiving end does not interpret this as being
|
||||
* part of the protocol message. This can be done e.g. by writing the size
|
||||
* of the message before the data, then making sure to limit the input to
|
||||
* that size on the receiving end (e.g. by wrapping the InputStream in one
|
||||
* which limits the input). Alternatively, just use
|
||||
* {@link #writeDelimitedTo(OutputStream)}.
|
||||
*/
|
||||
void writeTo(OutputStream output) throws IOException;
|
||||
|
||||
/**
|
||||
* Like {@link #writeTo(OutputStream)}, but writes the size of the message
|
||||
* as a varint before writing the data. This allows more data to be written
|
||||
* to the stream after the message without the need to delimit the message
|
||||
* data yourself. Use {@link Builder#mergeDelimitedFrom(InputStream)} (or
|
||||
* the static method {@code YourMessageType.parseDelimitedFrom(InputStream)})
|
||||
* to parse messages written by this method.
|
||||
*/
|
||||
void writeDelimitedTo(OutputStream output) throws IOException;
|
||||
|
||||
// =================================================================
|
||||
// Builders
|
||||
|
||||
/**
|
||||
* Constructs a new builder for a message of the same type as this message.
|
||||
*/
|
||||
Builder newBuilderForType();
|
||||
|
||||
/**
|
||||
* Constructs a builder initialized with the current message. Use this to
|
||||
* derive a new message from the current one.
|
||||
*/
|
||||
Builder toBuilder();
|
||||
|
||||
/**
|
||||
* Abstract interface implemented by Protocol Message builders.
|
||||
*/
|
||||
interface Builder extends MessageLiteOrBuilder, Cloneable {
|
||||
/** Resets all fields to their default values. */
|
||||
Builder clear();
|
||||
|
||||
/**
|
||||
* Constructs the message based on the state of the Builder. Subsequent
|
||||
* changes to the Builder will not affect the returned message.
|
||||
* @throws UninitializedMessageException The message is missing one or more
|
||||
* required fields (i.e. {@link #isInitialized()} returns false).
|
||||
* Use {@link #buildPartial()} to bypass this check.
|
||||
*/
|
||||
MessageLite build();
|
||||
|
||||
/**
|
||||
* Like {@link #build()}, but does not throw an exception if the message
|
||||
* is missing required fields. Instead, a partial message is returned.
|
||||
* Subsequent changes to the Builder will not affect the returned message.
|
||||
*/
|
||||
MessageLite buildPartial();
|
||||
|
||||
/**
|
||||
* Clones the Builder.
|
||||
* @see Object#clone()
|
||||
*/
|
||||
Builder clone();
|
||||
|
||||
/**
|
||||
* Parses a message of this type from the input and merges it with this
|
||||
* message.
|
||||
*
|
||||
* <p>Warning: This does not verify that all required fields are present in
|
||||
* the input message. If you call {@link #build()} without setting all
|
||||
* required fields, it will throw an {@link UninitializedMessageException},
|
||||
* which is a {@code RuntimeException} and thus might not be caught. There
|
||||
* are a few good ways to deal with this:
|
||||
* <ul>
|
||||
* <li>Call {@link #isInitialized()} to verify that all required fields
|
||||
* are set before building.
|
||||
* <li>Use {@code buildPartial()} to build, which ignores missing
|
||||
* required fields.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Note: The caller should call
|
||||
* {@link CodedInputStream#checkLastTagWas(int)} after calling this to
|
||||
* verify that the last tag seen was the appropriate end-group tag,
|
||||
* or zero for EOF.
|
||||
*/
|
||||
Builder mergeFrom(CodedInputStream input) throws IOException;
|
||||
|
||||
/**
|
||||
* Like {@link Builder#mergeFrom(CodedInputStream)}, but also
|
||||
* parses extensions. The extensions that you want to be able to parse
|
||||
* must be registered in {@code extensionRegistry}. Extensions not in
|
||||
* the registry will be treated as unknown fields.
|
||||
*/
|
||||
Builder mergeFrom(CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException;
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Convenience methods.
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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,ExtensionRegistryLite)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(ByteString data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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,ExtensionRegistryLite)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(byte[] data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* 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,ExtensionRegistryLite)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(byte[] data, int off, int len,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* 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)}. Note that this method always
|
||||
* reads the <i>entire</i> input (unless it throws an exception). If you
|
||||
* want it to stop earlier, you will need to wrap your input in some
|
||||
* wrapper stream that limits reading. Or, use
|
||||
* {@link MessageLite#writeDelimitedTo(OutputStream)} to write your message
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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,ExtensionRegistryLite)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Like {@link #mergeFrom(InputStream)}, but does not read until EOF.
|
||||
* Instead, the size of the message (encoded as a varint) is read first,
|
||||
* then the message data. Use
|
||||
* {@link MessageLite#writeDelimitedTo(OutputStream)} to write messages in
|
||||
* this format.
|
||||
*
|
||||
* @return True if successful, or false if the stream is at EOF when the
|
||||
* method starts. Any other error (including reaching EOF during
|
||||
* parsing) will cause an exception to be thrown.
|
||||
*/
|
||||
boolean mergeDelimitedFrom(InputStream input)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Like {@link #mergeDelimitedFrom(InputStream)} but supporting extensions.
|
||||
*/
|
||||
boolean mergeDelimitedFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
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 no fields set. Because no fields are set,
|
||||
* all getters for singular fields will return default values and repeated
|
||||
* fields will appear empty.
|
||||
* 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.
|
||||
*
|
||||
* <p>See also: {@link MessageOrBuilder#getInitializationErrorString()}
|
||||
*/
|
||||
boolean isInitialized();
|
||||
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.List;
|
||||
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();
|
||||
|
||||
/**
|
||||
* Returns a list of field paths (e.g. "foo.bar.baz") of required fields
|
||||
* which are not set in this message. You should call
|
||||
* {@link MessageLiteOrBuilder#isInitialized()} first to check if there
|
||||
* are any missing fields, as that method is likely to be much faster
|
||||
* than this one even when the message is fully-initialized.
|
||||
*/
|
||||
List<String> findInitializationErrors();
|
||||
|
||||
/**
|
||||
* Returns a comma-delimited list of required fields which are not set
|
||||
* in this message object. You should call
|
||||
* {@link MessageLiteOrBuilder#isInitialized()} first to check if there
|
||||
* are any missing fields, as that method is likely to be much faster
|
||||
* than this one even when the message is fully-initialized.
|
||||
*/
|
||||
String getInitializationErrorString();
|
||||
|
||||
/**
|
||||
* 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 returned. 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 returned. 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();
|
||||
}
|
||||
@@ -1,259 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Abstract interface for parsing Protocol Messages.
|
||||
*
|
||||
* @author liujisi@google.com (Pherl Liu)
|
||||
*/
|
||||
public interface Parser<MessageType> {
|
||||
/**
|
||||
* Parses a message of {@code MessageType} from the input.
|
||||
*
|
||||
* <p>Note: The caller should call
|
||||
* {@link CodedInputStream#checkLastTagWas(int)} after calling this to
|
||||
* verify that the last tag seen was the appropriate end-group tag,
|
||||
* or zero for EOF.
|
||||
*/
|
||||
public MessageType parseFrom(CodedInputStream input)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(CodedInputStream)}, but also parses extensions.
|
||||
* The extensions that you want to be able to parse must be registered in
|
||||
* {@code extensionRegistry}. Extensions not in the registry will be treated
|
||||
* as unknown fields.
|
||||
*/
|
||||
public MessageType parseFrom(CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(CodedInputStream)}, but does not throw an
|
||||
* exception if the message is missing required fields. Instead, a partial
|
||||
* message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(CodedInputStream input)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(CodedInputStream input, ExtensionRegistryLite)},
|
||||
* but does not throw an exception if the message is missing required fields.
|
||||
* Instead, a partial message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Convenience methods.
|
||||
|
||||
/**
|
||||
* Parses {@code data} as a message of {@code MessageType}.
|
||||
* This is just a small wrapper around {@link #parseFrom(CodedInputStream)}.
|
||||
*/
|
||||
public MessageType parseFrom(ByteString data)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parses {@code data} as a message of {@code MessageType}.
|
||||
* This is just a small wrapper around
|
||||
* {@link #parseFrom(CodedInputStream, ExtensionRegistryLite)}.
|
||||
*/
|
||||
public MessageType parseFrom(ByteString data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(ByteString)}, but does not throw an
|
||||
* exception if the message is missing required fields. Instead, a partial
|
||||
* message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(ByteString data)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(ByteString, ExtensionRegistryLite)},
|
||||
* but does not throw an exception if the message is missing required fields.
|
||||
* Instead, a partial message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(ByteString data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parses {@code data} as a message of {@code MessageType}.
|
||||
* This is just a small wrapper around {@link #parseFrom(CodedInputStream)}.
|
||||
*/
|
||||
public MessageType parseFrom(byte[] data, int off, int len)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parses {@code data} as a message of {@code MessageType}.
|
||||
* This is just a small wrapper around
|
||||
* {@link #parseFrom(CodedInputStream, ExtensionRegistryLite)}.
|
||||
*/
|
||||
public MessageType parseFrom(byte[] data, int off, int len,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parses {@code data} as a message of {@code MessageType}.
|
||||
* This is just a small wrapper around {@link #parseFrom(CodedInputStream)}.
|
||||
*/
|
||||
public MessageType parseFrom(byte[] data)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parses {@code data} as a message of {@code MessageType}.
|
||||
* This is just a small wrapper around
|
||||
* {@link #parseFrom(CodedInputStream, ExtensionRegistryLite)}.
|
||||
*/
|
||||
public MessageType parseFrom(byte[] data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(byte[], int, int)}, but does not throw an
|
||||
* exception if the message is missing required fields. Instead, a partial
|
||||
* message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(byte[] data, int off, int len)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(ByteString, ExtensionRegistryLite)},
|
||||
* but does not throw an exception if the message is missing required fields.
|
||||
* Instead, a partial message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(byte[] data, int off, int len,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(byte[])}, but does not throw an
|
||||
* exception if the message is missing required fields. Instead, a partial
|
||||
* message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(byte[] data)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(byte[], ExtensionRegistryLite)},
|
||||
* but does not throw an exception if the message is missing required fields.
|
||||
* Instead, a partial message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(byte[] data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parse a message of {@code MessageType} from {@code input}.
|
||||
* This is just a small wrapper around {@link #parseFrom(CodedInputStream)}.
|
||||
* Note that this method always reads the <i>entire</i> input (unless it
|
||||
* throws an exception). If you want it to stop earlier, you will need to
|
||||
* wrap your input in some wrapper stream that limits reading. Or, use
|
||||
* {@link MessageLite#writeDelimitedTo(java.io.OutputStream)} to write your
|
||||
* message and {@link #parseDelimitedFrom(InputStream)} to read it.
|
||||
* <p>
|
||||
* Despite usually reading the entire input, this does not close the stream.
|
||||
*/
|
||||
public MessageType parseFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parses a message of {@code MessageType} from {@code input}.
|
||||
* This is just a small wrapper around
|
||||
* {@link #parseFrom(CodedInputStream, ExtensionRegistryLite)}.
|
||||
*/
|
||||
public MessageType parseFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(InputStream)}, but does not throw an
|
||||
* exception if the message is missing required fields. Instead, a partial
|
||||
* message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(InputStream, ExtensionRegistryLite)},
|
||||
* but does not throw an exception if the message is missing required fields.
|
||||
* Instead, a partial message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(InputStream)}, but does not read util EOF.
|
||||
* Instead, the size of message (encoded as a varint) is read first,
|
||||
* then the message data. Use
|
||||
* {@link MessageLite#writeDelimitedTo(java.io.OutputStream)} to write
|
||||
* messages in this format.
|
||||
*
|
||||
* @return True if successful, or false if the stream is at EOF when the
|
||||
* method starts. Any other error (including reaching EOF during
|
||||
* parsing) will cause an exception to be thrown.
|
||||
*/
|
||||
public MessageType parseDelimitedFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseDelimitedFrom(InputStream)} but supporting extensions.
|
||||
*/
|
||||
public MessageType parseDelimitedFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseDelimitedFrom(InputStream)}, but does not throw an
|
||||
* exception if the message is missing required fields. Instead, a partial
|
||||
* message is returned.
|
||||
*/
|
||||
public MessageType parsePartialDelimitedFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseDelimitedFrom(InputStream, ExtensionRegistryLite)},
|
||||
* but does not throw an exception if the message is missing required fields.
|
||||
* Instead, a partial message is returned.
|
||||
*/
|
||||
public MessageType parsePartialDelimitedFrom(
|
||||
InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.Descriptors.EnumDescriptor;
|
||||
import com.google.protobuf.Descriptors.EnumValueDescriptor;
|
||||
|
||||
/**
|
||||
* Interface of useful methods added to all enums generated by the protocol
|
||||
* compiler.
|
||||
*/
|
||||
public interface ProtocolMessageEnum extends Internal.EnumLite {
|
||||
|
||||
/**
|
||||
* Return the value's numeric value as defined in the .proto file.
|
||||
*/
|
||||
int getNumber();
|
||||
|
||||
/**
|
||||
* Return the value's descriptor, which contains information such as
|
||||
* value name, number, and type.
|
||||
*/
|
||||
EnumValueDescriptor getValueDescriptor();
|
||||
|
||||
/**
|
||||
* Return the enum type's descriptor, which contains information
|
||||
* about each defined value, etc.
|
||||
*/
|
||||
EnumDescriptor getDescriptorForType();
|
||||
}
|
||||
@@ -1,696 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
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} implements a structure that a protocol
|
||||
* message uses to hold a repeated field of other protocol messages. It supports
|
||||
* the classical use case of adding immutable {@link Message}'s to the
|
||||
* repeated field and is highly optimized around this (no extra memory
|
||||
* allocations and sharing of immutable arrays).
|
||||
* <br>
|
||||
* It also supports the additional use case of adding a {@link Message.Builder}
|
||||
* to the repeated field and deferring conversion of that {@code Builder}
|
||||
* to an immutable {@code Message}. In this way, it's possible to maintain
|
||||
* a tree of {@code Builder}'s that acts as a fully read/write data
|
||||
* structure.
|
||||
* <br>
|
||||
* Logically, one can think of a tree of builders as converting the entire tree
|
||||
* to messages when build is called on the root or when any method is called
|
||||
* that desires a Message instead of a Builder. In terms of the implementation,
|
||||
* the {@code SingleFieldBuilder} and {@code RepeatedFieldBuilder}
|
||||
* 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}, it is converted to a {@code Message} by
|
||||
* calling {@link Message.Builder#buildPartial} on it.
|
||||
*
|
||||
* @param index the index of the message to get
|
||||
* @return the message for the specified index
|
||||
*/
|
||||
public MType getMessage(int index) {
|
||||
return getMessage(index, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message at the specified index. If the message is currently stored
|
||||
* as a {@code Builder}, it is converted to a {@code Message} by
|
||||
* calling {@link Message.Builder#buildPartial} on it.
|
||||
*
|
||||
* @param index the index of the message to get
|
||||
* @param forBuild this is being called for build so we want to make sure
|
||||
* we 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 (Java 1.6 override semantics, but we must support 1.5)
|
||||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,943 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* Class to represent {@code ByteStrings} formed by concatenation of other
|
||||
* ByteStrings, without copying the data in the pieces. The concatenation is
|
||||
* represented as a tree whose leaf nodes are each a {@link LiteralByteString}.
|
||||
*
|
||||
* <p>Most of the operation here is inspired by the now-famous paper <a
|
||||
* href="http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol25/issue12/spe986.pdf">
|
||||
* BAP95 </a> Ropes: an Alternative to Strings hans-j. boehm, russ atkinson and
|
||||
* michael plass
|
||||
*
|
||||
* <p>The algorithms described in the paper have been implemented for character
|
||||
* strings in {@link com.google.common.string.Rope} and in the c++ class {@code
|
||||
* cord.cc}.
|
||||
*
|
||||
* <p>Fundamentally the Rope algorithm represents the collection of pieces as a
|
||||
* binary tree. BAP95 uses a Fibonacci bound relating depth to a minimum
|
||||
* sequence length, sequences that are too short relative to their depth cause a
|
||||
* tree rebalance. More precisely, a tree of depth d is "balanced" in the
|
||||
* terminology of BAP95 if its length is at least F(d+2), where F(n) is the
|
||||
* n-the Fibonacci number. Thus for depths 0, 1, 2, 3, 4, 5,... we have minimum
|
||||
* lengths 1, 2, 3, 5, 8, 13,...
|
||||
*
|
||||
* @author carlanton@google.com (Carl Haverl)
|
||||
*/
|
||||
class RopeByteString extends ByteString {
|
||||
|
||||
/**
|
||||
* BAP95. Let Fn be the nth Fibonacci number. A {@link RopeByteString} of
|
||||
* depth n is "balanced", i.e flat enough, if its length is at least Fn+2,
|
||||
* e.g. a "balanced" {@link RopeByteString} of depth 1 must have length at
|
||||
* least 2, of depth 4 must have length >= 8, etc.
|
||||
*
|
||||
* <p>There's nothing special about using the Fibonacci numbers for this, but
|
||||
* they are a reasonable sequence for encapsulating the idea that we are OK
|
||||
* with longer strings being encoded in deeper binary trees.
|
||||
*
|
||||
* <p>For 32-bit integers, this array has length 46.
|
||||
*/
|
||||
private static final int[] minLengthByDepth;
|
||||
|
||||
static {
|
||||
// Dynamically generate the list of Fibonacci numbers the first time this
|
||||
// class is accessed.
|
||||
List<Integer> numbers = new ArrayList<Integer>();
|
||||
|
||||
// we skip the first Fibonacci number (1). So instead of: 1 1 2 3 5 8 ...
|
||||
// we have: 1 2 3 5 8 ...
|
||||
int f1 = 1;
|
||||
int f2 = 1;
|
||||
|
||||
// get all the values until we roll over.
|
||||
while (f2 > 0) {
|
||||
numbers.add(f2);
|
||||
int temp = f1 + f2;
|
||||
f1 = f2;
|
||||
f2 = temp;
|
||||
}
|
||||
|
||||
// we include this here so that we can index this array to [x + 1] in the
|
||||
// loops below.
|
||||
numbers.add(Integer.MAX_VALUE);
|
||||
minLengthByDepth = new int[numbers.size()];
|
||||
for (int i = 0; i < minLengthByDepth.length; i++) {
|
||||
// unbox all the values
|
||||
minLengthByDepth[i] = numbers.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
private final int totalLength;
|
||||
private final ByteString left;
|
||||
private final ByteString right;
|
||||
private final int leftLength;
|
||||
private final int treeDepth;
|
||||
|
||||
/**
|
||||
* Create a new RopeByteString, which can be thought of as a new tree node, by
|
||||
* recording references to the two given strings.
|
||||
*
|
||||
* @param left string on the left of this node, should have {@code size() >
|
||||
* 0}
|
||||
* @param right string on the right of this node, should have {@code size() >
|
||||
* 0}
|
||||
*/
|
||||
private RopeByteString(ByteString left, ByteString right) {
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
leftLength = left.size();
|
||||
totalLength = leftLength + right.size();
|
||||
treeDepth = Math.max(left.getTreeDepth(), right.getTreeDepth()) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate the given strings while performing various optimizations to
|
||||
* slow the growth rate of tree depth and tree node count. The result is
|
||||
* either a {@link LiteralByteString} or a {@link RopeByteString}
|
||||
* depending on which optimizations, if any, were applied.
|
||||
*
|
||||
* <p>Small pieces of length less than {@link
|
||||
* ByteString#CONCATENATE_BY_COPY_SIZE} may be copied by value here, as in
|
||||
* BAP95. Large pieces are referenced without copy.
|
||||
*
|
||||
* @param left string on the left
|
||||
* @param right string on the right
|
||||
* @return concatenation representing the same sequence as the given strings
|
||||
*/
|
||||
static ByteString concatenate(ByteString left, ByteString right) {
|
||||
ByteString result;
|
||||
RopeByteString leftRope =
|
||||
(left instanceof RopeByteString) ? (RopeByteString) left : null;
|
||||
if (right.size() == 0) {
|
||||
result = left;
|
||||
} else if (left.size() == 0) {
|
||||
result = right;
|
||||
} else {
|
||||
int newLength = left.size() + right.size();
|
||||
if (newLength < ByteString.CONCATENATE_BY_COPY_SIZE) {
|
||||
// Optimization from BAP95: For short (leaves in paper, but just short
|
||||
// here) total length, do a copy of data to a new leaf.
|
||||
result = concatenateBytes(left, right);
|
||||
} else if (leftRope != null
|
||||
&& leftRope.right.size() + right.size() < CONCATENATE_BY_COPY_SIZE) {
|
||||
// Optimization from BAP95: As an optimization of the case where the
|
||||
// ByteString is constructed by repeated concatenate, recognize the case
|
||||
// where a short string is concatenated to a left-hand node whose
|
||||
// right-hand branch is short. In the paper this applies to leaves, but
|
||||
// we just look at the length here. This has the advantage of shedding
|
||||
// references to unneeded data when substrings have been taken.
|
||||
//
|
||||
// When we recognize this case, we do a copy of the data and create a
|
||||
// new parent node so that the depth of the result is the same as the
|
||||
// given left tree.
|
||||
ByteString newRight = concatenateBytes(leftRope.right, right);
|
||||
result = new RopeByteString(leftRope.left, newRight);
|
||||
} else if (leftRope != null
|
||||
&& leftRope.left.getTreeDepth() > leftRope.right.getTreeDepth()
|
||||
&& leftRope.getTreeDepth() > right.getTreeDepth()) {
|
||||
// Typically for concatenate-built strings the left-side is deeper than
|
||||
// the right. This is our final attempt to concatenate without
|
||||
// increasing the tree depth. We'll redo the the node on the RHS. This
|
||||
// is yet another optimization for building the string by repeatedly
|
||||
// concatenating on the right.
|
||||
ByteString newRight = new RopeByteString(leftRope.right, right);
|
||||
result = new RopeByteString(leftRope.left, newRight);
|
||||
} else {
|
||||
// Fine, we'll add a node and increase the tree depth--unless we
|
||||
// rebalance ;^)
|
||||
int newDepth = Math.max(left.getTreeDepth(), right.getTreeDepth()) + 1;
|
||||
if (newLength >= minLengthByDepth[newDepth]) {
|
||||
// The tree is shallow enough, so don't rebalance
|
||||
result = new RopeByteString(left, right);
|
||||
} else {
|
||||
result = new Balancer().balance(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates two strings by copying data values. This is called in a few
|
||||
* cases in order to reduce the growth of the number of tree nodes.
|
||||
*
|
||||
* @param left string on the left
|
||||
* @param right string on the right
|
||||
* @return string formed by copying data bytes
|
||||
*/
|
||||
private static LiteralByteString concatenateBytes(ByteString left,
|
||||
ByteString right) {
|
||||
int leftSize = left.size();
|
||||
int rightSize = right.size();
|
||||
byte[] bytes = new byte[leftSize + rightSize];
|
||||
left.copyTo(bytes, 0, 0, leftSize);
|
||||
right.copyTo(bytes, 0, leftSize, rightSize);
|
||||
return new LiteralByteString(bytes); // Constructor wraps bytes
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new RopeByteString for testing only while bypassing all the
|
||||
* defenses of {@link #concatenate(ByteString, ByteString)}. This allows
|
||||
* testing trees of specific structure. We are also able to insert empty
|
||||
* leaves, though these are dis-allowed, so that we can make sure the
|
||||
* implementation can withstand their presence.
|
||||
*
|
||||
* @param left string on the left of this node
|
||||
* @param right string on the right of this node
|
||||
* @return an unsafe instance for testing only
|
||||
*/
|
||||
static RopeByteString newInstanceForTest(ByteString left, ByteString right) {
|
||||
return new RopeByteString(left, right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the byte at the given index.
|
||||
* Throws {@link ArrayIndexOutOfBoundsException} for backwards-compatibility
|
||||
* reasons although it would more properly be {@link
|
||||
* IndexOutOfBoundsException}.
|
||||
*
|
||||
* @param index index of byte
|
||||
* @return the value
|
||||
* @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or >= size
|
||||
*/
|
||||
@Override
|
||||
public byte byteAt(int index) {
|
||||
if (index < 0) {
|
||||
throw new ArrayIndexOutOfBoundsException("Index < 0: " + index);
|
||||
}
|
||||
if (index > totalLength) {
|
||||
throw new ArrayIndexOutOfBoundsException(
|
||||
"Index > length: " + index + ", " + totalLength);
|
||||
}
|
||||
|
||||
byte result;
|
||||
// Find the relevant piece by recursive descent
|
||||
if (index < leftLength) {
|
||||
result = left.byteAt(index);
|
||||
} else {
|
||||
result = right.byteAt(index - leftLength);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return totalLength;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Pieces
|
||||
|
||||
@Override
|
||||
protected int getTreeDepth() {
|
||||
return treeDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the tree is balanced according to BAP95, which means the tree
|
||||
* is flat-enough with respect to the bounds. Note that this definition of
|
||||
* balanced is one where sub-trees of balanced trees are not necessarily
|
||||
* balanced.
|
||||
*
|
||||
* @return true if the tree is balanced
|
||||
*/
|
||||
@Override
|
||||
protected boolean isBalanced() {
|
||||
return totalLength >= minLengthByDepth[treeDepth];
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a substring of this one. This involves recursive descent along the
|
||||
* left and right edges of the substring, and referencing any wholly contained
|
||||
* segments in between. Any leaf nodes entirely uninvolved in the substring
|
||||
* will not be referenced by the substring.
|
||||
*
|
||||
* <p>Substrings of {@code length < 2} should result in at most a single
|
||||
* recursive call chain, terminating at a leaf node. Thus the result will be a
|
||||
* {@link LiteralByteString}. {@link #RopeByteString(ByteString,
|
||||
* ByteString)}.
|
||||
*
|
||||
* @param beginIndex start at this index
|
||||
* @param endIndex the last character is the one before this index
|
||||
* @return substring leaf node or tree
|
||||
*/
|
||||
@Override
|
||||
public ByteString substring(int beginIndex, int endIndex) {
|
||||
if (beginIndex < 0) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Beginning index: " + beginIndex + " < 0");
|
||||
}
|
||||
if (endIndex > totalLength) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"End index: " + endIndex + " > " + totalLength);
|
||||
}
|
||||
int substringLength = endIndex - beginIndex;
|
||||
if (substringLength < 0) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Beginning index larger than ending index: " + beginIndex + ", "
|
||||
+ endIndex);
|
||||
}
|
||||
|
||||
ByteString result;
|
||||
if (substringLength == 0) {
|
||||
// Empty substring
|
||||
result = ByteString.EMPTY;
|
||||
} else if (substringLength == totalLength) {
|
||||
// The whole string
|
||||
result = this;
|
||||
} else {
|
||||
// Proper substring
|
||||
if (endIndex <= leftLength) {
|
||||
// Substring on the left
|
||||
result = left.substring(beginIndex, endIndex);
|
||||
} else if (beginIndex >= leftLength) {
|
||||
// Substring on the right
|
||||
result = right
|
||||
.substring(beginIndex - leftLength, endIndex - leftLength);
|
||||
} else {
|
||||
// Split substring
|
||||
ByteString leftSub = left.substring(beginIndex);
|
||||
ByteString rightSub = right.substring(0, endIndex - leftLength);
|
||||
// Intentionally not rebalancing, since in many cases these two
|
||||
// substrings will already be less deep than the top-level
|
||||
// RopeByteString we're taking a substring of.
|
||||
result = new RopeByteString(leftSub, rightSub);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteString -> byte[]
|
||||
|
||||
@Override
|
||||
protected void copyToInternal(byte[] target, int sourceOffset,
|
||||
int targetOffset, int numberToCopy) {
|
||||
if (sourceOffset + numberToCopy <= leftLength) {
|
||||
left.copyToInternal(target, sourceOffset, targetOffset, numberToCopy);
|
||||
} else if (sourceOffset >= leftLength) {
|
||||
right.copyToInternal(target, sourceOffset - leftLength, targetOffset,
|
||||
numberToCopy);
|
||||
} else {
|
||||
int leftLength = this.leftLength - sourceOffset;
|
||||
left.copyToInternal(target, sourceOffset, targetOffset, leftLength);
|
||||
right.copyToInternal(target, 0, targetOffset + leftLength,
|
||||
numberToCopy - leftLength);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyTo(ByteBuffer target) {
|
||||
left.copyTo(target);
|
||||
right.copyTo(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer asReadOnlyByteBuffer() {
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(toByteArray());
|
||||
return byteBuffer.asReadOnlyBuffer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ByteBuffer> asReadOnlyByteBufferList() {
|
||||
// Walk through the list of LiteralByteString's that make up this
|
||||
// rope, and add each one as a read-only ByteBuffer.
|
||||
List<ByteBuffer> result = new ArrayList<ByteBuffer>();
|
||||
PieceIterator pieces = new PieceIterator(this);
|
||||
while (pieces.hasNext()) {
|
||||
LiteralByteString byteString = pieces.next();
|
||||
result.add(byteString.asReadOnlyByteBuffer());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream outputStream) throws IOException {
|
||||
left.writeTo(outputStream);
|
||||
right.writeTo(outputStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(String charsetName)
|
||||
throws UnsupportedEncodingException {
|
||||
return new String(toByteArray(), charsetName);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// UTF-8 decoding
|
||||
|
||||
@Override
|
||||
public boolean isValidUtf8() {
|
||||
int leftPartial = left.partialIsValidUtf8(Utf8.COMPLETE, 0, leftLength);
|
||||
int state = right.partialIsValidUtf8(leftPartial, 0, right.size());
|
||||
return state == Utf8.COMPLETE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int partialIsValidUtf8(int state, int offset, int length) {
|
||||
int toIndex = offset + length;
|
||||
if (toIndex <= leftLength) {
|
||||
return left.partialIsValidUtf8(state, offset, length);
|
||||
} else if (offset >= leftLength) {
|
||||
return right.partialIsValidUtf8(state, offset - leftLength, length);
|
||||
} else {
|
||||
int leftLength = this.leftLength - offset;
|
||||
int leftPartial = left.partialIsValidUtf8(state, offset, leftLength);
|
||||
return right.partialIsValidUtf8(leftPartial, 0, length - leftLength);
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// equals() and hashCode()
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof ByteString)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ByteString otherByteString = (ByteString) other;
|
||||
if (totalLength != otherByteString.size()) {
|
||||
return false;
|
||||
}
|
||||
if (totalLength == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// You don't really want to be calling equals on long strings, but since
|
||||
// we cache the hashCode, we effectively cache inequality. We use the cached
|
||||
// hashCode if it's already computed. It's arguable we should compute the
|
||||
// hashCode here, and if we're going to be testing a bunch of byteStrings,
|
||||
// it might even make sense.
|
||||
if (hash != 0) {
|
||||
int cachedOtherHash = otherByteString.peekCachedHashCode();
|
||||
if (cachedOtherHash != 0 && hash != cachedOtherHash) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return equalsFragments(otherByteString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this string is equal to another of the same length by
|
||||
* iterating over the leaf nodes. On each step of the iteration, the
|
||||
* overlapping segments of the leaves are compared.
|
||||
*
|
||||
* @param other string of the same length as this one
|
||||
* @return true if the values of this string equals the value of the given
|
||||
* one
|
||||
*/
|
||||
private boolean equalsFragments(ByteString other) {
|
||||
int thisOffset = 0;
|
||||
Iterator<LiteralByteString> thisIter = new PieceIterator(this);
|
||||
LiteralByteString thisString = thisIter.next();
|
||||
|
||||
int thatOffset = 0;
|
||||
Iterator<LiteralByteString> thatIter = new PieceIterator(other);
|
||||
LiteralByteString thatString = thatIter.next();
|
||||
|
||||
int pos = 0;
|
||||
while (true) {
|
||||
int thisRemaining = thisString.size() - thisOffset;
|
||||
int thatRemaining = thatString.size() - thatOffset;
|
||||
int bytesToCompare = Math.min(thisRemaining, thatRemaining);
|
||||
|
||||
// At least one of the offsets will be zero
|
||||
boolean stillEqual = (thisOffset == 0)
|
||||
? thisString.equalsRange(thatString, thatOffset, bytesToCompare)
|
||||
: thatString.equalsRange(thisString, thisOffset, bytesToCompare);
|
||||
if (!stillEqual) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pos += bytesToCompare;
|
||||
if (pos >= totalLength) {
|
||||
if (pos == totalLength) {
|
||||
return true;
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
// We always get to the end of at least one of the pieces
|
||||
if (bytesToCompare == thisRemaining) { // If reached end of this
|
||||
thisOffset = 0;
|
||||
thisString = thisIter.next();
|
||||
} else {
|
||||
thisOffset += bytesToCompare;
|
||||
}
|
||||
if (bytesToCompare == thatRemaining) { // If reached end of that
|
||||
thatOffset = 0;
|
||||
thatString = thatIter.next();
|
||||
} else {
|
||||
thatOffset += bytesToCompare;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cached hash value. Intentionally accessed via a data race, which is safe
|
||||
* because of the Java Memory Model's "no out-of-thin-air values" guarantees
|
||||
* for ints.
|
||||
*/
|
||||
private int hash = 0;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = hash;
|
||||
|
||||
if (h == 0) {
|
||||
h = totalLength;
|
||||
h = partialHash(h, 0, totalLength);
|
||||
if (h == 0) {
|
||||
h = 1;
|
||||
}
|
||||
hash = h;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int peekCachedHashCode() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int partialHash(int h, int offset, int length) {
|
||||
int toIndex = offset + length;
|
||||
if (toIndex <= leftLength) {
|
||||
return left.partialHash(h, offset, length);
|
||||
} else if (offset >= leftLength) {
|
||||
return right.partialHash(h, offset - leftLength, length);
|
||||
} else {
|
||||
int leftLength = this.leftLength - offset;
|
||||
int leftPartial = left.partialHash(h, offset, leftLength);
|
||||
return right.partialHash(leftPartial, 0, length - leftLength);
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Input stream
|
||||
|
||||
@Override
|
||||
public CodedInputStream newCodedInput() {
|
||||
return CodedInputStream.newInstance(new RopeInputStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream newInput() {
|
||||
return new RopeInputStream();
|
||||
}
|
||||
|
||||
/**
|
||||
* This class implements the balancing algorithm of BAP95. In the paper the
|
||||
* authors use an array to keep track of pieces, while here we use a stack.
|
||||
* The tree is balanced by traversing subtrees in left to right order, and the
|
||||
* stack always contains the part of the string we've traversed so far.
|
||||
*
|
||||
* <p>One surprising aspect of the algorithm is the result of balancing is not
|
||||
* necessarily balanced, though it is nearly balanced. For details, see
|
||||
* BAP95.
|
||||
*/
|
||||
private static class Balancer {
|
||||
// Stack containing the part of the string, starting from the left, that
|
||||
// we've already traversed. The final string should be the equivalent of
|
||||
// concatenating the strings on the stack from bottom to top.
|
||||
private final Stack<ByteString> prefixesStack = new Stack<ByteString>();
|
||||
|
||||
private ByteString balance(ByteString left, ByteString right) {
|
||||
doBalance(left);
|
||||
doBalance(right);
|
||||
|
||||
// Sweep stack to gather the result
|
||||
ByteString partialString = prefixesStack.pop();
|
||||
while (!prefixesStack.isEmpty()) {
|
||||
ByteString newLeft = prefixesStack.pop();
|
||||
partialString = new RopeByteString(newLeft, partialString);
|
||||
}
|
||||
// We should end up with a RopeByteString since at a minimum we will
|
||||
// create one from concatenating left and right
|
||||
return partialString;
|
||||
}
|
||||
|
||||
private void doBalance(ByteString root) {
|
||||
// BAP95: Insert balanced subtrees whole. This means the result might not
|
||||
// be balanced, leading to repeated rebalancings on concatenate. However,
|
||||
// these rebalancings are shallow due to ignoring balanced subtrees, and
|
||||
// relatively few calls to insert() result.
|
||||
if (root.isBalanced()) {
|
||||
insert(root);
|
||||
} else if (root instanceof RopeByteString) {
|
||||
RopeByteString rbs = (RopeByteString) root;
|
||||
doBalance(rbs.left);
|
||||
doBalance(rbs.right);
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Has a new type of ByteString been created? Found " +
|
||||
root.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a string on the balance stack (BAP95). BAP95 uses an array and
|
||||
* calls the elements in the array 'bins'. We instead use a stack, so the
|
||||
* 'bins' of lengths are represented by differences between the elements of
|
||||
* minLengthByDepth.
|
||||
*
|
||||
* <p>If the length bin for our string, and all shorter length bins, are
|
||||
* empty, we just push it on the stack. Otherwise, we need to start
|
||||
* concatenating, putting the given string in the "middle" and continuing
|
||||
* until we land in an empty length bin that matches the length of our
|
||||
* concatenation.
|
||||
*
|
||||
* @param byteString string to place on the balance stack
|
||||
*/
|
||||
private void insert(ByteString byteString) {
|
||||
int depthBin = getDepthBinForLength(byteString.size());
|
||||
int binEnd = minLengthByDepth[depthBin + 1];
|
||||
|
||||
// BAP95: Concatenate all trees occupying bins representing the length of
|
||||
// our new piece or of shorter pieces, to the extent that is possible.
|
||||
// The goal is to clear the bin which our piece belongs in, but that may
|
||||
// not be entirely possible if there aren't enough longer bins occupied.
|
||||
if (prefixesStack.isEmpty() || prefixesStack.peek().size() >= binEnd) {
|
||||
prefixesStack.push(byteString);
|
||||
} else {
|
||||
int binStart = minLengthByDepth[depthBin];
|
||||
|
||||
// Concatenate the subtrees of shorter length
|
||||
ByteString newTree = prefixesStack.pop();
|
||||
while (!prefixesStack.isEmpty()
|
||||
&& prefixesStack.peek().size() < binStart) {
|
||||
ByteString left = prefixesStack.pop();
|
||||
newTree = new RopeByteString(left, newTree);
|
||||
}
|
||||
|
||||
// Concatenate the given string
|
||||
newTree = new RopeByteString(newTree, byteString);
|
||||
|
||||
// Continue concatenating until we land in an empty bin
|
||||
while (!prefixesStack.isEmpty()) {
|
||||
depthBin = getDepthBinForLength(newTree.size());
|
||||
binEnd = minLengthByDepth[depthBin + 1];
|
||||
if (prefixesStack.peek().size() < binEnd) {
|
||||
ByteString left = prefixesStack.pop();
|
||||
newTree = new RopeByteString(left, newTree);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
prefixesStack.push(newTree);
|
||||
}
|
||||
}
|
||||
|
||||
private int getDepthBinForLength(int length) {
|
||||
int depth = Arrays.binarySearch(minLengthByDepth, length);
|
||||
if (depth < 0) {
|
||||
// It wasn't an exact match, so convert to the index of the containing
|
||||
// fragment, which is one less even than the insertion point.
|
||||
int insertionPoint = -(depth + 1);
|
||||
depth = insertionPoint - 1;
|
||||
}
|
||||
|
||||
return depth;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a continuable tree traversal, which keeps the state
|
||||
* information which would exist on the stack in a recursive traversal instead
|
||||
* on a stack of "Bread Crumbs". The maximum depth of the stack in this
|
||||
* iterator is the same as the depth of the tree being traversed.
|
||||
*
|
||||
* <p>This iterator is used to implement
|
||||
* {@link RopeByteString#equalsFragments(ByteString)}.
|
||||
*/
|
||||
private static class PieceIterator implements Iterator<LiteralByteString> {
|
||||
|
||||
private final Stack<RopeByteString> breadCrumbs =
|
||||
new Stack<RopeByteString>();
|
||||
private LiteralByteString next;
|
||||
|
||||
private PieceIterator(ByteString root) {
|
||||
next = getLeafByLeft(root);
|
||||
}
|
||||
|
||||
private LiteralByteString getLeafByLeft(ByteString root) {
|
||||
ByteString pos = root;
|
||||
while (pos instanceof RopeByteString) {
|
||||
RopeByteString rbs = (RopeByteString) pos;
|
||||
breadCrumbs.push(rbs);
|
||||
pos = rbs.left;
|
||||
}
|
||||
return (LiteralByteString) pos;
|
||||
}
|
||||
|
||||
private LiteralByteString getNextNonEmptyLeaf() {
|
||||
while (true) {
|
||||
// Almost always, we go through this loop exactly once. However, if
|
||||
// we discover an empty string in the rope, we toss it and try again.
|
||||
if (breadCrumbs.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
LiteralByteString result = getLeafByLeft(breadCrumbs.pop().right);
|
||||
if (!result.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return next != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next item and advances one {@code LiteralByteString}.
|
||||
*
|
||||
* @return next non-empty LiteralByteString or {@code null}
|
||||
*/
|
||||
public LiteralByteString next() {
|
||||
if (next == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
LiteralByteString result = next;
|
||||
next = getNextNonEmptyLeaf();
|
||||
return result;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteIterator
|
||||
|
||||
@Override
|
||||
public ByteIterator iterator() {
|
||||
return new RopeByteIterator();
|
||||
}
|
||||
|
||||
private class RopeByteIterator implements ByteString.ByteIterator {
|
||||
|
||||
private final PieceIterator pieces;
|
||||
private ByteIterator bytes;
|
||||
int bytesRemaining;
|
||||
|
||||
private RopeByteIterator() {
|
||||
pieces = new PieceIterator(RopeByteString.this);
|
||||
bytes = pieces.next().iterator();
|
||||
bytesRemaining = size();
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return (bytesRemaining > 0);
|
||||
}
|
||||
|
||||
public Byte next() {
|
||||
return nextByte(); // Does not instantiate a Byte
|
||||
}
|
||||
|
||||
public byte nextByte() {
|
||||
if (!bytes.hasNext()) {
|
||||
bytes = pieces.next().iterator();
|
||||
}
|
||||
--bytesRemaining;
|
||||
return bytes.nextByte();
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is the {@link RopeByteString} equivalent for
|
||||
* {@link ByteArrayInputStream}.
|
||||
*/
|
||||
private class RopeInputStream extends InputStream {
|
||||
// Iterates through the pieces of the rope
|
||||
private PieceIterator pieceIterator;
|
||||
// The current piece
|
||||
private LiteralByteString currentPiece;
|
||||
// The size of the current piece
|
||||
private int currentPieceSize;
|
||||
// The index of the next byte to read in the current piece
|
||||
private int currentPieceIndex;
|
||||
// The offset of the start of the current piece in the rope byte string
|
||||
private int currentPieceOffsetInRope;
|
||||
// Offset in the buffer at which user called mark();
|
||||
private int mark;
|
||||
|
||||
public RopeInputStream() {
|
||||
initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte b[], int offset, int length) {
|
||||
if (b == null) {
|
||||
throw new NullPointerException();
|
||||
} else if (offset < 0 || length < 0 || length > b.length - offset) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
return readSkipInternal(b, offset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long length) {
|
||||
if (length < 0) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else if (length > Integer.MAX_VALUE) {
|
||||
length = Integer.MAX_VALUE;
|
||||
}
|
||||
return readSkipInternal(null, 0, (int) length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal implementation of read and skip. If b != null, then read the
|
||||
* next {@code length} bytes into the buffer {@code b} at
|
||||
* offset {@code offset}. If b == null, then skip the next {@code length)
|
||||
* bytes.
|
||||
* <p>
|
||||
* This method assumes that all error checking has already happened.
|
||||
* <p>
|
||||
* Returns the actual number of bytes read or skipped.
|
||||
*/
|
||||
private int readSkipInternal(byte b[], int offset, int length) {
|
||||
int bytesRemaining = length;
|
||||
while (bytesRemaining > 0) {
|
||||
advanceIfCurrentPieceFullyRead();
|
||||
if (currentPiece == null) {
|
||||
if (bytesRemaining == length) {
|
||||
// We didn't manage to read anything
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
// Copy the bytes from this piece.
|
||||
int currentPieceRemaining = currentPieceSize - currentPieceIndex;
|
||||
int count = Math.min(currentPieceRemaining, bytesRemaining);
|
||||
if (b != null) {
|
||||
currentPiece.copyTo(b, currentPieceIndex, offset, count);
|
||||
offset += count;
|
||||
}
|
||||
currentPieceIndex += count;
|
||||
bytesRemaining -= count;
|
||||
}
|
||||
}
|
||||
// Return the number of bytes read.
|
||||
return length - bytesRemaining;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
advanceIfCurrentPieceFullyRead();
|
||||
if (currentPiece == null) {
|
||||
return -1;
|
||||
} else {
|
||||
return currentPiece.byteAt(currentPieceIndex++) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
int bytesRead = currentPieceOffsetInRope + currentPieceIndex;
|
||||
return RopeByteString.this.size() - bytesRead;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mark(int readAheadLimit) {
|
||||
// Set the mark to our position in the byte string
|
||||
mark = currentPieceOffsetInRope + currentPieceIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reset() {
|
||||
// Just reinitialize and skip the specified number of bytes.
|
||||
initialize();
|
||||
readSkipInternal(null, 0, mark);
|
||||
}
|
||||
|
||||
/** Common initialization code used by both the constructor and reset() */
|
||||
private void initialize() {
|
||||
pieceIterator = new PieceIterator(RopeByteString.this);
|
||||
currentPiece = pieceIterator.next();
|
||||
currentPieceSize = currentPiece.size();
|
||||
currentPieceIndex = 0;
|
||||
currentPieceOffsetInRope = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips to the next piece if we have read all the data in the current
|
||||
* piece. Sets currentPiece to null if we have reached the end of the
|
||||
* input.
|
||||
*/
|
||||
private void advanceIfCurrentPieceFullyRead() {
|
||||
if (currentPiece != null && currentPieceIndex == currentPieceSize) {
|
||||
// Generally, we can only go through this loop at most once, since
|
||||
// empty strings can't end up in a rope. But better to test.
|
||||
currentPieceOffsetInRope += currentPieceSize;
|
||||
currentPieceIndex = 0;
|
||||
if (pieceIterator.hasNext()) {
|
||||
currentPiece = pieceIterator.next();
|
||||
currentPieceSize = currentPiece.size();
|
||||
} else {
|
||||
currentPiece = null;
|
||||
currentPieceSize = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
/**
|
||||
* Interface for an RPC callback, normally called when an RPC completes.
|
||||
* {@code ParameterType} is normally the method's response message type.
|
||||
*
|
||||
* <p>Starting with version 2.3.0, RPC implementations should not try to build
|
||||
* on this, but should instead provide code generator plugins which generate
|
||||
* code specific to the particular RPC implementation. This way the generated
|
||||
* code can be more appropriate for the implementation in use and can avoid
|
||||
* unnecessary layers of indirection.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public interface RpcCallback<ParameterType> {
|
||||
void run(ParameterType parameter);
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
/**
|
||||
* <p>Abstract interface for an RPC channel. An {@code RpcChannel} represents a
|
||||
* communication line to a {@link Service} which can be used to call that
|
||||
* {@link Service}'s methods. The {@link Service} may be running on another
|
||||
* machine. Normally, you should not call an {@code RpcChannel} directly, but
|
||||
* instead construct a stub {@link Service} wrapping it. Example:
|
||||
*
|
||||
* <pre>
|
||||
* RpcChannel channel = rpcImpl.newChannel("remotehost.example.com:1234");
|
||||
* RpcController controller = rpcImpl.newController();
|
||||
* MyService service = MyService.newStub(channel);
|
||||
* service.myMethod(controller, request, callback);
|
||||
* </pre>
|
||||
*
|
||||
* <p>Starting with version 2.3.0, RPC implementations should not try to build
|
||||
* on this, but should instead provide code generator plugins which generate
|
||||
* code specific to the particular RPC implementation. This way the generated
|
||||
* code can be more appropriate for the implementation in use and can avoid
|
||||
* unnecessary layers of indirection.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public interface RpcChannel {
|
||||
/**
|
||||
* Call the given method of the remote service. This method is similar to
|
||||
* {@code Service.callMethod()} with one important difference: the caller
|
||||
* decides the types of the {@code Message} objects, not the callee. The
|
||||
* request may be of any type as long as
|
||||
* {@code request.getDescriptor() == method.getInputType()}.
|
||||
* The response passed to the callback will be of the same type as
|
||||
* {@code responsePrototype} (which must have
|
||||
* {@code getDescriptor() == method.getOutputType()}).
|
||||
*/
|
||||
void callMethod(Descriptors.MethodDescriptor method,
|
||||
RpcController controller,
|
||||
Message request,
|
||||
Message responsePrototype,
|
||||
RpcCallback<Message> done);
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
/**
|
||||
* <p>An {@code RpcController} mediates a single method call. The primary
|
||||
* purpose of the controller is to provide a way to manipulate settings
|
||||
* specific to the RPC implementation and to find out about RPC-level errors.
|
||||
*
|
||||
* <p>Starting with version 2.3.0, RPC implementations should not try to build
|
||||
* on this, but should instead provide code generator plugins which generate
|
||||
* code specific to the particular RPC implementation. This way the generated
|
||||
* code can be more appropriate for the implementation in use and can avoid
|
||||
* unnecessary layers of indirection.
|
||||
*
|
||||
* <p>The methods provided by the {@code RpcController} interface are intended
|
||||
* to be a "least common denominator" set of features which we expect all
|
||||
* implementations to support. Specific implementations may provide more
|
||||
* advanced features (e.g. deadline propagation).
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public interface RpcController {
|
||||
// -----------------------------------------------------------------
|
||||
// These calls may be made from the client side only. Their results
|
||||
// are undefined on the server side (may throw RuntimeExceptions).
|
||||
|
||||
/**
|
||||
* Resets the RpcController to its initial state so that it may be reused in
|
||||
* a new call. This can be called from the client side only. It must not
|
||||
* be called while an RPC is in progress.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* After a call has finished, returns true if the call failed. The possible
|
||||
* reasons for failure depend on the RPC implementation. {@code failed()}
|
||||
* most only be called on the client side, and must not be called before a
|
||||
* call has finished.
|
||||
*/
|
||||
boolean failed();
|
||||
|
||||
/**
|
||||
* If {@code failed()} is {@code true}, returns a human-readable description
|
||||
* of the error.
|
||||
*/
|
||||
String errorText();
|
||||
|
||||
/**
|
||||
* Advises the RPC system that the caller desires that the RPC call be
|
||||
* canceled. The RPC system may cancel it immediately, may wait awhile and
|
||||
* then cancel it, or may not even cancel the call at all. If the call is
|
||||
* canceled, the "done" callback will still be called and the RpcController
|
||||
* will indicate that the call failed at that time.
|
||||
*/
|
||||
void startCancel();
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// These calls may be made from the server side only. Their results
|
||||
// are undefined on the client side (may throw RuntimeExceptions).
|
||||
|
||||
/**
|
||||
* Causes {@code failed()} to return true on the client side. {@code reason}
|
||||
* will be incorporated into the message returned by {@code errorText()}.
|
||||
* If you find you need to return machine-readable information about
|
||||
* failures, you should incorporate it into your response protocol buffer
|
||||
* and should NOT call {@code setFailed()}.
|
||||
*/
|
||||
void setFailed(String reason);
|
||||
|
||||
/**
|
||||
* If {@code true}, indicates that the client canceled the RPC, so the server
|
||||
* may as well give up on replying to it. This method must be called on the
|
||||
* server side only. The server should still call the final "done" callback.
|
||||
*/
|
||||
boolean isCanceled();
|
||||
|
||||
/**
|
||||
* Asks that the given callback be called when the RPC is canceled. The
|
||||
* parameter passed to the callback will always be {@code null}. The
|
||||
* callback will always be called exactly once. If the RPC completes without
|
||||
* being canceled, the callback will be called after completion. If the RPC
|
||||
* has already been canceled when NotifyOnCancel() is called, the callback
|
||||
* will be called immediately.
|
||||
*
|
||||
* <p>{@code notifyOnCancel()} must be called no more than once per request.
|
||||
* It must be called on the server side only.
|
||||
*/
|
||||
void notifyOnCancel(RpcCallback<Object> callback);
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
/**
|
||||
* Grab-bag of utility functions useful when dealing with RPCs.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public final class RpcUtil {
|
||||
private RpcUtil() {}
|
||||
|
||||
/**
|
||||
* Take an {@code RpcCallback<Message>} and convert it to an
|
||||
* {@code RpcCallback} accepting a specific message type. This is always
|
||||
* type-safe (parameter type contravariance).
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <Type extends Message> RpcCallback<Type>
|
||||
specializeCallback(final RpcCallback<Message> originalCallback) {
|
||||
return (RpcCallback<Type>)originalCallback;
|
||||
// The above cast works, but only due to technical details of the Java
|
||||
// implementation. A more theoretically correct -- but less efficient --
|
||||
// implementation would be as follows:
|
||||
// return new RpcCallback<Type>() {
|
||||
// public void run(Type parameter) {
|
||||
// originalCallback.run(parameter);
|
||||
// }
|
||||
// };
|
||||
}
|
||||
|
||||
/**
|
||||
* Take an {@code RpcCallback} accepting a specific message type and convert
|
||||
* it to an {@code RpcCallback<Message>}. The generalized callback will
|
||||
* accept any message object which has the same descriptor, and will convert
|
||||
* it to the correct class before calling the original callback. However,
|
||||
* if the generalized callback is given a message with a different descriptor,
|
||||
* an exception will be thrown.
|
||||
*/
|
||||
public static <Type extends Message>
|
||||
RpcCallback<Message> generalizeCallback(
|
||||
final RpcCallback<Type> originalCallback,
|
||||
final Class<Type> originalClass,
|
||||
final Type defaultInstance) {
|
||||
return new RpcCallback<Message>() {
|
||||
public void run(final Message parameter) {
|
||||
Type typedParameter;
|
||||
try {
|
||||
typedParameter = originalClass.cast(parameter);
|
||||
} catch (ClassCastException ignored) {
|
||||
typedParameter = copyAsType(defaultInstance, parameter);
|
||||
}
|
||||
originalCallback.run(typedParameter);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new message of type "Type" which is a copy of "source". "source"
|
||||
* must have the same descriptor but may be a different class (e.g.
|
||||
* DynamicMessage).
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <Type extends Message> Type copyAsType(
|
||||
final Type typeDefaultInstance, final Message source) {
|
||||
return (Type)typeDefaultInstance.newBuilderForType()
|
||||
.mergeFrom(source)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a callback which can only be called once. This may be useful for
|
||||
* security, when passing a callback to untrusted code: most callbacks do
|
||||
* not expect to be called more than once, so doing so may expose bugs if it
|
||||
* is not prevented.
|
||||
*/
|
||||
public static <ParameterType>
|
||||
RpcCallback<ParameterType> newOneTimeCallback(
|
||||
final RpcCallback<ParameterType> originalCallback) {
|
||||
return new RpcCallback<ParameterType>() {
|
||||
private boolean alreadyCalled = false;
|
||||
|
||||
public void run(final ParameterType parameter) {
|
||||
synchronized(this) {
|
||||
if (alreadyCalled) {
|
||||
throw new AlreadyCalledException();
|
||||
}
|
||||
alreadyCalled = true;
|
||||
}
|
||||
|
||||
originalCallback.run(parameter);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception thrown when a one-time callback is called more than once.
|
||||
*/
|
||||
public static final class AlreadyCalledException extends RuntimeException {
|
||||
private static final long serialVersionUID = 5469741279507848266L;
|
||||
|
||||
public AlreadyCalledException() {
|
||||
super("This RpcCallback was already called and cannot be called " +
|
||||
"multiple times.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
/**
|
||||
* Abstract base interface for protocol-buffer-based RPC services. Services
|
||||
* themselves are abstract classes (implemented either by servers or as
|
||||
* stubs), but they subclass this base interface. The methods of this
|
||||
* interface can be used to call the methods of the service without knowing
|
||||
* its exact type at compile time (analogous to the Message interface).
|
||||
*
|
||||
* <p>Starting with version 2.3.0, RPC implementations should not try to build
|
||||
* on this, but should instead provide code generator plugins which generate
|
||||
* code specific to the particular RPC implementation. This way the generated
|
||||
* code can be more appropriate for the implementation in use and can avoid
|
||||
* unnecessary layers of indirection.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public interface Service {
|
||||
/**
|
||||
* Get the {@code ServiceDescriptor} describing this service and its methods.
|
||||
*/
|
||||
Descriptors.ServiceDescriptor getDescriptorForType();
|
||||
|
||||
/**
|
||||
* <p>Call a method of the service specified by MethodDescriptor. This is
|
||||
* normally implemented as a simple {@code switch()} that calls the standard
|
||||
* definitions of the service's methods.
|
||||
*
|
||||
* <p>Preconditions:
|
||||
* <ul>
|
||||
* <li>{@code method.getService() == getDescriptorForType()}
|
||||
* <li>{@code request} is of the exact same class as the object returned by
|
||||
* {@code getRequestPrototype(method)}.
|
||||
* <li>{@code controller} is of the correct type for the RPC implementation
|
||||
* being used by this Service. For stubs, the "correct type" depends
|
||||
* on the RpcChannel which the stub is using. Server-side Service
|
||||
* implementations are expected to accept whatever type of
|
||||
* {@code RpcController} the server-side RPC implementation uses.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Postconditions:
|
||||
* <ul>
|
||||
* <li>{@code done} will be called when the method is complete. This may be
|
||||
* before {@code callMethod()} returns or it may be at some point in
|
||||
* the future.
|
||||
* <li>The parameter to {@code done} is the response. It must be of the
|
||||
* exact same type as would be returned by
|
||||
* {@code getResponsePrototype(method)}.
|
||||
* <li>If the RPC failed, the parameter to {@code done} will be
|
||||
* {@code null}. Further details about the failure can be found by
|
||||
* querying {@code controller}.
|
||||
* </ul>
|
||||
*/
|
||||
void callMethod(Descriptors.MethodDescriptor method,
|
||||
RpcController controller,
|
||||
Message request,
|
||||
RpcCallback<Message> done);
|
||||
|
||||
/**
|
||||
* <p>{@code callMethod()} requires that the request passed in is of a
|
||||
* particular subclass of {@code Message}. {@code getRequestPrototype()}
|
||||
* gets the default instances of this type for a given method. You can then
|
||||
* call {@code Message.newBuilderForType()} on this instance to
|
||||
* construct a builder to build an object which you can then pass to
|
||||
* {@code callMethod()}.
|
||||
*
|
||||
* <p>Example:
|
||||
* <pre>
|
||||
* MethodDescriptor method =
|
||||
* service.getDescriptorForType().findMethodByName("Foo");
|
||||
* Message request =
|
||||
* stub.getRequestPrototype(method).newBuilderForType()
|
||||
* .mergeFrom(input).build();
|
||||
* service.callMethod(method, request, callback);
|
||||
* </pre>
|
||||
*/
|
||||
Message getRequestPrototype(Descriptors.MethodDescriptor method);
|
||||
|
||||
/**
|
||||
* Like {@code getRequestPrototype()}, but gets a prototype of the response
|
||||
* message. {@code getResponsePrototype()} is generally not needed because
|
||||
* the {@code Service} implementation constructs the response message itself,
|
||||
* but it may be useful in some cases to know ahead of time what type of
|
||||
* object will be returned.
|
||||
*/
|
||||
Message getResponsePrototype(Descriptors.MethodDescriptor method);
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
/**
|
||||
* Thrown by blocking RPC methods when a failure occurs.
|
||||
*
|
||||
* @author cpovirk@google.com (Chris Povirk)
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
/**
|
||||
* {@code SingleFieldBuilder} implements a structure that a protocol
|
||||
* message uses to hold a single field of another protocol message. It supports
|
||||
* the classical use case of setting an immutable {@link Message} as the value
|
||||
* of the field and is highly optimized around this.
|
||||
* <br>
|
||||
* It also supports the additional use case of setting a {@link Message.Builder}
|
||||
* as the field and deferring conversion of that {@code Builder}
|
||||
* to an immutable {@code Message}. In this way, it's possible to maintain
|
||||
* a tree of {@code Builder}'s that acts as a fully read/write data
|
||||
* structure.
|
||||
* <br>
|
||||
* Logically, one can think of a tree of builders as converting the entire tree
|
||||
* to messages when build is called on the root or when any method is called
|
||||
* that desires a Message instead of a Builder. In terms of the implementation,
|
||||
* the {@code SingleFieldBuilder} and {@code RepeatedFieldBuilder}
|
||||
* 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}, it is converted to a {@code Message} by
|
||||
* calling {@link Message.Builder#buildPartial} on it. If no message has
|
||||
* been set, returns the default instance of the message.
|
||||
*
|
||||
* @return the message for the field
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public MType getMessage() {
|
||||
if (message == null) {
|
||||
// If message is null, the invariant is that we must be have a builder.
|
||||
message = (MType) builder.buildPartial();
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the message and returns it.
|
||||
*
|
||||
* @return the message
|
||||
*/
|
||||
public MType build() {
|
||||
// Now that build has been called, we are required to dispatch
|
||||
// invalidations.
|
||||
isClean = true;
|
||||
return getMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a builder for the field. If no builder has been created yet, a
|
||||
* builder is created on demand by calling {@link Message#toBuilder}.
|
||||
*
|
||||
* @return The builder for the field
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public BType getBuilder() {
|
||||
if (builder == null) {
|
||||
// builder.mergeFrom() on a fresh builder
|
||||
// does not create any sub-objects with independent clean/dirty states,
|
||||
// therefore setting the builder itself to clean without actually calling
|
||||
// build() cannot break any invariants.
|
||||
builder = (BType) message.newBuilderForType(this);
|
||||
builder.mergeFrom(message); // no-op if message is the default message
|
||||
builder.markClean();
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the base class interface for the field. This may either be a builder
|
||||
* or a message. It will return whatever is more efficient.
|
||||
*
|
||||
* @return the message or builder for the field as the base class interface
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public IType getMessageOrBuilder() {
|
||||
if (builder != null) {
|
||||
return (IType) builder;
|
||||
} else {
|
||||
return (IType) message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a message for the field replacing any existing value.
|
||||
*
|
||||
* @param message the message to set
|
||||
* @return the builder
|
||||
*/
|
||||
public 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 (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void markDirty() {
|
||||
onChanged();
|
||||
}
|
||||
}
|
||||
@@ -1,618 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
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> {@code
|
||||
* 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 within 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 (Java 1.6 override semantics, but we must support 1.5)
|
||||
public K getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public V getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public int compareTo(Entry other) {
|
||||
return getKey().compareTo(other.getKey());
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
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 (Java 1.6 override semantics, but we must support 1.5)
|
||||
public boolean hasNext() {
|
||||
return (pos + 1) < entryList.size() ||
|
||||
getOverflowIterator().hasNext();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
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 (Java 1.6 override semantics, but we must support 1.5)
|
||||
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 (Java 1.6 override semantics, but we must support 1.5)
|
||||
public boolean hasNext() {
|
||||
return false;
|
||||
}
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public Object next() {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
|
||||
private static final Iterable<Object> ITERABLE = new Iterable<Object>() {
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public Iterator<Object> iterator() {
|
||||
return ITERATOR;
|
||||
}
|
||||
};
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> Iterable<T> iterable() {
|
||||
return (Iterable<T>) ITERABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,99 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Thrown when attempting to build a protocol message that is missing required
|
||||
* fields. This is a {@code RuntimeException} because it normally represents
|
||||
* a programming error: it happens when some code which constructs a message
|
||||
* fails to set all the fields. {@code parseFrom()} methods <b>do not</b>
|
||||
* throw this; they throw an {@link InvalidProtocolBufferException} if
|
||||
* required fields are missing, because it is not a programming error to
|
||||
* receive an incomplete message. In other words,
|
||||
* {@code UninitializedMessageException} should never be thrown by correct
|
||||
* code, but {@code InvalidProtocolBufferException} might be.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class UninitializedMessageException extends RuntimeException {
|
||||
private static final long serialVersionUID = -7466929953374883507L;
|
||||
|
||||
public UninitializedMessageException(final MessageLite message) {
|
||||
super("Message was missing required fields. (Lite runtime could not " +
|
||||
"determine which fields were missing).");
|
||||
missingFields = null;
|
||||
}
|
||||
|
||||
public UninitializedMessageException(final List<String> missingFields) {
|
||||
super(buildDescription(missingFields));
|
||||
this.missingFields = missingFields;
|
||||
}
|
||||
|
||||
private final List<String> missingFields;
|
||||
|
||||
/**
|
||||
* Get a list of human-readable names of required fields missing from this
|
||||
* message. Each name is a full path to a field, e.g. "foo.bar[5].baz".
|
||||
* Returns null if the lite runtime was used, since it lacks the ability to
|
||||
* find missing fields.
|
||||
*/
|
||||
public List<String> getMissingFields() {
|
||||
return Collections.unmodifiableList(missingFields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this exception to an {@link InvalidProtocolBufferException}.
|
||||
* When a parsed message is missing required fields, this should be thrown
|
||||
* instead of {@code UninitializedMessageException}.
|
||||
*/
|
||||
public InvalidProtocolBufferException asInvalidProtocolBufferException() {
|
||||
return new InvalidProtocolBufferException(getMessage());
|
||||
}
|
||||
|
||||
/** Construct the description string for this exception. */
|
||||
private static String buildDescription(final List<String> missingFields) {
|
||||
final StringBuilder description =
|
||||
new StringBuilder("Message missing required fields: ");
|
||||
boolean first = true;
|
||||
for (final String field : missingFields) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
description.append(", ");
|
||||
}
|
||||
description.append(field);
|
||||
}
|
||||
return description.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,978 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.AbstractMessageLite.Builder.LimitedInputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* {@code UnknownFieldSet} is used to keep track of fields which were seen when
|
||||
* parsing a protocol message but whose field numbers or types are unrecognized.
|
||||
* This most frequently occurs when new fields are added to a message type
|
||||
* and then messages containing those fields are read by old software that was
|
||||
* compiled before the new types were added.
|
||||
*
|
||||
* <p>Every {@link Message} contains an {@code UnknownFieldSet} (and every
|
||||
* {@link Message.Builder} contains an {@link Builder}).
|
||||
*
|
||||
* <p>Most users will never need to use this class.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public final class UnknownFieldSet implements MessageLite {
|
||||
private UnknownFieldSet() {}
|
||||
|
||||
/** Create a new {@link Builder}. */
|
||||
public static Builder newBuilder() {
|
||||
return Builder.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Builder} and initialize it to be a copy
|
||||
* of {@code copyFrom}.
|
||||
*/
|
||||
public static Builder newBuilder(final UnknownFieldSet copyFrom) {
|
||||
return newBuilder().mergeFrom(copyFrom);
|
||||
}
|
||||
|
||||
/** Get an empty {@code UnknownFieldSet}. */
|
||||
public static UnknownFieldSet getDefaultInstance() {
|
||||
return defaultInstance;
|
||||
}
|
||||
public UnknownFieldSet getDefaultInstanceForType() {
|
||||
return defaultInstance;
|
||||
}
|
||||
private static final UnknownFieldSet defaultInstance =
|
||||
new UnknownFieldSet(Collections.<Integer, Field>emptyMap());
|
||||
|
||||
/**
|
||||
* Construct an {@code UnknownFieldSet} around the given map. The map is
|
||||
* expected to be immutable.
|
||||
*/
|
||||
private UnknownFieldSet(final Map<Integer, Field> fields) {
|
||||
this.fields = fields;
|
||||
}
|
||||
private Map<Integer, Field> fields;
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
return (other instanceof UnknownFieldSet) &&
|
||||
fields.equals(((UnknownFieldSet) other).fields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return fields.hashCode();
|
||||
}
|
||||
|
||||
/** Get a map of fields in the set by number. */
|
||||
public Map<Integer, Field> asMap() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
/** Check if the given field number is present in the set. */
|
||||
public boolean hasField(final int number) {
|
||||
return fields.containsKey(number);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a field by number. Returns an empty field if not present. Never
|
||||
* returns {@code null}.
|
||||
*/
|
||||
public Field getField(final int number) {
|
||||
final Field result = fields.get(number);
|
||||
return (result == null) ? Field.getDefaultInstance() : result;
|
||||
}
|
||||
|
||||
/** Serializes the set and writes it to {@code output}. */
|
||||
public void writeTo(final CodedOutputStream output) throws IOException {
|
||||
for (final Map.Entry<Integer, Field> entry : fields.entrySet()) {
|
||||
entry.getValue().writeTo(entry.getKey(), output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the set to a string in protocol buffer text format. This is
|
||||
* just a trivial wrapper around
|
||||
* {@link TextFormat#printToString(UnknownFieldSet)}.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return TextFormat.printToString(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the message to a {@code ByteString} and returns it. This is
|
||||
* just a trivial wrapper around {@link #writeTo(CodedOutputStream)}.
|
||||
*/
|
||||
public ByteString toByteString() {
|
||||
try {
|
||||
final ByteString.CodedBuilder out =
|
||||
ByteString.newCodedBuilder(getSerializedSize());
|
||||
writeTo(out.getCodedOutput());
|
||||
return out.build();
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Serializing to a ByteString threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the message to a {@code byte} array and returns it. This is
|
||||
* just a trivial wrapper around {@link #writeTo(CodedOutputStream)}.
|
||||
*/
|
||||
public byte[] toByteArray() {
|
||||
try {
|
||||
final byte[] result = new byte[getSerializedSize()];
|
||||
final CodedOutputStream output = CodedOutputStream.newInstance(result);
|
||||
writeTo(output);
|
||||
output.checkNoSpaceLeft();
|
||||
return result;
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Serializing to a byte array threw an IOException " +
|
||||
"(should never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the message and writes it to {@code output}. This is just a
|
||||
* trivial wrapper around {@link #writeTo(CodedOutputStream)}.
|
||||
*/
|
||||
public void writeTo(final OutputStream output) throws IOException {
|
||||
final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output);
|
||||
writeTo(codedOutput);
|
||||
codedOutput.flush();
|
||||
}
|
||||
|
||||
public void writeDelimitedTo(OutputStream output) throws IOException {
|
||||
final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output);
|
||||
codedOutput.writeRawVarint32(getSerializedSize());
|
||||
writeTo(codedOutput);
|
||||
codedOutput.flush();
|
||||
}
|
||||
|
||||
/** Get the number of bytes required to encode this set. */
|
||||
public int getSerializedSize() {
|
||||
int result = 0;
|
||||
for (final Map.Entry<Integer, Field> entry : fields.entrySet()) {
|
||||
result += entry.getValue().getSerializedSize(entry.getKey());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the set and writes it to {@code output} using
|
||||
* {@code MessageSet} wire format.
|
||||
*/
|
||||
public void writeAsMessageSetTo(final CodedOutputStream output)
|
||||
throws IOException {
|
||||
for (final Map.Entry<Integer, Field> entry : fields.entrySet()) {
|
||||
entry.getValue().writeAsMessageSetExtensionTo(
|
||||
entry.getKey(), output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of bytes required to encode this set using
|
||||
* {@code MessageSet} wire format.
|
||||
*/
|
||||
public int getSerializedSizeAsMessageSet() {
|
||||
int result = 0;
|
||||
for (final Map.Entry<Integer, Field> entry : fields.entrySet()) {
|
||||
result += entry.getValue().getSerializedSizeAsMessageSetExtension(
|
||||
entry.getKey());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
// UnknownFieldSets do not have required fields, so they are always
|
||||
// initialized.
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Parse an {@code UnknownFieldSet} from the given input stream. */
|
||||
public static UnknownFieldSet parseFrom(final CodedInputStream input)
|
||||
throws IOException {
|
||||
return newBuilder().mergeFrom(input).build();
|
||||
}
|
||||
|
||||
/** Parse {@code data} as an {@code UnknownFieldSet} and return it. */
|
||||
public static UnknownFieldSet parseFrom(final ByteString data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return newBuilder().mergeFrom(data).build();
|
||||
}
|
||||
|
||||
/** Parse {@code data} as an {@code UnknownFieldSet} and return it. */
|
||||
public static UnknownFieldSet parseFrom(final byte[] data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return newBuilder().mergeFrom(data).build();
|
||||
}
|
||||
|
||||
/** Parse an {@code UnknownFieldSet} from {@code input} and return it. */
|
||||
public static UnknownFieldSet parseFrom(final InputStream input)
|
||||
throws IOException {
|
||||
return newBuilder().mergeFrom(input).build();
|
||||
}
|
||||
|
||||
public Builder newBuilderForType() {
|
||||
return newBuilder();
|
||||
}
|
||||
|
||||
public Builder toBuilder() {
|
||||
return newBuilder().mergeFrom(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link UnknownFieldSet}s.
|
||||
*
|
||||
* <p>Note that this class maintains {@link Field.Builder}s for all fields
|
||||
* in the set. Thus, adding one element to an existing {@link Field} does not
|
||||
* require making a copy. This is important for efficient parsing of
|
||||
* unknown repeated fields. However, it implies that {@link Field}s cannot
|
||||
* be constructed independently, nor can two {@link UnknownFieldSet}s share
|
||||
* the same {@code Field} object.
|
||||
*
|
||||
* <p>Use {@link UnknownFieldSet#newBuilder()} to construct a {@code Builder}.
|
||||
*/
|
||||
public static final class Builder implements MessageLite.Builder {
|
||||
// This constructor should never be called directly (except from 'create').
|
||||
private Builder() {}
|
||||
|
||||
private Map<Integer, Field> fields;
|
||||
|
||||
// Optimization: We keep around a builder for the last field that was
|
||||
// modified so that we can efficiently add to it multiple times in a
|
||||
// row (important when parsing an unknown repeated field).
|
||||
private int lastFieldNumber;
|
||||
private Field.Builder lastField;
|
||||
|
||||
private static Builder create() {
|
||||
Builder builder = new Builder();
|
||||
builder.reinitialize();
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a field builder for the given field number which includes any
|
||||
* values that already exist.
|
||||
*/
|
||||
private Field.Builder getFieldBuilder(final int number) {
|
||||
if (lastField != null) {
|
||||
if (number == lastFieldNumber) {
|
||||
return lastField;
|
||||
}
|
||||
// Note: addField() will reset lastField and lastFieldNumber.
|
||||
addField(lastFieldNumber, lastField.build());
|
||||
}
|
||||
if (number == 0) {
|
||||
return null;
|
||||
} else {
|
||||
final Field existing = fields.get(number);
|
||||
lastFieldNumber = number;
|
||||
lastField = Field.newBuilder();
|
||||
if (existing != null) {
|
||||
lastField.mergeFrom(existing);
|
||||
}
|
||||
return lastField;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the {@link UnknownFieldSet} and return it.
|
||||
*
|
||||
* <p>Once {@code build()} has been called, the {@code Builder} will no
|
||||
* longer be usable. Calling any method after {@code build()} will result
|
||||
* in undefined behavior and can cause a {@code NullPointerException} to be
|
||||
* thrown.
|
||||
*/
|
||||
public UnknownFieldSet build() {
|
||||
getFieldBuilder(0); // Force lastField to be built.
|
||||
final UnknownFieldSet result;
|
||||
if (fields.isEmpty()) {
|
||||
result = getDefaultInstance();
|
||||
} else {
|
||||
result = new UnknownFieldSet(Collections.unmodifiableMap(fields));
|
||||
}
|
||||
fields = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
public UnknownFieldSet buildPartial() {
|
||||
// No required fields, so this is the same as build().
|
||||
return build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder clone() {
|
||||
getFieldBuilder(0); // Force lastField to be built.
|
||||
return UnknownFieldSet.newBuilder().mergeFrom(
|
||||
new UnknownFieldSet(fields));
|
||||
}
|
||||
|
||||
public UnknownFieldSet getDefaultInstanceForType() {
|
||||
return UnknownFieldSet.getDefaultInstance();
|
||||
}
|
||||
|
||||
private void reinitialize() {
|
||||
fields = Collections.emptyMap();
|
||||
lastFieldNumber = 0;
|
||||
lastField = null;
|
||||
}
|
||||
|
||||
/** Reset the builder to an empty set. */
|
||||
public Builder clear() {
|
||||
reinitialize();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the fields from {@code other} into this set. If a field number
|
||||
* exists in both sets, {@code other}'s values for that field will be
|
||||
* appended to the values in this set.
|
||||
*/
|
||||
public Builder mergeFrom(final UnknownFieldSet other) {
|
||||
if (other != getDefaultInstance()) {
|
||||
for (final Map.Entry<Integer, Field> entry : other.fields.entrySet()) {
|
||||
mergeField(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a field to the {@code UnknownFieldSet}. If a field with the same
|
||||
* number already exists, the two are merged.
|
||||
*/
|
||||
public Builder mergeField(final int number, final Field field) {
|
||||
if (number == 0) {
|
||||
throw new IllegalArgumentException("Zero is not a valid field number.");
|
||||
}
|
||||
if (hasField(number)) {
|
||||
getFieldBuilder(number).mergeFrom(field);
|
||||
} else {
|
||||
// Optimization: We could call getFieldBuilder(number).mergeFrom(field)
|
||||
// in this case, but that would create a copy of the Field object.
|
||||
// We'd rather reuse the one passed to us, so call addField() instead.
|
||||
addField(number, field);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for merging a new field containing a single varint
|
||||
* value. This is used in particular when an unknown enum value is
|
||||
* encountered.
|
||||
*/
|
||||
public Builder mergeVarintField(final int number, final int value) {
|
||||
if (number == 0) {
|
||||
throw new IllegalArgumentException("Zero is not a valid field number.");
|
||||
}
|
||||
getFieldBuilder(number).addVarint(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Check if the given field number is present in the set. */
|
||||
public boolean hasField(final int number) {
|
||||
if (number == 0) {
|
||||
throw new IllegalArgumentException("Zero is not a valid field number.");
|
||||
}
|
||||
return number == lastFieldNumber || fields.containsKey(number);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a field to the {@code UnknownFieldSet}. If a field with the same
|
||||
* number already exists, it is removed.
|
||||
*/
|
||||
public Builder addField(final int number, final Field field) {
|
||||
if (number == 0) {
|
||||
throw new IllegalArgumentException("Zero is not a valid field number.");
|
||||
}
|
||||
if (lastField != null && lastFieldNumber == number) {
|
||||
// Discard this.
|
||||
lastField = null;
|
||||
lastFieldNumber = 0;
|
||||
}
|
||||
if (fields.isEmpty()) {
|
||||
fields = new TreeMap<Integer,Field>();
|
||||
}
|
||||
fields.put(number, field);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all present {@code Field}s as an immutable {@code Map}. If more
|
||||
* fields are added, the changes may or may not be reflected in this map.
|
||||
*/
|
||||
public Map<Integer, Field> asMap() {
|
||||
getFieldBuilder(0); // Force lastField to be built.
|
||||
return Collections.unmodifiableMap(fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an entire message from {@code input} and merge its fields into
|
||||
* this set.
|
||||
*/
|
||||
public Builder mergeFrom(final CodedInputStream input) throws IOException {
|
||||
while (true) {
|
||||
final int tag = input.readTag();
|
||||
if (tag == 0 || !mergeFieldFrom(tag, input)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single field from {@code input} and merge it into this set.
|
||||
* @param tag The field's tag number, which was already parsed.
|
||||
* @return {@code false} if the tag is an end group tag.
|
||||
*/
|
||||
public boolean mergeFieldFrom(final int tag, final CodedInputStream input)
|
||||
throws IOException {
|
||||
final int number = WireFormat.getTagFieldNumber(tag);
|
||||
switch (WireFormat.getTagWireType(tag)) {
|
||||
case WireFormat.WIRETYPE_VARINT:
|
||||
getFieldBuilder(number).addVarint(input.readInt64());
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_FIXED64:
|
||||
getFieldBuilder(number).addFixed64(input.readFixed64());
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_LENGTH_DELIMITED:
|
||||
getFieldBuilder(number).addLengthDelimited(input.readBytes());
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_START_GROUP:
|
||||
final Builder subBuilder = newBuilder();
|
||||
input.readGroup(number, subBuilder,
|
||||
ExtensionRegistry.getEmptyRegistry());
|
||||
getFieldBuilder(number).addGroup(subBuilder.build());
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_END_GROUP:
|
||||
return false;
|
||||
case WireFormat.WIRETYPE_FIXED32:
|
||||
getFieldBuilder(number).addFixed32(input.readFixed32());
|
||||
return true;
|
||||
default:
|
||||
throw InvalidProtocolBufferException.invalidWireType();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse {@code data} as an {@code UnknownFieldSet} and merge it with the
|
||||
* set being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream)}.
|
||||
*/
|
||||
public Builder mergeFrom(final ByteString data)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
final CodedInputStream input = data.newCodedInput();
|
||||
mergeFrom(input);
|
||||
input.checkLastTagWas(0);
|
||||
return this;
|
||||
} catch (final InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a ByteString threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse {@code data} as an {@code UnknownFieldSet} and merge it with the
|
||||
* set being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream)}.
|
||||
*/
|
||||
public Builder mergeFrom(final byte[] data)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
final CodedInputStream input = CodedInputStream.newInstance(data);
|
||||
mergeFrom(input);
|
||||
input.checkLastTagWas(0);
|
||||
return this;
|
||||
} catch (final InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a byte array threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an {@code UnknownFieldSet} from {@code input} and merge it with the
|
||||
* set being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream)}.
|
||||
*/
|
||||
public Builder mergeFrom(final InputStream input) throws IOException {
|
||||
final CodedInputStream codedInput = CodedInputStream.newInstance(input);
|
||||
mergeFrom(codedInput);
|
||||
codedInput.checkLastTagWas(0);
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean mergeDelimitedFrom(InputStream input)
|
||||
throws IOException {
|
||||
final int firstByte = input.read();
|
||||
if (firstByte == -1) {
|
||||
return false;
|
||||
}
|
||||
final int size = CodedInputStream.readRawVarint32(firstByte, input);
|
||||
final InputStream limitedInput = new LimitedInputStream(input, size);
|
||||
mergeFrom(limitedInput);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean mergeDelimitedFrom(
|
||||
InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry) throws IOException {
|
||||
// UnknownFieldSet has no extensions.
|
||||
return mergeDelimitedFrom(input);
|
||||
}
|
||||
|
||||
public Builder mergeFrom(
|
||||
CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry) throws IOException {
|
||||
// UnknownFieldSet has no extensions.
|
||||
return mergeFrom(input);
|
||||
}
|
||||
|
||||
public Builder mergeFrom(
|
||||
ByteString data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
// UnknownFieldSet has no extensions.
|
||||
return mergeFrom(data);
|
||||
}
|
||||
|
||||
public Builder mergeFrom(byte[] data, int off, int len)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
final CodedInputStream input =
|
||||
CodedInputStream.newInstance(data, off, len);
|
||||
mergeFrom(input);
|
||||
input.checkLastTagWas(0);
|
||||
return this;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a byte array threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Builder mergeFrom(
|
||||
byte[] data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
// UnknownFieldSet has no extensions.
|
||||
return mergeFrom(data);
|
||||
}
|
||||
|
||||
public Builder mergeFrom(
|
||||
byte[] data, int off, int len,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
// UnknownFieldSet has no extensions.
|
||||
return mergeFrom(data, off, len);
|
||||
}
|
||||
|
||||
public Builder mergeFrom(
|
||||
InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry) throws IOException {
|
||||
// UnknownFieldSet has no extensions.
|
||||
return mergeFrom(input);
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
// UnknownFieldSets do not have required fields, so they are always
|
||||
// initialized.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a single field in an {@code UnknownFieldSet}.
|
||||
*
|
||||
* <p>A {@code Field} consists of five lists of values. The lists correspond
|
||||
* to the five "wire types" used in the protocol buffer binary format.
|
||||
* The wire type of each field can be determined from the encoded form alone,
|
||||
* without knowing the field's declared type. So, we are able to parse
|
||||
* unknown values at least this far and separate them. Normally, only one
|
||||
* of the five lists will contain any values, since it is impossible to
|
||||
* define a valid message type that declares two different types for the
|
||||
* same field number. However, the code is designed to allow for the case
|
||||
* where the same unknown field number is encountered using multiple different
|
||||
* wire types.
|
||||
*
|
||||
* <p>{@code Field} is an immutable class. To construct one, you must use a
|
||||
* {@link Builder}.
|
||||
*
|
||||
* @see UnknownFieldSet
|
||||
*/
|
||||
public static final class Field {
|
||||
private Field() {}
|
||||
|
||||
/** Construct a new {@link Builder}. */
|
||||
public static Builder newBuilder() {
|
||||
return Builder.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@link Builder} and initialize it to a copy of
|
||||
* {@code copyFrom}.
|
||||
*/
|
||||
public static Builder newBuilder(final Field copyFrom) {
|
||||
return newBuilder().mergeFrom(copyFrom);
|
||||
}
|
||||
|
||||
/** Get an empty {@code Field}. */
|
||||
public static Field getDefaultInstance() {
|
||||
return fieldDefaultInstance;
|
||||
}
|
||||
private static final Field fieldDefaultInstance = newBuilder().build();
|
||||
|
||||
/** Get the list of varint values for this field. */
|
||||
public List<Long> getVarintList() { return varint; }
|
||||
|
||||
/** Get the list of fixed32 values for this field. */
|
||||
public List<Integer> getFixed32List() { return fixed32; }
|
||||
|
||||
/** Get the list of fixed64 values for this field. */
|
||||
public List<Long> getFixed64List() { return fixed64; }
|
||||
|
||||
/** Get the list of length-delimited values for this field. */
|
||||
public List<ByteString> getLengthDelimitedList() { return lengthDelimited; }
|
||||
|
||||
/**
|
||||
* Get the list of embedded group values for this field. These are
|
||||
* represented using {@link UnknownFieldSet}s rather than {@link Message}s
|
||||
* since the group's type is presumably unknown.
|
||||
*/
|
||||
public List<UnknownFieldSet> getGroupList() { return group; }
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof Field)) {
|
||||
return false;
|
||||
}
|
||||
return Arrays.equals(getIdentityArray(),
|
||||
((Field) other).getIdentityArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(getIdentityArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of objects to be used to uniquely identify this
|
||||
* {@link Field} instance.
|
||||
*/
|
||||
private Object[] getIdentityArray() {
|
||||
return new Object[] {
|
||||
varint,
|
||||
fixed32,
|
||||
fixed64,
|
||||
lengthDelimited,
|
||||
group};
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the field, including field number, and writes it to
|
||||
* {@code output}.
|
||||
*/
|
||||
public void writeTo(final int fieldNumber, final CodedOutputStream output)
|
||||
throws IOException {
|
||||
for (final long value : varint) {
|
||||
output.writeUInt64(fieldNumber, value);
|
||||
}
|
||||
for (final int value : fixed32) {
|
||||
output.writeFixed32(fieldNumber, value);
|
||||
}
|
||||
for (final long value : fixed64) {
|
||||
output.writeFixed64(fieldNumber, value);
|
||||
}
|
||||
for (final ByteString value : lengthDelimited) {
|
||||
output.writeBytes(fieldNumber, value);
|
||||
}
|
||||
for (final UnknownFieldSet value : group) {
|
||||
output.writeGroup(fieldNumber, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of bytes required to encode this field, including field
|
||||
* number.
|
||||
*/
|
||||
public int getSerializedSize(final int fieldNumber) {
|
||||
int result = 0;
|
||||
for (final long value : varint) {
|
||||
result += CodedOutputStream.computeUInt64Size(fieldNumber, value);
|
||||
}
|
||||
for (final int value : fixed32) {
|
||||
result += CodedOutputStream.computeFixed32Size(fieldNumber, value);
|
||||
}
|
||||
for (final long value : fixed64) {
|
||||
result += CodedOutputStream.computeFixed64Size(fieldNumber, value);
|
||||
}
|
||||
for (final ByteString value : lengthDelimited) {
|
||||
result += CodedOutputStream.computeBytesSize(fieldNumber, value);
|
||||
}
|
||||
for (final UnknownFieldSet value : group) {
|
||||
result += CodedOutputStream.computeGroupSize(fieldNumber, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the field, including field number, and writes it to
|
||||
* {@code output}, using {@code MessageSet} wire format.
|
||||
*/
|
||||
public void writeAsMessageSetExtensionTo(
|
||||
final int fieldNumber,
|
||||
final CodedOutputStream output)
|
||||
throws IOException {
|
||||
for (final ByteString value : lengthDelimited) {
|
||||
output.writeRawMessageSetExtension(fieldNumber, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of bytes required to encode this field, including field
|
||||
* number, using {@code MessageSet} wire format.
|
||||
*/
|
||||
public int getSerializedSizeAsMessageSetExtension(final int fieldNumber) {
|
||||
int result = 0;
|
||||
for (final ByteString value : lengthDelimited) {
|
||||
result += CodedOutputStream.computeRawMessageSetExtensionSize(
|
||||
fieldNumber, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Long> varint;
|
||||
private List<Integer> fixed32;
|
||||
private List<Long> fixed64;
|
||||
private List<ByteString> lengthDelimited;
|
||||
private List<UnknownFieldSet> group;
|
||||
|
||||
/**
|
||||
* Used to build a {@link Field} within an {@link UnknownFieldSet}.
|
||||
*
|
||||
* <p>Use {@link Field#newBuilder()} to construct a {@code Builder}.
|
||||
*/
|
||||
public static final class Builder {
|
||||
// This constructor should never be called directly (except from 'create').
|
||||
private Builder() {}
|
||||
|
||||
private static Builder create() {
|
||||
Builder builder = new Builder();
|
||||
builder.result = new Field();
|
||||
return builder;
|
||||
}
|
||||
|
||||
private Field result;
|
||||
|
||||
/**
|
||||
* Build the field. After {@code build()} has been called, the
|
||||
* {@code Builder} is no longer usable. Calling any other method will
|
||||
* result in undefined behavior and can cause a
|
||||
* {@code NullPointerException} to be thrown.
|
||||
*/
|
||||
public Field build() {
|
||||
if (result.varint == null) {
|
||||
result.varint = Collections.emptyList();
|
||||
} else {
|
||||
result.varint = Collections.unmodifiableList(result.varint);
|
||||
}
|
||||
if (result.fixed32 == null) {
|
||||
result.fixed32 = Collections.emptyList();
|
||||
} else {
|
||||
result.fixed32 = Collections.unmodifiableList(result.fixed32);
|
||||
}
|
||||
if (result.fixed64 == null) {
|
||||
result.fixed64 = Collections.emptyList();
|
||||
} else {
|
||||
result.fixed64 = Collections.unmodifiableList(result.fixed64);
|
||||
}
|
||||
if (result.lengthDelimited == null) {
|
||||
result.lengthDelimited = Collections.emptyList();
|
||||
} else {
|
||||
result.lengthDelimited =
|
||||
Collections.unmodifiableList(result.lengthDelimited);
|
||||
}
|
||||
if (result.group == null) {
|
||||
result.group = Collections.emptyList();
|
||||
} else {
|
||||
result.group = Collections.unmodifiableList(result.group);
|
||||
}
|
||||
|
||||
final Field returnMe = result;
|
||||
result = null;
|
||||
return returnMe;
|
||||
}
|
||||
|
||||
/** Discard the field's contents. */
|
||||
public Builder clear() {
|
||||
result = new Field();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the values in {@code other} into this field. For each list
|
||||
* of values, {@code other}'s values are append to the ones in this
|
||||
* field.
|
||||
*/
|
||||
public Builder mergeFrom(final Field other) {
|
||||
if (!other.varint.isEmpty()) {
|
||||
if (result.varint == null) {
|
||||
result.varint = new ArrayList<Long>();
|
||||
}
|
||||
result.varint.addAll(other.varint);
|
||||
}
|
||||
if (!other.fixed32.isEmpty()) {
|
||||
if (result.fixed32 == null) {
|
||||
result.fixed32 = new ArrayList<Integer>();
|
||||
}
|
||||
result.fixed32.addAll(other.fixed32);
|
||||
}
|
||||
if (!other.fixed64.isEmpty()) {
|
||||
if (result.fixed64 == null) {
|
||||
result.fixed64 = new ArrayList<Long>();
|
||||
}
|
||||
result.fixed64.addAll(other.fixed64);
|
||||
}
|
||||
if (!other.lengthDelimited.isEmpty()) {
|
||||
if (result.lengthDelimited == null) {
|
||||
result.lengthDelimited = new ArrayList<ByteString>();
|
||||
}
|
||||
result.lengthDelimited.addAll(other.lengthDelimited);
|
||||
}
|
||||
if (!other.group.isEmpty()) {
|
||||
if (result.group == null) {
|
||||
result.group = new ArrayList<UnknownFieldSet>();
|
||||
}
|
||||
result.group.addAll(other.group);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Add a varint value. */
|
||||
public Builder addVarint(final long value) {
|
||||
if (result.varint == null) {
|
||||
result.varint = new ArrayList<Long>();
|
||||
}
|
||||
result.varint.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Add a fixed32 value. */
|
||||
public Builder addFixed32(final int value) {
|
||||
if (result.fixed32 == null) {
|
||||
result.fixed32 = new ArrayList<Integer>();
|
||||
}
|
||||
result.fixed32.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Add a fixed64 value. */
|
||||
public Builder addFixed64(final long value) {
|
||||
if (result.fixed64 == null) {
|
||||
result.fixed64 = new ArrayList<Long>();
|
||||
}
|
||||
result.fixed64.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Add a length-delimited value. */
|
||||
public Builder addLengthDelimited(final ByteString value) {
|
||||
if (result.lengthDelimited == null) {
|
||||
result.lengthDelimited = new ArrayList<ByteString>();
|
||||
}
|
||||
result.lengthDelimited.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Add an embedded group. */
|
||||
public Builder addGroup(final UnknownFieldSet value) {
|
||||
if (result.group == null) {
|
||||
result.group = new ArrayList<UnknownFieldSet>();
|
||||
}
|
||||
result.group.add(value);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parser to implement MessageLite interface.
|
||||
*/
|
||||
public static final class Parser extends AbstractParser<UnknownFieldSet> {
|
||||
public UnknownFieldSet parsePartialFrom(
|
||||
CodedInputStream input, ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
Builder builder = newBuilder();
|
||||
try {
|
||||
builder.mergeFrom(input);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e.setUnfinishedMessage(builder.buildPartial());
|
||||
} catch (IOException e) {
|
||||
throw new InvalidProtocolBufferException(e.getMessage())
|
||||
.setUnfinishedMessage(builder.buildPartial());
|
||||
}
|
||||
return builder.buildPartial();
|
||||
}
|
||||
}
|
||||
|
||||
private static final Parser PARSER = new Parser();
|
||||
public final Parser getParserForType() {
|
||||
return PARSER;
|
||||
}
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.RandomAccess;
|
||||
import java.util.List;
|
||||
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 (Java 1.6 override semantics, but we must support 1.5)
|
||||
public ByteString getByteString(int index) {
|
||||
return list.getByteString(index);
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void add(ByteString element) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public ListIterator<String> listIterator(final int index) {
|
||||
return new ListIterator<String>() {
|
||||
ListIterator<String> iter = list.listIterator(index);
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public boolean hasNext() {
|
||||
return iter.hasNext();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public String next() {
|
||||
return iter.next();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public boolean hasPrevious() {
|
||||
return iter.hasPrevious();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public String previous() {
|
||||
return iter.previous();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public int nextIndex() {
|
||||
return iter.nextIndex();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public int previousIndex() {
|
||||
return iter.previousIndex();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void set(String o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void add(String o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> iterator() {
|
||||
return new Iterator<String>() {
|
||||
Iterator<String> iter = list.iterator();
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public boolean hasNext() {
|
||||
return iter.hasNext();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public String next() {
|
||||
return iter.next();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public List<?> getUnderlyingElements() {
|
||||
// The returned value is already unmodifiable.
|
||||
return list.getUnderlyingElements();
|
||||
}
|
||||
}
|
||||
@@ -1,349 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
/**
|
||||
* A set of low-level, high-performance static utility methods related
|
||||
* to the UTF-8 character encoding. This class has no dependencies
|
||||
* outside of the core JDK libraries.
|
||||
*
|
||||
* <p>There are several variants of UTF-8. The one implemented by
|
||||
* this class is the restricted definition of UTF-8 introduced in
|
||||
* Unicode 3.1, which mandates the rejection of "overlong" byte
|
||||
* sequences as well as rejection of 3-byte surrogate codepoint byte
|
||||
* sequences. Note that the UTF-8 decoder included in Oracle's JDK
|
||||
* has been modified to also reject "overlong" byte sequences, but (as
|
||||
* of 2011) still accepts 3-byte surrogate codepoint byte sequences.
|
||||
*
|
||||
* <p>The byte sequences considered valid by this class are exactly
|
||||
* those that can be roundtrip converted to Strings and back to bytes
|
||||
* using the UTF-8 charset, without loss: <pre> {@code
|
||||
* Arrays.equals(bytes, new String(bytes, "UTF-8").getBytes("UTF-8"))
|
||||
* }</pre>
|
||||
*
|
||||
* <p>See the Unicode Standard,</br>
|
||||
* Table 3-6. <em>UTF-8 Bit Distribution</em>,</br>
|
||||
* Table 3-7. <em>Well Formed UTF-8 Byte Sequences</em>.
|
||||
*
|
||||
* <p>This class supports decoding of partial byte sequences, so that the
|
||||
* bytes in a complete UTF-8 byte sequences can be stored in multiple
|
||||
* segments. Methods typically return {@link #MALFORMED} if the partial
|
||||
* byte sequence is definitely not well-formed, {@link #COMPLETE} if it is
|
||||
* well-formed in the absence of additional input, or if the byte sequence
|
||||
* apparently terminated in the middle of a character, an opaque integer
|
||||
* "state" value containing enough information to decode the character when
|
||||
* passed to a subsequent invocation of a partial decoding method.
|
||||
*
|
||||
* @author martinrb@google.com (Martin Buchholz)
|
||||
*/
|
||||
final class Utf8 {
|
||||
private Utf8() {}
|
||||
|
||||
/**
|
||||
* State value indicating that the byte sequence is well-formed and
|
||||
* complete (no further bytes are needed to complete a character).
|
||||
*/
|
||||
public static final int COMPLETE = 0;
|
||||
|
||||
/**
|
||||
* State value indicating that the byte sequence is definitely not
|
||||
* well-formed.
|
||||
*/
|
||||
public static final int MALFORMED = -1;
|
||||
|
||||
// Other state values include the partial bytes of the incomplete
|
||||
// character to be decoded in the simplest way: we pack the bytes
|
||||
// into the state int in little-endian order. For example:
|
||||
//
|
||||
// int state = byte1 ^ (byte2 << 8) ^ (byte3 << 16);
|
||||
//
|
||||
// Such a state is unpacked thus (note the ~ operation for byte2 to
|
||||
// undo byte1's sign-extension bits):
|
||||
//
|
||||
// int byte1 = (byte) state;
|
||||
// int byte2 = (byte) ~(state >> 8);
|
||||
// int byte3 = (byte) (state >> 16);
|
||||
//
|
||||
// We cannot store a zero byte in the state because it would be
|
||||
// indistinguishable from the absence of a byte. But we don't need
|
||||
// to, because partial bytes must always be negative. When building
|
||||
// a state, we ensure that byte1 is negative and subsequent bytes
|
||||
// are valid trailing bytes.
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given byte array is a well-formed
|
||||
* UTF-8 byte sequence.
|
||||
*
|
||||
* <p>This is a convenience method, equivalent to a call to {@code
|
||||
* isValidUtf8(bytes, 0, bytes.length)}.
|
||||
*/
|
||||
public static boolean isValidUtf8(byte[] bytes) {
|
||||
return isValidUtf8(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given byte array slice is a
|
||||
* well-formed UTF-8 byte sequence. The range of bytes to be
|
||||
* checked extends from index {@code index}, inclusive, to {@code
|
||||
* limit}, exclusive.
|
||||
*
|
||||
* <p>This is a convenience method, equivalent to {@code
|
||||
* partialIsValidUtf8(bytes, index, limit) == Utf8.COMPLETE}.
|
||||
*/
|
||||
public static boolean isValidUtf8(byte[] bytes, int index, int limit) {
|
||||
return partialIsValidUtf8(bytes, index, limit) == COMPLETE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether the given byte array slice is a well-formed,
|
||||
* malformed, or incomplete UTF-8 byte sequence. The range of bytes
|
||||
* to be checked extends from index {@code index}, inclusive, to
|
||||
* {@code limit}, exclusive.
|
||||
*
|
||||
* @param state either {@link Utf8#COMPLETE} (if this is the initial decoding
|
||||
* operation) or the value returned from a call to a partial decoding method
|
||||
* for the previous bytes
|
||||
*
|
||||
* @return {@link #MALFORMED} if the partial byte sequence is
|
||||
* definitely not well-formed, {@link #COMPLETE} if it is well-formed
|
||||
* (no additional input needed), or if the byte sequence is
|
||||
* "incomplete", i.e. apparently terminated in the middle of a character,
|
||||
* an opaque integer "state" value containing enough information to
|
||||
* decode the character when passed to a subsequent invocation of a
|
||||
* partial decoding method.
|
||||
*/
|
||||
public static int partialIsValidUtf8(
|
||||
int state, byte[] bytes, int index, int limit) {
|
||||
if (state != COMPLETE) {
|
||||
// The previous decoding operation was incomplete (or malformed).
|
||||
// We look for a well-formed sequence consisting of bytes from
|
||||
// the previous decoding operation (stored in state) together
|
||||
// with bytes from the array slice.
|
||||
//
|
||||
// We expect such "straddler characters" to be rare.
|
||||
|
||||
if (index >= limit) { // No bytes? No progress.
|
||||
return state;
|
||||
}
|
||||
int byte1 = (byte) state;
|
||||
// byte1 is never ASCII.
|
||||
if (byte1 < (byte) 0xE0) {
|
||||
// two-byte form
|
||||
|
||||
// Simultaneously checks for illegal trailing-byte in
|
||||
// leading position and overlong 2-byte form.
|
||||
if (byte1 < (byte) 0xC2 ||
|
||||
// byte2 trailing-byte test
|
||||
bytes[index++] > (byte) 0xBF) {
|
||||
return MALFORMED;
|
||||
}
|
||||
} else if (byte1 < (byte) 0xF0) {
|
||||
// three-byte form
|
||||
|
||||
// Get byte2 from saved state or array
|
||||
int byte2 = (byte) ~(state >> 8);
|
||||
if (byte2 == 0) {
|
||||
byte2 = bytes[index++];
|
||||
if (index >= limit) {
|
||||
return incompleteStateFor(byte1, byte2);
|
||||
}
|
||||
}
|
||||
if (byte2 > (byte) 0xBF ||
|
||||
// overlong? 5 most significant bits must not all be zero
|
||||
(byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) ||
|
||||
// illegal surrogate codepoint?
|
||||
(byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) ||
|
||||
// byte3 trailing-byte test
|
||||
bytes[index++] > (byte) 0xBF) {
|
||||
return MALFORMED;
|
||||
}
|
||||
} else {
|
||||
// four-byte form
|
||||
|
||||
// Get byte2 and byte3 from saved state or array
|
||||
int byte2 = (byte) ~(state >> 8);
|
||||
int byte3 = 0;
|
||||
if (byte2 == 0) {
|
||||
byte2 = bytes[index++];
|
||||
if (index >= limit) {
|
||||
return incompleteStateFor(byte1, byte2);
|
||||
}
|
||||
} else {
|
||||
byte3 = (byte) (state >> 16);
|
||||
}
|
||||
if (byte3 == 0) {
|
||||
byte3 = bytes[index++];
|
||||
if (index >= limit) {
|
||||
return incompleteStateFor(byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
|
||||
// If we were called with state == MALFORMED, then byte1 is 0xFF,
|
||||
// which never occurs in well-formed UTF-8, and so we will return
|
||||
// MALFORMED again below.
|
||||
|
||||
if (byte2 > (byte) 0xBF ||
|
||||
// Check that 1 <= plane <= 16. Tricky optimized form of:
|
||||
// if (byte1 > (byte) 0xF4 ||
|
||||
// byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
|
||||
// byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
|
||||
(((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 ||
|
||||
// byte3 trailing-byte test
|
||||
byte3 > (byte) 0xBF ||
|
||||
// byte4 trailing-byte test
|
||||
bytes[index++] > (byte) 0xBF) {
|
||||
return MALFORMED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return partialIsValidUtf8(bytes, index, limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether the given byte array slice is a well-formed,
|
||||
* malformed, or incomplete UTF-8 byte sequence. The range of bytes
|
||||
* to be checked extends from index {@code index}, inclusive, to
|
||||
* {@code limit}, exclusive.
|
||||
*
|
||||
* <p>This is a convenience method, equivalent to a call to {@code
|
||||
* partialIsValidUtf8(Utf8.COMPLETE, bytes, index, limit)}.
|
||||
*
|
||||
* @return {@link #MALFORMED} if the partial byte sequence is
|
||||
* definitely not well-formed, {@link #COMPLETE} if it is well-formed
|
||||
* (no additional input needed), or if the byte sequence is
|
||||
* "incomplete", i.e. apparently terminated in the middle of a character,
|
||||
* an opaque integer "state" value containing enough information to
|
||||
* decode the character when passed to a subsequent invocation of a
|
||||
* partial decoding method.
|
||||
*/
|
||||
public static int partialIsValidUtf8(
|
||||
byte[] bytes, int index, int limit) {
|
||||
// Optimize for 100% ASCII.
|
||||
// Hotspot loves small simple top-level loops like this.
|
||||
while (index < limit && bytes[index] >= 0) {
|
||||
index++;
|
||||
}
|
||||
|
||||
return (index >= limit) ? COMPLETE :
|
||||
partialIsValidUtf8NonAscii(bytes, index, limit);
|
||||
}
|
||||
|
||||
private static int partialIsValidUtf8NonAscii(
|
||||
byte[] bytes, int index, int limit) {
|
||||
for (;;) {
|
||||
int byte1, byte2;
|
||||
|
||||
// Optimize for interior runs of ASCII bytes.
|
||||
do {
|
||||
if (index >= limit) {
|
||||
return COMPLETE;
|
||||
}
|
||||
} while ((byte1 = bytes[index++]) >= 0);
|
||||
|
||||
if (byte1 < (byte) 0xE0) {
|
||||
// two-byte form
|
||||
|
||||
if (index >= limit) {
|
||||
return byte1;
|
||||
}
|
||||
|
||||
// Simultaneously checks for illegal trailing-byte in
|
||||
// leading position and overlong 2-byte form.
|
||||
if (byte1 < (byte) 0xC2 ||
|
||||
bytes[index++] > (byte) 0xBF) {
|
||||
return MALFORMED;
|
||||
}
|
||||
} else if (byte1 < (byte) 0xF0) {
|
||||
// three-byte form
|
||||
|
||||
if (index >= limit - 1) { // incomplete sequence
|
||||
return incompleteStateFor(bytes, index, limit);
|
||||
}
|
||||
if ((byte2 = bytes[index++]) > (byte) 0xBF ||
|
||||
// overlong? 5 most significant bits must not all be zero
|
||||
(byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) ||
|
||||
// check for illegal surrogate codepoints
|
||||
(byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) ||
|
||||
// byte3 trailing-byte test
|
||||
bytes[index++] > (byte) 0xBF) {
|
||||
return MALFORMED;
|
||||
}
|
||||
} else {
|
||||
// four-byte form
|
||||
|
||||
if (index >= limit - 2) { // incomplete sequence
|
||||
return incompleteStateFor(bytes, index, limit);
|
||||
}
|
||||
if ((byte2 = bytes[index++]) > (byte) 0xBF ||
|
||||
// Check that 1 <= plane <= 16. Tricky optimized form of:
|
||||
// if (byte1 > (byte) 0xF4 ||
|
||||
// byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
|
||||
// byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
|
||||
(((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 ||
|
||||
// byte3 trailing-byte test
|
||||
bytes[index++] > (byte) 0xBF ||
|
||||
// byte4 trailing-byte test
|
||||
bytes[index++] > (byte) 0xBF) {
|
||||
return MALFORMED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int incompleteStateFor(int byte1) {
|
||||
return (byte1 > (byte) 0xF4) ?
|
||||
MALFORMED : byte1;
|
||||
}
|
||||
|
||||
private static int incompleteStateFor(int byte1, int byte2) {
|
||||
return (byte1 > (byte) 0xF4 ||
|
||||
byte2 > (byte) 0xBF) ?
|
||||
MALFORMED : byte1 ^ (byte2 << 8);
|
||||
}
|
||||
|
||||
private static int incompleteStateFor(int byte1, int byte2, int byte3) {
|
||||
return (byte1 > (byte) 0xF4 ||
|
||||
byte2 > (byte) 0xBF ||
|
||||
byte3 > (byte) 0xBF) ?
|
||||
MALFORMED : byte1 ^ (byte2 << 8) ^ (byte3 << 16);
|
||||
}
|
||||
|
||||
private static int incompleteStateFor(byte[] bytes, int index, int limit) {
|
||||
int byte1 = bytes[index - 1];
|
||||
switch (limit - index) {
|
||||
case 0: return incompleteStateFor(byte1);
|
||||
case 1: return incompleteStateFor(byte1, bytes[index]);
|
||||
case 2: return incompleteStateFor(byte1, bytes[index], bytes[index + 1]);
|
||||
default: throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
/**
|
||||
* This class is used internally by the Protocol Buffer library and generated
|
||||
* message implementations. It is public only because those generated messages
|
||||
* do not reside in the {@code protobuf} package. Others should not use this
|
||||
* class directly.
|
||||
*
|
||||
* This class contains constants and helper functions useful for dealing with
|
||||
* the Protocol Buffer wire format.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public final class WireFormat {
|
||||
// Do not allow instantiation.
|
||||
private WireFormat() {}
|
||||
|
||||
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;
|
||||
|
||||
/** Given a tag value, determines the wire type (the lower 3 bits). */
|
||||
static int getTagWireType(final int tag) {
|
||||
return tag & TAG_TYPE_MASK;
|
||||
}
|
||||
|
||||
/** Given a tag value, determines the field number (the upper 29 bits). */
|
||||
public static int getTagFieldNumber(final int tag) {
|
||||
return tag >>> TAG_TYPE_BITS;
|
||||
}
|
||||
|
||||
/** Makes a tag value given a field number and wire type. */
|
||||
static int makeTag(final int fieldNumber, final int wireType) {
|
||||
return (fieldNumber << TAG_TYPE_BITS) | wireType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lite equivalent to {@link Descriptors.FieldDescriptor.JavaType}. This is
|
||||
* only here to support the lite runtime and should not be used by users.
|
||||
*/
|
||||
public enum JavaType {
|
||||
INT(0),
|
||||
LONG(0L),
|
||||
FLOAT(0F),
|
||||
DOUBLE(0D),
|
||||
BOOLEAN(false),
|
||||
STRING(""),
|
||||
BYTE_STRING(ByteString.EMPTY),
|
||||
ENUM(null),
|
||||
MESSAGE(null);
|
||||
|
||||
JavaType(final Object defaultDefault) {
|
||||
this.defaultDefault = defaultDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default default value for fields of this type, if it's a primitive
|
||||
* type.
|
||||
*/
|
||||
Object getDefaultDefault() {
|
||||
return defaultDefault;
|
||||
}
|
||||
|
||||
private final Object defaultDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lite equivalent to {@link Descriptors.FieldDescriptor.Type}. This is
|
||||
* only here to support the lite runtime and should not be used by users.
|
||||
*/
|
||||
public enum FieldType {
|
||||
DOUBLE (JavaType.DOUBLE , WIRETYPE_FIXED64 ),
|
||||
FLOAT (JavaType.FLOAT , WIRETYPE_FIXED32 ),
|
||||
INT64 (JavaType.LONG , WIRETYPE_VARINT ),
|
||||
UINT64 (JavaType.LONG , WIRETYPE_VARINT ),
|
||||
INT32 (JavaType.INT , WIRETYPE_VARINT ),
|
||||
FIXED64 (JavaType.LONG , WIRETYPE_FIXED64 ),
|
||||
FIXED32 (JavaType.INT , WIRETYPE_FIXED32 ),
|
||||
BOOL (JavaType.BOOLEAN , WIRETYPE_VARINT ),
|
||||
STRING (JavaType.STRING , WIRETYPE_LENGTH_DELIMITED) {
|
||||
public boolean isPackable() { return false; }
|
||||
},
|
||||
GROUP (JavaType.MESSAGE , WIRETYPE_START_GROUP ) {
|
||||
public boolean isPackable() { return false; }
|
||||
},
|
||||
MESSAGE (JavaType.MESSAGE , WIRETYPE_LENGTH_DELIMITED) {
|
||||
public boolean isPackable() { return false; }
|
||||
},
|
||||
BYTES (JavaType.BYTE_STRING, WIRETYPE_LENGTH_DELIMITED) {
|
||||
public boolean isPackable() { return false; }
|
||||
},
|
||||
UINT32 (JavaType.INT , WIRETYPE_VARINT ),
|
||||
ENUM (JavaType.ENUM , WIRETYPE_VARINT ),
|
||||
SFIXED32(JavaType.INT , WIRETYPE_FIXED32 ),
|
||||
SFIXED64(JavaType.LONG , WIRETYPE_FIXED64 ),
|
||||
SINT32 (JavaType.INT , WIRETYPE_VARINT ),
|
||||
SINT64 (JavaType.LONG , WIRETYPE_VARINT );
|
||||
|
||||
FieldType(final JavaType javaType, final int wireType) {
|
||||
this.javaType = javaType;
|
||||
this.wireType = wireType;
|
||||
}
|
||||
|
||||
private final JavaType javaType;
|
||||
private final int wireType;
|
||||
|
||||
public JavaType getJavaType() { return javaType; }
|
||||
public int getWireType() { return wireType; }
|
||||
|
||||
public boolean isPackable() { return true; }
|
||||
}
|
||||
|
||||
// Field numbers for fields in MessageSet wire format.
|
||||
static final int MESSAGE_SET_ITEM = 1;
|
||||
static final int MESSAGE_SET_TYPE_ID = 2;
|
||||
static final int MESSAGE_SET_MESSAGE = 3;
|
||||
|
||||
// Tag numbers.
|
||||
static final int MESSAGE_SET_ITEM_TAG =
|
||||
makeTag(MESSAGE_SET_ITEM, WIRETYPE_START_GROUP);
|
||||
static final int MESSAGE_SET_ITEM_END_TAG =
|
||||
makeTag(MESSAGE_SET_ITEM, WIRETYPE_END_GROUP);
|
||||
static final int MESSAGE_SET_TYPE_ID_TAG =
|
||||
makeTag(MESSAGE_SET_TYPE_ID, WIRETYPE_VARINT);
|
||||
static final int MESSAGE_SET_MESSAGE_TAG =
|
||||
makeTag(MESSAGE_SET_MESSAGE, WIRETYPE_LENGTH_DELIMITED);
|
||||
}
|
||||
@@ -1,509 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.Descriptors.FieldDescriptor;
|
||||
import protobuf_unittest.UnittestOptimizeFor.TestOptimizedForSize;
|
||||
import protobuf_unittest.UnittestProto;
|
||||
import protobuf_unittest.UnittestProto.ForeignMessage;
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestPackedTypes;
|
||||
import protobuf_unittest.UnittestProto.TestRequired;
|
||||
import protobuf_unittest.UnittestProto.TestRequiredForeign;
|
||||
import protobuf_unittest.UnittestProto.TestUnpackedTypes;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Unit test for {@link AbstractMessage}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class AbstractMessageTest extends TestCase {
|
||||
/**
|
||||
* Extends AbstractMessage and wraps some other message object. The methods
|
||||
* of the Message interface which aren't explicitly implemented by
|
||||
* AbstractMessage are forwarded to the wrapped object. This allows us to
|
||||
* test that AbstractMessage's implementations work even if the wrapped
|
||||
* object does not use them.
|
||||
*/
|
||||
private static class AbstractMessageWrapper extends AbstractMessage {
|
||||
private final Message wrappedMessage;
|
||||
|
||||
public AbstractMessageWrapper(Message wrappedMessage) {
|
||||
this.wrappedMessage = wrappedMessage;
|
||||
}
|
||||
|
||||
public Descriptors.Descriptor getDescriptorForType() {
|
||||
return wrappedMessage.getDescriptorForType();
|
||||
}
|
||||
public AbstractMessageWrapper getDefaultInstanceForType() {
|
||||
return new AbstractMessageWrapper(
|
||||
wrappedMessage.getDefaultInstanceForType());
|
||||
}
|
||||
public Map<Descriptors.FieldDescriptor, Object> getAllFields() {
|
||||
return wrappedMessage.getAllFields();
|
||||
}
|
||||
public boolean hasField(Descriptors.FieldDescriptor field) {
|
||||
return wrappedMessage.hasField(field);
|
||||
}
|
||||
public Object getField(Descriptors.FieldDescriptor field) {
|
||||
return wrappedMessage.getField(field);
|
||||
}
|
||||
public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) {
|
||||
return wrappedMessage.getRepeatedFieldCount(field);
|
||||
}
|
||||
public Object getRepeatedField(
|
||||
Descriptors.FieldDescriptor field, int index) {
|
||||
return wrappedMessage.getRepeatedField(field, index);
|
||||
}
|
||||
public UnknownFieldSet getUnknownFields() {
|
||||
return wrappedMessage.getUnknownFields();
|
||||
}
|
||||
public Builder newBuilderForType() {
|
||||
return new Builder(wrappedMessage.newBuilderForType());
|
||||
}
|
||||
public Builder toBuilder() {
|
||||
return new Builder(wrappedMessage.toBuilder());
|
||||
}
|
||||
|
||||
static class Builder extends AbstractMessage.Builder<Builder> {
|
||||
private final Message.Builder wrappedBuilder;
|
||||
|
||||
public Builder(Message.Builder wrappedBuilder) {
|
||||
this.wrappedBuilder = wrappedBuilder;
|
||||
}
|
||||
|
||||
public AbstractMessageWrapper build() {
|
||||
return new AbstractMessageWrapper(wrappedBuilder.build());
|
||||
}
|
||||
public AbstractMessageWrapper buildPartial() {
|
||||
return new AbstractMessageWrapper(wrappedBuilder.buildPartial());
|
||||
}
|
||||
public Builder clone() {
|
||||
return new Builder(wrappedBuilder.clone());
|
||||
}
|
||||
public boolean isInitialized() {
|
||||
return clone().buildPartial().isInitialized();
|
||||
}
|
||||
public Descriptors.Descriptor getDescriptorForType() {
|
||||
return wrappedBuilder.getDescriptorForType();
|
||||
}
|
||||
public AbstractMessageWrapper getDefaultInstanceForType() {
|
||||
return new AbstractMessageWrapper(
|
||||
wrappedBuilder.getDefaultInstanceForType());
|
||||
}
|
||||
public Map<Descriptors.FieldDescriptor, Object> getAllFields() {
|
||||
return wrappedBuilder.getAllFields();
|
||||
}
|
||||
public Builder newBuilderForField(Descriptors.FieldDescriptor field) {
|
||||
return new Builder(wrappedBuilder.newBuilderForField(field));
|
||||
}
|
||||
public boolean hasField(Descriptors.FieldDescriptor field) {
|
||||
return wrappedBuilder.hasField(field);
|
||||
}
|
||||
public Object getField(Descriptors.FieldDescriptor field) {
|
||||
return wrappedBuilder.getField(field);
|
||||
}
|
||||
public Builder setField(Descriptors.FieldDescriptor field, Object value) {
|
||||
wrappedBuilder.setField(field, value);
|
||||
return this;
|
||||
}
|
||||
public Builder clearField(Descriptors.FieldDescriptor field) {
|
||||
wrappedBuilder.clearField(field);
|
||||
return this;
|
||||
}
|
||||
public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) {
|
||||
return wrappedBuilder.getRepeatedFieldCount(field);
|
||||
}
|
||||
public Object getRepeatedField(
|
||||
Descriptors.FieldDescriptor field, int index) {
|
||||
return wrappedBuilder.getRepeatedField(field, index);
|
||||
}
|
||||
public Builder setRepeatedField(Descriptors.FieldDescriptor field,
|
||||
int index, Object value) {
|
||||
wrappedBuilder.setRepeatedField(field, index, value);
|
||||
return this;
|
||||
}
|
||||
public Builder addRepeatedField(
|
||||
Descriptors.FieldDescriptor field, Object value) {
|
||||
wrappedBuilder.addRepeatedField(field, value);
|
||||
return this;
|
||||
}
|
||||
public UnknownFieldSet getUnknownFields() {
|
||||
return wrappedBuilder.getUnknownFields();
|
||||
}
|
||||
public Builder setUnknownFields(UnknownFieldSet unknownFields) {
|
||||
wrappedBuilder.setUnknownFields(unknownFields);
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public Message.Builder getFieldBuilder(FieldDescriptor field) {
|
||||
return wrappedBuilder.getFieldBuilder(field);
|
||||
}
|
||||
}
|
||||
public Parser<? extends Message> getParserForType() {
|
||||
return wrappedMessage.getParserForType();
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
TestUtil.ReflectionTester reflectionTester =
|
||||
new TestUtil.ReflectionTester(TestAllTypes.getDescriptor(), null);
|
||||
|
||||
TestUtil.ReflectionTester extensionsReflectionTester =
|
||||
new TestUtil.ReflectionTester(TestAllExtensions.getDescriptor(),
|
||||
TestUtil.getExtensionRegistry());
|
||||
|
||||
public void testClear() throws Exception {
|
||||
AbstractMessageWrapper message =
|
||||
new AbstractMessageWrapper.Builder(
|
||||
TestAllTypes.newBuilder(TestUtil.getAllSet()))
|
||||
.clear().build();
|
||||
TestUtil.assertClear((TestAllTypes) message.wrappedMessage);
|
||||
}
|
||||
|
||||
public void testCopy() throws Exception {
|
||||
AbstractMessageWrapper message =
|
||||
new AbstractMessageWrapper.Builder(TestAllTypes.newBuilder())
|
||||
.mergeFrom(TestUtil.getAllSet()).build();
|
||||
TestUtil.assertAllFieldsSet((TestAllTypes) message.wrappedMessage);
|
||||
}
|
||||
|
||||
public void testSerializedSize() throws Exception {
|
||||
TestAllTypes message = TestUtil.getAllSet();
|
||||
Message abstractMessage = new AbstractMessageWrapper(TestUtil.getAllSet());
|
||||
|
||||
assertEquals(message.getSerializedSize(),
|
||||
abstractMessage.getSerializedSize());
|
||||
}
|
||||
|
||||
public void testSerialization() throws Exception {
|
||||
Message abstractMessage = new AbstractMessageWrapper(TestUtil.getAllSet());
|
||||
|
||||
TestUtil.assertAllFieldsSet(
|
||||
TestAllTypes.parseFrom(abstractMessage.toByteString()));
|
||||
|
||||
assertEquals(TestUtil.getAllSet().toByteString(),
|
||||
abstractMessage.toByteString());
|
||||
}
|
||||
|
||||
public void testParsing() throws Exception {
|
||||
AbstractMessageWrapper.Builder builder =
|
||||
new AbstractMessageWrapper.Builder(TestAllTypes.newBuilder());
|
||||
AbstractMessageWrapper message =
|
||||
builder.mergeFrom(TestUtil.getAllSet().toByteString()).build();
|
||||
TestUtil.assertAllFieldsSet((TestAllTypes) message.wrappedMessage);
|
||||
}
|
||||
|
||||
public void testParsingUninitialized() throws Exception {
|
||||
TestRequiredForeign.Builder builder = TestRequiredForeign.newBuilder();
|
||||
builder.getOptionalMessageBuilder().setDummy2(10);
|
||||
ByteString bytes = builder.buildPartial().toByteString();
|
||||
Message.Builder abstractMessageBuilder =
|
||||
new AbstractMessageWrapper.Builder(TestRequiredForeign.newBuilder());
|
||||
// mergeFrom() should not throw initialization error.
|
||||
abstractMessageBuilder.mergeFrom(bytes).buildPartial();
|
||||
try {
|
||||
abstractMessageBuilder.mergeFrom(bytes).build();
|
||||
fail();
|
||||
} catch (UninitializedMessageException ex) {
|
||||
// pass
|
||||
}
|
||||
|
||||
// test DynamicMessage directly.
|
||||
Message.Builder dynamicMessageBuilder = DynamicMessage.newBuilder(
|
||||
TestRequiredForeign.getDescriptor());
|
||||
// mergeFrom() should not throw initialization error.
|
||||
dynamicMessageBuilder.mergeFrom(bytes).buildPartial();
|
||||
try {
|
||||
dynamicMessageBuilder.mergeFrom(bytes).build();
|
||||
fail();
|
||||
} catch (UninitializedMessageException ex) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
public void testPackedSerialization() throws Exception {
|
||||
Message abstractMessage =
|
||||
new AbstractMessageWrapper(TestUtil.getPackedSet());
|
||||
|
||||
TestUtil.assertPackedFieldsSet(
|
||||
TestPackedTypes.parseFrom(abstractMessage.toByteString()));
|
||||
|
||||
assertEquals(TestUtil.getPackedSet().toByteString(),
|
||||
abstractMessage.toByteString());
|
||||
}
|
||||
|
||||
public void testPackedParsing() throws Exception {
|
||||
AbstractMessageWrapper.Builder builder =
|
||||
new AbstractMessageWrapper.Builder(TestPackedTypes.newBuilder());
|
||||
AbstractMessageWrapper message =
|
||||
builder.mergeFrom(TestUtil.getPackedSet().toByteString()).build();
|
||||
TestUtil.assertPackedFieldsSet((TestPackedTypes) message.wrappedMessage);
|
||||
}
|
||||
|
||||
public void testUnpackedSerialization() throws Exception {
|
||||
Message abstractMessage =
|
||||
new AbstractMessageWrapper(TestUtil.getUnpackedSet());
|
||||
|
||||
TestUtil.assertUnpackedFieldsSet(
|
||||
TestUnpackedTypes.parseFrom(abstractMessage.toByteString()));
|
||||
|
||||
assertEquals(TestUtil.getUnpackedSet().toByteString(),
|
||||
abstractMessage.toByteString());
|
||||
}
|
||||
|
||||
public void testParsePackedToUnpacked() throws Exception {
|
||||
AbstractMessageWrapper.Builder builder =
|
||||
new AbstractMessageWrapper.Builder(TestUnpackedTypes.newBuilder());
|
||||
AbstractMessageWrapper message =
|
||||
builder.mergeFrom(TestUtil.getPackedSet().toByteString()).build();
|
||||
TestUtil.assertUnpackedFieldsSet(
|
||||
(TestUnpackedTypes) message.wrappedMessage);
|
||||
}
|
||||
|
||||
public void testParseUnpackedToPacked() throws Exception {
|
||||
AbstractMessageWrapper.Builder builder =
|
||||
new AbstractMessageWrapper.Builder(TestPackedTypes.newBuilder());
|
||||
AbstractMessageWrapper message =
|
||||
builder.mergeFrom(TestUtil.getUnpackedSet().toByteString()).build();
|
||||
TestUtil.assertPackedFieldsSet((TestPackedTypes) message.wrappedMessage);
|
||||
}
|
||||
|
||||
public void testUnpackedParsing() throws Exception {
|
||||
AbstractMessageWrapper.Builder builder =
|
||||
new AbstractMessageWrapper.Builder(TestUnpackedTypes.newBuilder());
|
||||
AbstractMessageWrapper message =
|
||||
builder.mergeFrom(TestUtil.getUnpackedSet().toByteString()).build();
|
||||
TestUtil.assertUnpackedFieldsSet(
|
||||
(TestUnpackedTypes) message.wrappedMessage);
|
||||
}
|
||||
|
||||
public void testOptimizedForSize() throws Exception {
|
||||
// We're mostly only checking that this class was compiled successfully.
|
||||
TestOptimizedForSize message =
|
||||
TestOptimizedForSize.newBuilder().setI(1).build();
|
||||
message = TestOptimizedForSize.parseFrom(message.toByteString());
|
||||
assertEquals(2, message.getSerializedSize());
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Tests for isInitialized().
|
||||
|
||||
private static final TestRequired TEST_REQUIRED_UNINITIALIZED =
|
||||
TestRequired.getDefaultInstance();
|
||||
private static final TestRequired TEST_REQUIRED_INITIALIZED =
|
||||
TestRequired.newBuilder().setA(1).setB(2).setC(3).build();
|
||||
|
||||
public void testIsInitialized() throws Exception {
|
||||
TestRequired.Builder builder = TestRequired.newBuilder();
|
||||
AbstractMessageWrapper.Builder abstractBuilder =
|
||||
new AbstractMessageWrapper.Builder(builder);
|
||||
|
||||
assertFalse(abstractBuilder.isInitialized());
|
||||
assertEquals("a, b, c", abstractBuilder.getInitializationErrorString());
|
||||
builder.setA(1);
|
||||
assertFalse(abstractBuilder.isInitialized());
|
||||
assertEquals("b, c", abstractBuilder.getInitializationErrorString());
|
||||
builder.setB(1);
|
||||
assertFalse(abstractBuilder.isInitialized());
|
||||
assertEquals("c", abstractBuilder.getInitializationErrorString());
|
||||
builder.setC(1);
|
||||
assertTrue(abstractBuilder.isInitialized());
|
||||
assertEquals("", abstractBuilder.getInitializationErrorString());
|
||||
}
|
||||
|
||||
public void testForeignIsInitialized() throws Exception {
|
||||
TestRequiredForeign.Builder builder = TestRequiredForeign.newBuilder();
|
||||
AbstractMessageWrapper.Builder abstractBuilder =
|
||||
new AbstractMessageWrapper.Builder(builder);
|
||||
|
||||
assertTrue(abstractBuilder.isInitialized());
|
||||
assertEquals("", abstractBuilder.getInitializationErrorString());
|
||||
|
||||
builder.setOptionalMessage(TEST_REQUIRED_UNINITIALIZED);
|
||||
assertFalse(abstractBuilder.isInitialized());
|
||||
assertEquals(
|
||||
"optional_message.a, optional_message.b, optional_message.c",
|
||||
abstractBuilder.getInitializationErrorString());
|
||||
|
||||
builder.setOptionalMessage(TEST_REQUIRED_INITIALIZED);
|
||||
assertTrue(abstractBuilder.isInitialized());
|
||||
assertEquals("", abstractBuilder.getInitializationErrorString());
|
||||
|
||||
builder.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED);
|
||||
assertFalse(abstractBuilder.isInitialized());
|
||||
assertEquals(
|
||||
"repeated_message[0].a, repeated_message[0].b, repeated_message[0].c",
|
||||
abstractBuilder.getInitializationErrorString());
|
||||
|
||||
builder.setRepeatedMessage(0, TEST_REQUIRED_INITIALIZED);
|
||||
assertTrue(abstractBuilder.isInitialized());
|
||||
assertEquals("", abstractBuilder.getInitializationErrorString());
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Tests for mergeFrom
|
||||
|
||||
static final TestAllTypes MERGE_SOURCE =
|
||||
TestAllTypes.newBuilder()
|
||||
.setOptionalInt32(1)
|
||||
.setOptionalString("foo")
|
||||
.setOptionalForeignMessage(ForeignMessage.getDefaultInstance())
|
||||
.addRepeatedString("bar")
|
||||
.build();
|
||||
|
||||
static final TestAllTypes MERGE_DEST =
|
||||
TestAllTypes.newBuilder()
|
||||
.setOptionalInt64(2)
|
||||
.setOptionalString("baz")
|
||||
.setOptionalForeignMessage(ForeignMessage.newBuilder().setC(3).build())
|
||||
.addRepeatedString("qux")
|
||||
.build();
|
||||
|
||||
static final String MERGE_RESULT_TEXT =
|
||||
"optional_int32: 1\n" +
|
||||
"optional_int64: 2\n" +
|
||||
"optional_string: \"foo\"\n" +
|
||||
"optional_foreign_message {\n" +
|
||||
" c: 3\n" +
|
||||
"}\n" +
|
||||
"repeated_string: \"qux\"\n" +
|
||||
"repeated_string: \"bar\"\n";
|
||||
|
||||
public void testMergeFrom() throws Exception {
|
||||
AbstractMessageWrapper result =
|
||||
new AbstractMessageWrapper.Builder(
|
||||
TestAllTypes.newBuilder(MERGE_DEST))
|
||||
.mergeFrom(MERGE_SOURCE).build();
|
||||
|
||||
assertEquals(MERGE_RESULT_TEXT, result.toString());
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Tests for equals and hashCode
|
||||
|
||||
public void testEqualsAndHashCode() throws Exception {
|
||||
TestAllTypes a = TestUtil.getAllSet();
|
||||
TestAllTypes b = TestAllTypes.newBuilder().build();
|
||||
TestAllTypes c = TestAllTypes.newBuilder(b).addRepeatedString("x").build();
|
||||
TestAllTypes d = TestAllTypes.newBuilder(c).addRepeatedString("y").build();
|
||||
TestAllExtensions e = TestUtil.getAllExtensionsSet();
|
||||
TestAllExtensions f = TestAllExtensions.newBuilder(e)
|
||||
.addExtension(UnittestProto.repeatedInt32Extension, 999).build();
|
||||
|
||||
checkEqualsIsConsistent(a);
|
||||
checkEqualsIsConsistent(b);
|
||||
checkEqualsIsConsistent(c);
|
||||
checkEqualsIsConsistent(d);
|
||||
checkEqualsIsConsistent(e);
|
||||
checkEqualsIsConsistent(f);
|
||||
|
||||
checkNotEqual(a, b);
|
||||
checkNotEqual(a, c);
|
||||
checkNotEqual(a, d);
|
||||
checkNotEqual(a, e);
|
||||
checkNotEqual(a, f);
|
||||
|
||||
checkNotEqual(b, c);
|
||||
checkNotEqual(b, d);
|
||||
checkNotEqual(b, e);
|
||||
checkNotEqual(b, f);
|
||||
|
||||
checkNotEqual(c, d);
|
||||
checkNotEqual(c, e);
|
||||
checkNotEqual(c, f);
|
||||
|
||||
checkNotEqual(d, e);
|
||||
checkNotEqual(d, f);
|
||||
|
||||
checkNotEqual(e, f);
|
||||
|
||||
// Deserializing into the TestEmptyMessage such that every field
|
||||
// is an {@link UnknownFieldSet.Field}.
|
||||
UnittestProto.TestEmptyMessage eUnknownFields =
|
||||
UnittestProto.TestEmptyMessage.parseFrom(e.toByteArray());
|
||||
UnittestProto.TestEmptyMessage fUnknownFields =
|
||||
UnittestProto.TestEmptyMessage.parseFrom(f.toByteArray());
|
||||
checkNotEqual(eUnknownFields, fUnknownFields);
|
||||
checkEqualsIsConsistent(eUnknownFields);
|
||||
checkEqualsIsConsistent(fUnknownFields);
|
||||
|
||||
// Subsequent reconstitutions should be identical
|
||||
UnittestProto.TestEmptyMessage eUnknownFields2 =
|
||||
UnittestProto.TestEmptyMessage.parseFrom(e.toByteArray());
|
||||
checkEqualsIsConsistent(eUnknownFields, eUnknownFields2);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that the given proto has symmetric 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given protos are equal and have the same hash code.
|
||||
*/
|
||||
private void checkEqualsIsConsistent(Message message1, Message message2) {
|
||||
assertEquals(message1, message2);
|
||||
assertEquals(message2, message1);
|
||||
assertEquals(message2.hashCode(), message1.hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
private void checkNotEqual(Message m1, Message m2) {
|
||||
String equalsError = String.format("%s should not be equal to %s", m1, m2);
|
||||
assertFalse(equalsError, m1.equals(m2));
|
||||
assertFalse(equalsError, m2.equals(m1));
|
||||
|
||||
assertFalse(
|
||||
String.format("%s should have a different hash code from %s", m1, m2),
|
||||
m1.hashCode() == m2.hashCode());
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* This class tests {@link BoundedByteString}, which extends {@link LiteralByteString},
|
||||
* by inheriting the tests from {@link LiteralByteStringTest}. The only method which
|
||||
* is strange enough that it needs to be overridden here is {@link #testToString()}.
|
||||
*
|
||||
* @author carlanton@google.com (Carl Haverl)
|
||||
*/
|
||||
public class BoundedByteStringTest extends LiteralByteStringTest {
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
classUnderTest = "BoundedByteString";
|
||||
byte[] sourceBytes = ByteStringTest.getTestBytes(2341, 11337766L);
|
||||
int from = 100;
|
||||
int to = sourceBytes.length - 100;
|
||||
stringUnderTest = ByteString.copyFrom(sourceBytes).substring(from, to);
|
||||
referenceBytes = new byte[to - from];
|
||||
System.arraycopy(sourceBytes, from, referenceBytes, 0, to - from);
|
||||
expectedHashCode = 727575887;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testToString() throws UnsupportedEncodingException {
|
||||
String testString = "I love unicode \u1234\u5678 characters";
|
||||
LiteralByteString unicode = new LiteralByteString(testString.getBytes(UTF_8));
|
||||
ByteString chopped = unicode.substring(2, unicode.size() - 6);
|
||||
assertEquals(classUnderTest + ".substring() must have the expected type",
|
||||
classUnderTest, getActualClassName(chopped));
|
||||
|
||||
String roundTripString = chopped.toString(UTF_8);
|
||||
assertEquals(classUnderTest + " unicode bytes must match",
|
||||
testString.substring(2, testString.length() - 6), roundTripString);
|
||||
}
|
||||
}
|
||||
@@ -1,692 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.ByteString.Output;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Test methods with implementations in {@link ByteString}, plus do some top-level "integration"
|
||||
* tests.
|
||||
*
|
||||
* @author carlanton@google.com (Carl Haverl)
|
||||
*/
|
||||
public class ByteStringTest extends TestCase {
|
||||
|
||||
private static final String UTF_16 = "UTF-16";
|
||||
|
||||
static byte[] getTestBytes(int size, long seed) {
|
||||
Random random = new Random(seed);
|
||||
byte[] result = new byte[size];
|
||||
random.nextBytes(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private byte[] getTestBytes(int size) {
|
||||
return getTestBytes(size, 445566L);
|
||||
}
|
||||
|
||||
private byte[] getTestBytes() {
|
||||
return getTestBytes(1000);
|
||||
}
|
||||
|
||||
// Compare the entire left array with a subset of the right array.
|
||||
private boolean isArrayRange(byte[] left, byte[] right, int rightOffset, int length) {
|
||||
boolean stillEqual = (left.length == length);
|
||||
for (int i = 0; (stillEqual && i < length); ++i) {
|
||||
stillEqual = (left[i] == right[rightOffset + i]);
|
||||
}
|
||||
return stillEqual;
|
||||
}
|
||||
|
||||
// Returns true only if the given two arrays have identical contents.
|
||||
private boolean isArray(byte[] left, byte[] right) {
|
||||
return left.length == right.length && isArrayRange(left, right, 0, left.length);
|
||||
}
|
||||
|
||||
public void testSubstring_BeginIndex() {
|
||||
byte[] bytes = getTestBytes();
|
||||
ByteString substring = ByteString.copyFrom(bytes).substring(500);
|
||||
assertTrue("substring must contain the tail of the string",
|
||||
isArrayRange(substring.toByteArray(), bytes, 500, bytes.length - 500));
|
||||
}
|
||||
|
||||
public void testCopyFrom_BytesOffsetSize() {
|
||||
byte[] bytes = getTestBytes();
|
||||
ByteString byteString = ByteString.copyFrom(bytes, 500, 200);
|
||||
assertTrue("copyFrom sub-range must contain the expected bytes",
|
||||
isArrayRange(byteString.toByteArray(), bytes, 500, 200));
|
||||
}
|
||||
|
||||
public void testCopyFrom_Bytes() {
|
||||
byte[] bytes = getTestBytes();
|
||||
ByteString byteString = ByteString.copyFrom(bytes);
|
||||
assertTrue("copyFrom must contain the expected bytes",
|
||||
isArray(byteString.toByteArray(), bytes));
|
||||
}
|
||||
|
||||
public void testCopyFrom_ByteBufferSize() {
|
||||
byte[] bytes = getTestBytes();
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
|
||||
byteBuffer.put(bytes);
|
||||
byteBuffer.position(500);
|
||||
ByteString byteString = ByteString.copyFrom(byteBuffer, 200);
|
||||
assertTrue("copyFrom byteBuffer sub-range must contain the expected bytes",
|
||||
isArrayRange(byteString.toByteArray(), bytes, 500, 200));
|
||||
}
|
||||
|
||||
public void testCopyFrom_ByteBuffer() {
|
||||
byte[] bytes = getTestBytes();
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
|
||||
byteBuffer.put(bytes);
|
||||
byteBuffer.position(500);
|
||||
ByteString byteString = ByteString.copyFrom(byteBuffer);
|
||||
assertTrue("copyFrom byteBuffer sub-range must contain the expected bytes",
|
||||
isArrayRange(byteString.toByteArray(), bytes, 500, bytes.length - 500));
|
||||
}
|
||||
|
||||
public void testCopyFrom_StringEncoding() throws UnsupportedEncodingException {
|
||||
String testString = "I love unicode \u1234\u5678 characters";
|
||||
ByteString byteString = ByteString.copyFrom(testString, UTF_16);
|
||||
byte[] testBytes = testString.getBytes(UTF_16);
|
||||
assertTrue("copyFrom string must respect the charset",
|
||||
isArrayRange(byteString.toByteArray(), testBytes, 0, testBytes.length));
|
||||
}
|
||||
|
||||
public void testCopyFrom_Utf8() throws UnsupportedEncodingException {
|
||||
String testString = "I love unicode \u1234\u5678 characters";
|
||||
ByteString byteString = ByteString.copyFromUtf8(testString);
|
||||
byte[] testBytes = testString.getBytes("UTF-8");
|
||||
assertTrue("copyFromUtf8 string must respect the charset",
|
||||
isArrayRange(byteString.toByteArray(), testBytes, 0, testBytes.length));
|
||||
}
|
||||
|
||||
public void testCopyFrom_Iterable() {
|
||||
byte[] testBytes = getTestBytes(77777, 113344L);
|
||||
final List<ByteString> pieces = makeConcretePieces(testBytes);
|
||||
// Call copyFrom() on a Collection
|
||||
ByteString byteString = ByteString.copyFrom(pieces);
|
||||
assertTrue("copyFrom a List must contain the expected bytes",
|
||||
isArrayRange(byteString.toByteArray(), testBytes, 0, testBytes.length));
|
||||
// Call copyFrom on an iteration that's not a collection
|
||||
ByteString byteStringAlt = ByteString.copyFrom(new Iterable<ByteString>() {
|
||||
public Iterator<ByteString> iterator() {
|
||||
return pieces.iterator();
|
||||
}
|
||||
});
|
||||
assertEquals("copyFrom from an Iteration must contain the expected bytes",
|
||||
byteString, byteStringAlt);
|
||||
}
|
||||
|
||||
public void testCopyTo_TargetOffset() {
|
||||
byte[] bytes = getTestBytes();
|
||||
ByteString byteString = ByteString.copyFrom(bytes);
|
||||
byte[] target = new byte[bytes.length + 1000];
|
||||
byteString.copyTo(target, 400);
|
||||
assertTrue("copyFrom byteBuffer sub-range must contain the expected bytes",
|
||||
isArrayRange(bytes, target, 400, bytes.length));
|
||||
}
|
||||
|
||||
public void testReadFrom_emptyStream() throws IOException {
|
||||
ByteString byteString =
|
||||
ByteString.readFrom(new ByteArrayInputStream(new byte[0]));
|
||||
assertSame("reading an empty stream must result in the EMPTY constant "
|
||||
+ "byte string", ByteString.EMPTY, byteString);
|
||||
}
|
||||
|
||||
public void testReadFrom_smallStream() throws IOException {
|
||||
assertReadFrom(getTestBytes(10));
|
||||
}
|
||||
|
||||
public void testReadFrom_mutating() throws IOException {
|
||||
byte[] capturedArray = null;
|
||||
EvilInputStream eis = new EvilInputStream();
|
||||
ByteString byteString = ByteString.readFrom(eis);
|
||||
|
||||
capturedArray = eis.capturedArray;
|
||||
byte[] originalValue = byteString.toByteArray();
|
||||
for (int x = 0; x < capturedArray.length; ++x) {
|
||||
capturedArray[x] = (byte) 0;
|
||||
}
|
||||
|
||||
byte[] newValue = byteString.toByteArray();
|
||||
assertTrue("copyFrom byteBuffer must not grant access to underlying array",
|
||||
Arrays.equals(originalValue, newValue));
|
||||
}
|
||||
|
||||
// Tests sizes that are near the rope copy-out threshold.
|
||||
public void testReadFrom_mediumStream() throws IOException {
|
||||
assertReadFrom(getTestBytes(ByteString.CONCATENATE_BY_COPY_SIZE - 1));
|
||||
assertReadFrom(getTestBytes(ByteString.CONCATENATE_BY_COPY_SIZE));
|
||||
assertReadFrom(getTestBytes(ByteString.CONCATENATE_BY_COPY_SIZE + 1));
|
||||
assertReadFrom(getTestBytes(200));
|
||||
}
|
||||
|
||||
// Tests sizes that are over multi-segment rope threshold.
|
||||
public void testReadFrom_largeStream() throws IOException {
|
||||
assertReadFrom(getTestBytes(0x100));
|
||||
assertReadFrom(getTestBytes(0x101));
|
||||
assertReadFrom(getTestBytes(0x110));
|
||||
assertReadFrom(getTestBytes(0x1000));
|
||||
assertReadFrom(getTestBytes(0x1001));
|
||||
assertReadFrom(getTestBytes(0x1010));
|
||||
assertReadFrom(getTestBytes(0x10000));
|
||||
assertReadFrom(getTestBytes(0x10001));
|
||||
assertReadFrom(getTestBytes(0x10010));
|
||||
}
|
||||
|
||||
// Tests sizes that are near the read buffer size.
|
||||
public void testReadFrom_byteBoundaries() throws IOException {
|
||||
final int min = ByteString.MIN_READ_FROM_CHUNK_SIZE;
|
||||
final int max = ByteString.MAX_READ_FROM_CHUNK_SIZE;
|
||||
|
||||
assertReadFrom(getTestBytes(min - 1));
|
||||
assertReadFrom(getTestBytes(min));
|
||||
assertReadFrom(getTestBytes(min + 1));
|
||||
|
||||
assertReadFrom(getTestBytes(min * 2 - 1));
|
||||
assertReadFrom(getTestBytes(min * 2));
|
||||
assertReadFrom(getTestBytes(min * 2 + 1));
|
||||
|
||||
assertReadFrom(getTestBytes(min * 4 - 1));
|
||||
assertReadFrom(getTestBytes(min * 4));
|
||||
assertReadFrom(getTestBytes(min * 4 + 1));
|
||||
|
||||
assertReadFrom(getTestBytes(min * 8 - 1));
|
||||
assertReadFrom(getTestBytes(min * 8));
|
||||
assertReadFrom(getTestBytes(min * 8 + 1));
|
||||
|
||||
assertReadFrom(getTestBytes(max - 1));
|
||||
assertReadFrom(getTestBytes(max));
|
||||
assertReadFrom(getTestBytes(max + 1));
|
||||
|
||||
assertReadFrom(getTestBytes(max * 2 - 1));
|
||||
assertReadFrom(getTestBytes(max * 2));
|
||||
assertReadFrom(getTestBytes(max * 2 + 1));
|
||||
}
|
||||
|
||||
// Tests that IOExceptions propagate through ByteString.readFrom().
|
||||
public void testReadFrom_IOExceptions() {
|
||||
try {
|
||||
ByteString.readFrom(new FailStream());
|
||||
fail("readFrom must throw the underlying IOException");
|
||||
|
||||
} catch (IOException e) {
|
||||
assertEquals("readFrom must throw the expected exception",
|
||||
"synthetic failure", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that ByteString.readFrom works with streams that don't
|
||||
// always fill their buffers.
|
||||
public void testReadFrom_reluctantStream() throws IOException {
|
||||
final byte[] data = getTestBytes(0x1000);
|
||||
|
||||
ByteString byteString = ByteString.readFrom(new ReluctantStream(data));
|
||||
assertTrue("readFrom byte stream must contain the expected bytes",
|
||||
isArray(byteString.toByteArray(), data));
|
||||
|
||||
// Same test as above, but with some specific chunk sizes.
|
||||
assertReadFromReluctantStream(data, 100);
|
||||
assertReadFromReluctantStream(data, 248);
|
||||
assertReadFromReluctantStream(data, 249);
|
||||
assertReadFromReluctantStream(data, 250);
|
||||
assertReadFromReluctantStream(data, 251);
|
||||
assertReadFromReluctantStream(data, 0x1000);
|
||||
assertReadFromReluctantStream(data, 0x1001);
|
||||
}
|
||||
|
||||
// Fails unless ByteString.readFrom reads the bytes correctly from a
|
||||
// reluctant stream with the given chunkSize parameter.
|
||||
private void assertReadFromReluctantStream(byte[] bytes, int chunkSize)
|
||||
throws IOException {
|
||||
ByteString b = ByteString.readFrom(new ReluctantStream(bytes), chunkSize);
|
||||
assertTrue("readFrom byte stream must contain the expected bytes",
|
||||
isArray(b.toByteArray(), bytes));
|
||||
}
|
||||
|
||||
// Tests that ByteString.readFrom works with streams that implement
|
||||
// available().
|
||||
public void testReadFrom_available() throws IOException {
|
||||
final byte[] data = getTestBytes(0x1001);
|
||||
|
||||
ByteString byteString = ByteString.readFrom(new AvailableStream(data));
|
||||
assertTrue("readFrom byte stream must contain the expected bytes",
|
||||
isArray(byteString.toByteArray(), data));
|
||||
}
|
||||
|
||||
// Fails unless ByteString.readFrom reads the bytes correctly.
|
||||
private void assertReadFrom(byte[] bytes) throws IOException {
|
||||
ByteString byteString =
|
||||
ByteString.readFrom(new ByteArrayInputStream(bytes));
|
||||
assertTrue("readFrom byte stream must contain the expected bytes",
|
||||
isArray(byteString.toByteArray(), bytes));
|
||||
}
|
||||
|
||||
// A stream that fails when read.
|
||||
private static final class FailStream extends InputStream {
|
||||
@Override public int read() throws IOException {
|
||||
throw new IOException("synthetic failure");
|
||||
}
|
||||
}
|
||||
|
||||
// A stream that simulates blocking by only producing 250 characters
|
||||
// per call to read(byte[]).
|
||||
private static class ReluctantStream extends InputStream {
|
||||
protected final byte[] data;
|
||||
protected int pos = 0;
|
||||
|
||||
public ReluctantStream(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override public int read() {
|
||||
if (pos == data.length) {
|
||||
return -1;
|
||||
} else {
|
||||
return data[pos++];
|
||||
}
|
||||
}
|
||||
|
||||
@Override public int read(byte[] buf) {
|
||||
return read(buf, 0, buf.length);
|
||||
}
|
||||
|
||||
@Override public int read(byte[] buf, int offset, int size) {
|
||||
if (pos == data.length) {
|
||||
return -1;
|
||||
}
|
||||
int count = Math.min(Math.min(size, data.length - pos), 250);
|
||||
System.arraycopy(data, pos, buf, offset, count);
|
||||
pos += count;
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
// Same as above, but also implements available().
|
||||
private static final class AvailableStream extends ReluctantStream {
|
||||
public AvailableStream(byte[] data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
@Override public int available() {
|
||||
return Math.min(250, data.length - pos);
|
||||
}
|
||||
}
|
||||
|
||||
// A stream which exposes the byte array passed into read(byte[], int, int).
|
||||
private static class EvilInputStream extends InputStream {
|
||||
public byte[] capturedArray = null;
|
||||
|
||||
@Override
|
||||
public int read(byte[] buf, int off, int len) {
|
||||
if (capturedArray != null) {
|
||||
return -1;
|
||||
} else {
|
||||
capturedArray = buf;
|
||||
for (int x = 0; x < len; ++x) {
|
||||
buf[x] = (byte) x;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() {
|
||||
// Purposefully do nothing.
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// A stream which exposes the byte array passed into write(byte[], int, int).
|
||||
private static class EvilOutputStream extends OutputStream {
|
||||
public byte[] capturedArray = null;
|
||||
|
||||
@Override
|
||||
public void write(byte[] buf, int off, int len) {
|
||||
if (capturedArray == null) {
|
||||
capturedArray = buf;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int ignored) {
|
||||
// Purposefully do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
public void testToStringUtf8() throws UnsupportedEncodingException {
|
||||
String testString = "I love unicode \u1234\u5678 characters";
|
||||
byte[] testBytes = testString.getBytes("UTF-8");
|
||||
ByteString byteString = ByteString.copyFrom(testBytes);
|
||||
assertEquals("copyToStringUtf8 must respect the charset",
|
||||
testString, byteString.toStringUtf8());
|
||||
}
|
||||
|
||||
public void testNewOutput_InitialCapacity() throws IOException {
|
||||
byte[] bytes = getTestBytes();
|
||||
ByteString.Output output = ByteString.newOutput(bytes.length + 100);
|
||||
output.write(bytes);
|
||||
ByteString byteString = output.toByteString();
|
||||
assertTrue(
|
||||
"String built from newOutput(int) must contain the expected bytes",
|
||||
isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
|
||||
}
|
||||
|
||||
// Test newOutput() using a variety of buffer sizes and a variety of (fixed)
|
||||
// write sizes
|
||||
public void testNewOutput_ArrayWrite() throws IOException {
|
||||
byte[] bytes = getTestBytes();
|
||||
int length = bytes.length;
|
||||
int[] bufferSizes = {128, 256, length / 2, length - 1, length, length + 1,
|
||||
2 * length, 3 * length};
|
||||
int[] writeSizes = {1, 4, 5, 7, 23, bytes.length};
|
||||
|
||||
for (int bufferSize : bufferSizes) {
|
||||
for (int writeSize : writeSizes) {
|
||||
// Test writing the entire output writeSize bytes at a time.
|
||||
ByteString.Output output = ByteString.newOutput(bufferSize);
|
||||
for (int i = 0; i < length; i += writeSize) {
|
||||
output.write(bytes, i, Math.min(writeSize, length - i));
|
||||
}
|
||||
ByteString byteString = output.toByteString();
|
||||
assertTrue("String built from newOutput() must contain the expected bytes",
|
||||
isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test newOutput() using a variety of buffer sizes, but writing all the
|
||||
// characters using write(byte);
|
||||
public void testNewOutput_WriteChar() throws IOException {
|
||||
byte[] bytes = getTestBytes();
|
||||
int length = bytes.length;
|
||||
int[] bufferSizes = {0, 1, 128, 256, length / 2,
|
||||
length - 1, length, length + 1,
|
||||
2 * length, 3 * length};
|
||||
for (int bufferSize : bufferSizes) {
|
||||
ByteString.Output output = ByteString.newOutput(bufferSize);
|
||||
for (byte byteValue : bytes) {
|
||||
output.write(byteValue);
|
||||
}
|
||||
ByteString byteString = output.toByteString();
|
||||
assertTrue("String built from newOutput() must contain the expected bytes",
|
||||
isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
|
||||
}
|
||||
}
|
||||
|
||||
// Test newOutput() in which we write the bytes using a variety of methods
|
||||
// and sizes, and in which we repeatedly call toByteString() in the middle.
|
||||
public void testNewOutput_Mixed() throws IOException {
|
||||
Random rng = new Random(1);
|
||||
byte[] bytes = getTestBytes();
|
||||
int length = bytes.length;
|
||||
int[] bufferSizes = {0, 1, 128, 256, length / 2,
|
||||
length - 1, length, length + 1,
|
||||
2 * length, 3 * length};
|
||||
|
||||
for (int bufferSize : bufferSizes) {
|
||||
// Test writing the entire output using a mixture of write sizes and
|
||||
// methods;
|
||||
ByteString.Output output = ByteString.newOutput(bufferSize);
|
||||
int position = 0;
|
||||
while (position < bytes.length) {
|
||||
if (rng.nextBoolean()) {
|
||||
int count = 1 + rng.nextInt(bytes.length - position);
|
||||
output.write(bytes, position, count);
|
||||
position += count;
|
||||
} else {
|
||||
output.write(bytes[position]);
|
||||
position++;
|
||||
}
|
||||
assertEquals("size() returns the right value", position, output.size());
|
||||
assertTrue("newOutput() substring must have correct bytes",
|
||||
isArrayRange(output.toByteString().toByteArray(),
|
||||
bytes, 0, position));
|
||||
}
|
||||
ByteString byteString = output.toByteString();
|
||||
assertTrue("String built from newOutput() must contain the expected bytes",
|
||||
isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
|
||||
}
|
||||
}
|
||||
|
||||
public void testNewOutputEmpty() throws IOException {
|
||||
// Make sure newOutput() correctly builds empty byte strings
|
||||
ByteString byteString = ByteString.newOutput().toByteString();
|
||||
assertEquals(ByteString.EMPTY, byteString);
|
||||
}
|
||||
|
||||
public void testNewOutput_Mutating() throws IOException {
|
||||
Output os = ByteString.newOutput(5);
|
||||
os.write(new byte[] {1, 2, 3, 4, 5});
|
||||
EvilOutputStream eos = new EvilOutputStream();
|
||||
os.writeTo(eos);
|
||||
byte[] capturedArray = eos.capturedArray;
|
||||
ByteString byteString = os.toByteString();
|
||||
byte[] oldValue = byteString.toByteArray();
|
||||
Arrays.fill(capturedArray, (byte) 0);
|
||||
byte[] newValue = byteString.toByteArray();
|
||||
assertTrue("Output must not provide access to the underlying byte array",
|
||||
Arrays.equals(oldValue, newValue));
|
||||
}
|
||||
|
||||
public void testNewCodedBuilder() throws IOException {
|
||||
byte[] bytes = getTestBytes();
|
||||
ByteString.CodedBuilder builder = ByteString.newCodedBuilder(bytes.length);
|
||||
builder.getCodedOutput().writeRawBytes(bytes);
|
||||
ByteString byteString = builder.build();
|
||||
assertTrue("String built from newCodedBuilder() must contain the expected bytes",
|
||||
isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
|
||||
}
|
||||
|
||||
public void testSubstringParity() {
|
||||
byte[] bigBytes = getTestBytes(2048 * 1024, 113344L);
|
||||
int start = 512 * 1024 - 3333;
|
||||
int end = 512 * 1024 + 7777;
|
||||
ByteString concreteSubstring = ByteString.copyFrom(bigBytes).substring(start, end);
|
||||
boolean ok = true;
|
||||
for (int i = start; ok && i < end; ++i) {
|
||||
ok = (bigBytes[i] == concreteSubstring.byteAt(i - start));
|
||||
}
|
||||
assertTrue("Concrete substring didn't capture the right bytes", ok);
|
||||
|
||||
ByteString literalString = ByteString.copyFrom(bigBytes, start, end - start);
|
||||
assertTrue("Substring must be equal to literal string",
|
||||
concreteSubstring.equals(literalString));
|
||||
assertEquals("Substring must have same hashcode as literal string",
|
||||
literalString.hashCode(), concreteSubstring.hashCode());
|
||||
}
|
||||
|
||||
public void testCompositeSubstring() {
|
||||
byte[] referenceBytes = getTestBytes(77748, 113344L);
|
||||
|
||||
List<ByteString> pieces = makeConcretePieces(referenceBytes);
|
||||
ByteString listString = ByteString.copyFrom(pieces);
|
||||
|
||||
int from = 1000;
|
||||
int to = 40000;
|
||||
ByteString compositeSubstring = listString.substring(from, to);
|
||||
byte[] substringBytes = compositeSubstring.toByteArray();
|
||||
boolean stillEqual = true;
|
||||
for (int i = 0; stillEqual && i < to - from; ++i) {
|
||||
stillEqual = referenceBytes[from + i] == substringBytes[i];
|
||||
}
|
||||
assertTrue("Substring must return correct bytes", stillEqual);
|
||||
|
||||
stillEqual = true;
|
||||
for (int i = 0; stillEqual && i < to - from; ++i) {
|
||||
stillEqual = referenceBytes[from + i] == compositeSubstring.byteAt(i);
|
||||
}
|
||||
assertTrue("Substring must support byteAt() correctly", stillEqual);
|
||||
|
||||
ByteString literalSubstring = ByteString.copyFrom(referenceBytes, from, to - from);
|
||||
assertTrue("Composite substring must equal a literal substring over the same bytes",
|
||||
compositeSubstring.equals(literalSubstring));
|
||||
assertTrue("Literal substring must equal a composite substring over the same bytes",
|
||||
literalSubstring.equals(compositeSubstring));
|
||||
|
||||
assertEquals("We must get the same hashcodes for composite and literal substrings",
|
||||
literalSubstring.hashCode(), compositeSubstring.hashCode());
|
||||
|
||||
assertFalse("We can't be equal to a proper substring",
|
||||
compositeSubstring.equals(literalSubstring.substring(0, literalSubstring.size() - 1)));
|
||||
}
|
||||
|
||||
public void testCopyFromList() {
|
||||
byte[] referenceBytes = getTestBytes(77748, 113344L);
|
||||
ByteString literalString = ByteString.copyFrom(referenceBytes);
|
||||
|
||||
List<ByteString> pieces = makeConcretePieces(referenceBytes);
|
||||
ByteString listString = ByteString.copyFrom(pieces);
|
||||
|
||||
assertTrue("Composite string must be equal to literal string",
|
||||
listString.equals(literalString));
|
||||
assertEquals("Composite string must have same hashcode as literal string",
|
||||
literalString.hashCode(), listString.hashCode());
|
||||
}
|
||||
|
||||
public void testConcat() {
|
||||
byte[] referenceBytes = getTestBytes(77748, 113344L);
|
||||
ByteString literalString = ByteString.copyFrom(referenceBytes);
|
||||
|
||||
List<ByteString> pieces = makeConcretePieces(referenceBytes);
|
||||
|
||||
Iterator<ByteString> iter = pieces.iterator();
|
||||
ByteString concatenatedString = iter.next();
|
||||
while (iter.hasNext()) {
|
||||
concatenatedString = concatenatedString.concat(iter.next());
|
||||
}
|
||||
|
||||
assertTrue("Concatenated string must be equal to literal string",
|
||||
concatenatedString.equals(literalString));
|
||||
assertEquals("Concatenated string must have same hashcode as literal string",
|
||||
literalString.hashCode(), concatenatedString.hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the Rope implementation can deal with Empty nodes, even though we
|
||||
* guard against them. See also {@link LiteralByteStringTest#testConcat_empty()}.
|
||||
*/
|
||||
public void testConcat_empty() {
|
||||
byte[] referenceBytes = getTestBytes(7748, 113344L);
|
||||
ByteString literalString = ByteString.copyFrom(referenceBytes);
|
||||
|
||||
ByteString duo = RopeByteString.newInstanceForTest(literalString, literalString);
|
||||
ByteString temp = RopeByteString.newInstanceForTest(
|
||||
RopeByteString.newInstanceForTest(literalString, ByteString.EMPTY),
|
||||
RopeByteString.newInstanceForTest(ByteString.EMPTY, literalString));
|
||||
ByteString quintet = RopeByteString.newInstanceForTest(temp, ByteString.EMPTY);
|
||||
|
||||
assertTrue("String with concatenated nulls must equal simple concatenate",
|
||||
duo.equals(quintet));
|
||||
assertEquals("String with concatenated nulls have same hashcode as simple concatenate",
|
||||
duo.hashCode(), quintet.hashCode());
|
||||
|
||||
ByteString.ByteIterator duoIter = duo.iterator();
|
||||
ByteString.ByteIterator quintetIter = quintet.iterator();
|
||||
boolean stillEqual = true;
|
||||
while (stillEqual && quintetIter.hasNext()) {
|
||||
stillEqual = (duoIter.nextByte() == quintetIter.nextByte());
|
||||
}
|
||||
assertTrue("We must get the same characters by iterating", stillEqual);
|
||||
assertFalse("Iterator must be exhausted", duoIter.hasNext());
|
||||
try {
|
||||
duoIter.nextByte();
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (NoSuchElementException e) {
|
||||
// This is success
|
||||
}
|
||||
try {
|
||||
quintetIter.nextByte();
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (NoSuchElementException e) {
|
||||
// This is success
|
||||
}
|
||||
|
||||
// Test that even if we force empty strings in as rope leaves in this
|
||||
// configuration, we always get a (possibly Bounded) LiteralByteString
|
||||
// for a length 1 substring.
|
||||
//
|
||||
// It is possible, using the testing factory method to create deeply nested
|
||||
// trees of empty leaves, to make a string that will fail this test.
|
||||
for (int i = 1; i < duo.size(); ++i) {
|
||||
assertTrue("Substrings of size() < 2 must not be RopeByteStrings",
|
||||
duo.substring(i - 1, i) instanceof LiteralByteString);
|
||||
}
|
||||
for (int i = 1; i < quintet.size(); ++i) {
|
||||
assertTrue("Substrings of size() < 2 must not be RopeByteStrings",
|
||||
quintet.substring(i - 1, i) instanceof LiteralByteString);
|
||||
}
|
||||
}
|
||||
|
||||
public void testStartsWith() {
|
||||
byte[] bytes = getTestBytes(1000, 1234L);
|
||||
ByteString string = ByteString.copyFrom(bytes);
|
||||
ByteString prefix = ByteString.copyFrom(bytes, 0, 500);
|
||||
ByteString suffix = ByteString.copyFrom(bytes, 400, 600);
|
||||
assertTrue(string.startsWith(ByteString.EMPTY));
|
||||
assertTrue(string.startsWith(string));
|
||||
assertTrue(string.startsWith(prefix));
|
||||
assertFalse(string.startsWith(suffix));
|
||||
assertFalse(prefix.startsWith(suffix));
|
||||
assertFalse(suffix.startsWith(prefix));
|
||||
assertFalse(ByteString.EMPTY.startsWith(prefix));
|
||||
assertTrue(ByteString.EMPTY.startsWith(ByteString.EMPTY));
|
||||
}
|
||||
|
||||
static List<ByteString> makeConcretePieces(byte[] referenceBytes) {
|
||||
List<ByteString> pieces = new ArrayList<ByteString>();
|
||||
// Starting length should be small enough that we'll do some concatenating by
|
||||
// copying if we just concatenate all these pieces together.
|
||||
for (int start = 0, length = 16; start < referenceBytes.length; start += length) {
|
||||
length = (length << 1) - 1;
|
||||
if (start + length > referenceBytes.length) {
|
||||
length = referenceBytes.length - start;
|
||||
}
|
||||
pieces.add(ByteString.copyFrom(referenceBytes, start, length));
|
||||
}
|
||||
return pieces;
|
||||
}
|
||||
}
|
||||
@@ -1,528 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestRecursiveMessage;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Unit test for {@link CodedInputStream}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class CodedInputStreamTest extends TestCase {
|
||||
/**
|
||||
* Helper to construct a byte array from a bunch of bytes. The inputs are
|
||||
* actually ints so that I can use hex notation and not get stupid errors
|
||||
* about precision.
|
||||
*/
|
||||
private byte[] bytes(int... bytesAsInts) {
|
||||
byte[] bytes = new byte[bytesAsInts.length];
|
||||
for (int i = 0; i < bytesAsInts.length; i++) {
|
||||
bytes[i] = (byte) bytesAsInts[i];
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* An InputStream which limits the number of bytes it reads at a time.
|
||||
* We use this to make sure that CodedInputStream doesn't screw up when
|
||||
* reading in small blocks.
|
||||
*/
|
||||
private static final class SmallBlockInputStream extends FilterInputStream {
|
||||
private final int blockSize;
|
||||
|
||||
public SmallBlockInputStream(byte[] data, int blockSize) {
|
||||
this(new ByteArrayInputStream(data), blockSize);
|
||||
}
|
||||
|
||||
public SmallBlockInputStream(InputStream in, int blockSize) {
|
||||
super(in);
|
||||
this.blockSize = blockSize;
|
||||
}
|
||||
|
||||
public int read(byte[] b) throws IOException {
|
||||
return super.read(b, 0, Math.min(b.length, blockSize));
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
return super.read(b, off, Math.min(len, blockSize));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given bytes using readRawVarint32() and readRawVarint64() and
|
||||
* checks that the result matches the given value.
|
||||
*/
|
||||
private void assertReadVarint(byte[] data, long value) throws Exception {
|
||||
CodedInputStream input = CodedInputStream.newInstance(data);
|
||||
assertEquals((int)value, input.readRawVarint32());
|
||||
|
||||
input = CodedInputStream.newInstance(data);
|
||||
assertEquals(value, input.readRawVarint64());
|
||||
assertTrue(input.isAtEnd());
|
||||
|
||||
// Try different block sizes.
|
||||
for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
|
||||
input = CodedInputStream.newInstance(
|
||||
new SmallBlockInputStream(data, blockSize));
|
||||
assertEquals((int)value, input.readRawVarint32());
|
||||
|
||||
input = CodedInputStream.newInstance(
|
||||
new SmallBlockInputStream(data, blockSize));
|
||||
assertEquals(value, input.readRawVarint64());
|
||||
assertTrue(input.isAtEnd());
|
||||
}
|
||||
|
||||
// Try reading direct from an InputStream. We want to verify that it
|
||||
// doesn't read past the end of the input, so we copy to a new, bigger
|
||||
// array first.
|
||||
byte[] longerData = new byte[data.length + 1];
|
||||
System.arraycopy(data, 0, longerData, 0, data.length);
|
||||
InputStream rawInput = new ByteArrayInputStream(longerData);
|
||||
assertEquals((int)value, CodedInputStream.readRawVarint32(rawInput));
|
||||
assertEquals(1, rawInput.available());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given bytes using readRawVarint32() and readRawVarint64() and
|
||||
* expects them to fail with an InvalidProtocolBufferException whose
|
||||
* description matches the given one.
|
||||
*/
|
||||
private void assertReadVarintFailure(
|
||||
InvalidProtocolBufferException expected, byte[] data)
|
||||
throws Exception {
|
||||
CodedInputStream input = CodedInputStream.newInstance(data);
|
||||
try {
|
||||
input.readRawVarint32();
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
assertEquals(expected.getMessage(), e.getMessage());
|
||||
}
|
||||
|
||||
input = CodedInputStream.newInstance(data);
|
||||
try {
|
||||
input.readRawVarint64();
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
assertEquals(expected.getMessage(), e.getMessage());
|
||||
}
|
||||
|
||||
// Make sure we get the same error when reading direct from an InputStream.
|
||||
try {
|
||||
CodedInputStream.readRawVarint32(new ByteArrayInputStream(data));
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
assertEquals(expected.getMessage(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests readRawVarint32() and readRawVarint64(). */
|
||||
public void testReadVarint() throws Exception {
|
||||
assertReadVarint(bytes(0x00), 0);
|
||||
assertReadVarint(bytes(0x01), 1);
|
||||
assertReadVarint(bytes(0x7f), 127);
|
||||
// 14882
|
||||
assertReadVarint(bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7));
|
||||
// 2961488830
|
||||
assertReadVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),
|
||||
(0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
|
||||
(0x0bL << 28));
|
||||
|
||||
// 64-bit
|
||||
// 7256456126
|
||||
assertReadVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),
|
||||
(0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
|
||||
(0x1bL << 28));
|
||||
// 41256202580718336
|
||||
assertReadVarint(
|
||||
bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),
|
||||
(0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
|
||||
(0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49));
|
||||
// 11964378330978735131
|
||||
assertReadVarint(
|
||||
bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),
|
||||
(0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
|
||||
(0x3bL << 28) | (0x56L << 35) | (0x00L << 42) |
|
||||
(0x05L << 49) | (0x26L << 56) | (0x01L << 63));
|
||||
|
||||
// Failures
|
||||
assertReadVarintFailure(
|
||||
InvalidProtocolBufferException.malformedVarint(),
|
||||
bytes(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
||||
0x00));
|
||||
assertReadVarintFailure(
|
||||
InvalidProtocolBufferException.truncatedMessage(),
|
||||
bytes(0x80));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given bytes using readRawLittleEndian32() and checks
|
||||
* that the result matches the given value.
|
||||
*/
|
||||
private void assertReadLittleEndian32(byte[] data, int value)
|
||||
throws Exception {
|
||||
CodedInputStream input = CodedInputStream.newInstance(data);
|
||||
assertEquals(value, input.readRawLittleEndian32());
|
||||
assertTrue(input.isAtEnd());
|
||||
|
||||
// Try different block sizes.
|
||||
for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
|
||||
input = CodedInputStream.newInstance(
|
||||
new SmallBlockInputStream(data, blockSize));
|
||||
assertEquals(value, input.readRawLittleEndian32());
|
||||
assertTrue(input.isAtEnd());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given bytes using readRawLittleEndian64() and checks
|
||||
* that the result matches the given value.
|
||||
*/
|
||||
private void assertReadLittleEndian64(byte[] data, long value)
|
||||
throws Exception {
|
||||
CodedInputStream input = CodedInputStream.newInstance(data);
|
||||
assertEquals(value, input.readRawLittleEndian64());
|
||||
assertTrue(input.isAtEnd());
|
||||
|
||||
// Try different block sizes.
|
||||
for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
|
||||
input = CodedInputStream.newInstance(
|
||||
new SmallBlockInputStream(data, blockSize));
|
||||
assertEquals(value, input.readRawLittleEndian64());
|
||||
assertTrue(input.isAtEnd());
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests readRawLittleEndian32() and readRawLittleEndian64(). */
|
||||
public void testReadLittleEndian() throws Exception {
|
||||
assertReadLittleEndian32(bytes(0x78, 0x56, 0x34, 0x12), 0x12345678);
|
||||
assertReadLittleEndian32(bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0);
|
||||
|
||||
assertReadLittleEndian64(
|
||||
bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12),
|
||||
0x123456789abcdef0L);
|
||||
assertReadLittleEndian64(
|
||||
bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a),
|
||||
0x9abcdef012345678L);
|
||||
}
|
||||
|
||||
/** Test decodeZigZag32() and decodeZigZag64(). */
|
||||
public void testDecodeZigZag() throws Exception {
|
||||
assertEquals( 0, CodedInputStream.decodeZigZag32(0));
|
||||
assertEquals(-1, CodedInputStream.decodeZigZag32(1));
|
||||
assertEquals( 1, CodedInputStream.decodeZigZag32(2));
|
||||
assertEquals(-2, CodedInputStream.decodeZigZag32(3));
|
||||
assertEquals(0x3FFFFFFF, CodedInputStream.decodeZigZag32(0x7FFFFFFE));
|
||||
assertEquals(0xC0000000, CodedInputStream.decodeZigZag32(0x7FFFFFFF));
|
||||
assertEquals(0x7FFFFFFF, CodedInputStream.decodeZigZag32(0xFFFFFFFE));
|
||||
assertEquals(0x80000000, CodedInputStream.decodeZigZag32(0xFFFFFFFF));
|
||||
|
||||
assertEquals( 0, CodedInputStream.decodeZigZag64(0));
|
||||
assertEquals(-1, CodedInputStream.decodeZigZag64(1));
|
||||
assertEquals( 1, CodedInputStream.decodeZigZag64(2));
|
||||
assertEquals(-2, CodedInputStream.decodeZigZag64(3));
|
||||
assertEquals(0x000000003FFFFFFFL,
|
||||
CodedInputStream.decodeZigZag64(0x000000007FFFFFFEL));
|
||||
assertEquals(0xFFFFFFFFC0000000L,
|
||||
CodedInputStream.decodeZigZag64(0x000000007FFFFFFFL));
|
||||
assertEquals(0x000000007FFFFFFFL,
|
||||
CodedInputStream.decodeZigZag64(0x00000000FFFFFFFEL));
|
||||
assertEquals(0xFFFFFFFF80000000L,
|
||||
CodedInputStream.decodeZigZag64(0x00000000FFFFFFFFL));
|
||||
assertEquals(0x7FFFFFFFFFFFFFFFL,
|
||||
CodedInputStream.decodeZigZag64(0xFFFFFFFFFFFFFFFEL));
|
||||
assertEquals(0x8000000000000000L,
|
||||
CodedInputStream.decodeZigZag64(0xFFFFFFFFFFFFFFFFL));
|
||||
}
|
||||
|
||||
/** Tests reading and parsing a whole message with every field type. */
|
||||
public void testReadWholeMessage() throws Exception {
|
||||
TestAllTypes message = TestUtil.getAllSet();
|
||||
|
||||
byte[] rawBytes = message.toByteArray();
|
||||
assertEquals(rawBytes.length, message.getSerializedSize());
|
||||
|
||||
TestAllTypes message2 = TestAllTypes.parseFrom(rawBytes);
|
||||
TestUtil.assertAllFieldsSet(message2);
|
||||
|
||||
// Try different block sizes.
|
||||
for (int blockSize = 1; blockSize < 256; blockSize *= 2) {
|
||||
message2 = TestAllTypes.parseFrom(
|
||||
new SmallBlockInputStream(rawBytes, blockSize));
|
||||
TestUtil.assertAllFieldsSet(message2);
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests skipField(). */
|
||||
public void testSkipWholeMessage() throws Exception {
|
||||
TestAllTypes message = TestUtil.getAllSet();
|
||||
byte[] rawBytes = message.toByteArray();
|
||||
|
||||
// Create two parallel inputs. Parse one as unknown fields while using
|
||||
// skipField() to skip each field on the other. Expect the same tags.
|
||||
CodedInputStream input1 = CodedInputStream.newInstance(rawBytes);
|
||||
CodedInputStream input2 = CodedInputStream.newInstance(rawBytes);
|
||||
UnknownFieldSet.Builder unknownFields = UnknownFieldSet.newBuilder();
|
||||
|
||||
while (true) {
|
||||
int tag = input1.readTag();
|
||||
assertEquals(tag, input2.readTag());
|
||||
if (tag == 0) {
|
||||
break;
|
||||
}
|
||||
unknownFields.mergeFieldFrom(tag, input1);
|
||||
input2.skipField(tag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a bug in skipRawBytes() has been fixed: if the skip skips
|
||||
* exactly up to a limit, this should not break things.
|
||||
*/
|
||||
public void testSkipRawBytesBug() throws Exception {
|
||||
byte[] rawBytes = new byte[] { 1, 2 };
|
||||
CodedInputStream input = CodedInputStream.newInstance(rawBytes);
|
||||
|
||||
int limit = input.pushLimit(1);
|
||||
input.skipRawBytes(1);
|
||||
input.popLimit(limit);
|
||||
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];
|
||||
for (int i = 0; i < blob.length; i++) {
|
||||
blob[i] = (byte)i;
|
||||
}
|
||||
|
||||
// Make a message containing it.
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TestUtil.setAllFields(builder);
|
||||
builder.setOptionalBytes(ByteString.copyFrom(blob));
|
||||
TestAllTypes message = builder.build();
|
||||
|
||||
// Serialize and parse it. Make sure to parse from an InputStream, not
|
||||
// directly from a ByteString, so that CodedInputStream uses buffered
|
||||
// reading.
|
||||
TestAllTypes message2 =
|
||||
TestAllTypes.parseFrom(message.toByteString().newInput());
|
||||
|
||||
assertEquals(message.getOptionalBytes(), message2.getOptionalBytes());
|
||||
|
||||
// Make sure all the other fields were parsed correctly.
|
||||
TestAllTypes message3 = TestAllTypes.newBuilder(message2)
|
||||
.setOptionalBytes(TestUtil.getAllSet().getOptionalBytes())
|
||||
.build();
|
||||
TestUtil.assertAllFieldsSet(message3);
|
||||
}
|
||||
|
||||
public void testReadMaliciouslyLargeBlob() throws Exception {
|
||||
ByteString.Output rawOutput = ByteString.newOutput();
|
||||
CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
|
||||
|
||||
int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
|
||||
output.writeRawVarint32(tag);
|
||||
output.writeRawVarint32(0x7FFFFFFF);
|
||||
output.writeRawBytes(new byte[32]); // Pad with a few random bytes.
|
||||
output.flush();
|
||||
|
||||
CodedInputStream input = rawOutput.toByteString().newCodedInput();
|
||||
assertEquals(tag, input.readTag());
|
||||
|
||||
try {
|
||||
input.readBytes();
|
||||
fail("Should have thrown an exception!");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// success.
|
||||
}
|
||||
}
|
||||
|
||||
private TestRecursiveMessage makeRecursiveMessage(int depth) {
|
||||
if (depth == 0) {
|
||||
return TestRecursiveMessage.newBuilder().setI(5).build();
|
||||
} else {
|
||||
return TestRecursiveMessage.newBuilder()
|
||||
.setA(makeRecursiveMessage(depth - 1)).build();
|
||||
}
|
||||
}
|
||||
|
||||
private void assertMessageDepth(TestRecursiveMessage message, int depth) {
|
||||
if (depth == 0) {
|
||||
assertFalse(message.hasA());
|
||||
assertEquals(5, message.getI());
|
||||
} else {
|
||||
assertTrue(message.hasA());
|
||||
assertMessageDepth(message.getA(), depth - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void testMaliciousRecursion() throws Exception {
|
||||
ByteString data64 = makeRecursiveMessage(64).toByteString();
|
||||
ByteString data65 = makeRecursiveMessage(65).toByteString();
|
||||
|
||||
assertMessageDepth(TestRecursiveMessage.parseFrom(data64), 64);
|
||||
|
||||
try {
|
||||
TestRecursiveMessage.parseFrom(data65);
|
||||
fail("Should have thrown an exception!");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// success.
|
||||
}
|
||||
|
||||
CodedInputStream input = data64.newCodedInput();
|
||||
input.setRecursionLimit(8);
|
||||
try {
|
||||
TestRecursiveMessage.parseFrom(input);
|
||||
fail("Should have thrown an exception!");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// success.
|
||||
}
|
||||
}
|
||||
|
||||
public void testSizeLimit() throws Exception {
|
||||
CodedInputStream input = CodedInputStream.newInstance(
|
||||
TestUtil.getAllSet().toByteString().newInput());
|
||||
input.setSizeLimit(16);
|
||||
|
||||
try {
|
||||
TestAllTypes.parseFrom(input);
|
||||
fail("Should have thrown an exception!");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// success.
|
||||
}
|
||||
}
|
||||
|
||||
public void testResetSizeCounter() throws Exception {
|
||||
CodedInputStream input = CodedInputStream.newInstance(
|
||||
new SmallBlockInputStream(new byte[256], 8));
|
||||
input.setSizeLimit(16);
|
||||
input.readRawBytes(16);
|
||||
assertEquals(16, input.getTotalBytesRead());
|
||||
|
||||
try {
|
||||
input.readRawByte();
|
||||
fail("Should have thrown an exception!");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// success.
|
||||
}
|
||||
|
||||
input.resetSizeCounter();
|
||||
assertEquals(0, input.getTotalBytesRead());
|
||||
input.readRawByte(); // No exception thrown.
|
||||
input.resetSizeCounter();
|
||||
assertEquals(0, input.getTotalBytesRead());
|
||||
|
||||
try {
|
||||
input.readRawBytes(16); // Hits limit again.
|
||||
fail("Should have thrown an exception!");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// success.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that if we read an string that contains invalid UTF-8, no exception
|
||||
* is thrown. Instead, the invalid bytes are replaced with the Unicode
|
||||
* "replacement character" U+FFFD.
|
||||
*/
|
||||
public void testReadInvalidUtf8() throws Exception {
|
||||
ByteString.Output rawOutput = ByteString.newOutput();
|
||||
CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
|
||||
|
||||
int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
|
||||
output.writeRawVarint32(tag);
|
||||
output.writeRawVarint32(1);
|
||||
output.writeRawBytes(new byte[] { (byte)0x80 });
|
||||
output.flush();
|
||||
|
||||
CodedInputStream input = rawOutput.toByteString().newCodedInput();
|
||||
assertEquals(tag, input.readTag());
|
||||
String text = input.readString();
|
||||
assertEquals(0xfffd, text.charAt(0));
|
||||
}
|
||||
|
||||
public void testReadFromSlice() throws Exception {
|
||||
byte[] bytes = bytes(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
|
||||
CodedInputStream in = CodedInputStream.newInstance(bytes, 3, 5);
|
||||
assertEquals(0, in.getTotalBytesRead());
|
||||
for (int i = 3; i < 8; i++) {
|
||||
assertEquals(i, in.readRawByte());
|
||||
assertEquals(i-2, in.getTotalBytesRead());
|
||||
}
|
||||
// eof
|
||||
assertEquals(0, in.readTag());
|
||||
assertEquals(5, in.getTotalBytesRead());
|
||||
}
|
||||
|
||||
public void testInvalidTag() throws Exception {
|
||||
// Any tag number which corresponds to field number zero is invalid and
|
||||
// should throw InvalidProtocolBufferException.
|
||||
for (int i = 0; i < 8; i++) {
|
||||
try {
|
||||
CodedInputStream.newInstance(bytes(i)).readTag();
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
assertEquals(InvalidProtocolBufferException.invalidTag().getMessage(),
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,317 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import protobuf_unittest.UnittestProto.SparseEnumMessage;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestPackedTypes;
|
||||
import protobuf_unittest.UnittestProto.TestSparseEnum;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Unit test for {@link CodedOutputStream}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class CodedOutputStreamTest extends TestCase {
|
||||
/**
|
||||
* Helper to construct a byte array from a bunch of bytes. The inputs are
|
||||
* actually ints so that I can use hex notation and not get stupid errors
|
||||
* about precision.
|
||||
*/
|
||||
private byte[] bytes(int... bytesAsInts) {
|
||||
byte[] bytes = new byte[bytesAsInts.length];
|
||||
for (int i = 0; i < bytesAsInts.length; i++) {
|
||||
bytes[i] = (byte) bytesAsInts[i];
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/** Arrays.asList() does not work with arrays of primitives. :( */
|
||||
private List<Byte> toList(byte[] bytes) {
|
||||
List<Byte> result = new ArrayList<Byte>();
|
||||
for (byte b : bytes) {
|
||||
result.add(b);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void assertEqualBytes(byte[] a, byte[] b) {
|
||||
assertEquals(toList(a), toList(b));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given value using writeRawVarint32() and writeRawVarint64() and
|
||||
* checks that the result matches the given bytes.
|
||||
*/
|
||||
private void assertWriteVarint(byte[] data, long value) throws Exception {
|
||||
// Only do 32-bit write if the value fits in 32 bits.
|
||||
if ((value >>> 32) == 0) {
|
||||
ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
|
||||
CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
|
||||
output.writeRawVarint32((int) value);
|
||||
output.flush();
|
||||
assertEqualBytes(data, rawOutput.toByteArray());
|
||||
|
||||
// Also try computing size.
|
||||
assertEquals(data.length,
|
||||
CodedOutputStream.computeRawVarint32Size((int) value));
|
||||
}
|
||||
|
||||
{
|
||||
ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
|
||||
CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
|
||||
output.writeRawVarint64(value);
|
||||
output.flush();
|
||||
assertEqualBytes(data, rawOutput.toByteArray());
|
||||
|
||||
// Also try computing size.
|
||||
assertEquals(data.length,
|
||||
CodedOutputStream.computeRawVarint64Size(value));
|
||||
}
|
||||
|
||||
// Try different block sizes.
|
||||
for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
|
||||
// Only do 32-bit write if the value fits in 32 bits.
|
||||
if ((value >>> 32) == 0) {
|
||||
ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
|
||||
CodedOutputStream output =
|
||||
CodedOutputStream.newInstance(rawOutput, blockSize);
|
||||
output.writeRawVarint32((int) value);
|
||||
output.flush();
|
||||
assertEqualBytes(data, rawOutput.toByteArray());
|
||||
}
|
||||
|
||||
{
|
||||
ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
|
||||
CodedOutputStream output =
|
||||
CodedOutputStream.newInstance(rawOutput, blockSize);
|
||||
output.writeRawVarint64(value);
|
||||
output.flush();
|
||||
assertEqualBytes(data, rawOutput.toByteArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests writeRawVarint32() and writeRawVarint64(). */
|
||||
public void testWriteVarint() throws Exception {
|
||||
assertWriteVarint(bytes(0x00), 0);
|
||||
assertWriteVarint(bytes(0x01), 1);
|
||||
assertWriteVarint(bytes(0x7f), 127);
|
||||
// 14882
|
||||
assertWriteVarint(bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7));
|
||||
// 2961488830
|
||||
assertWriteVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),
|
||||
(0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
|
||||
(0x0bL << 28));
|
||||
|
||||
// 64-bit
|
||||
// 7256456126
|
||||
assertWriteVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),
|
||||
(0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
|
||||
(0x1bL << 28));
|
||||
// 41256202580718336
|
||||
assertWriteVarint(
|
||||
bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),
|
||||
(0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
|
||||
(0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49));
|
||||
// 11964378330978735131
|
||||
assertWriteVarint(
|
||||
bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),
|
||||
(0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
|
||||
(0x3bL << 28) | (0x56L << 35) | (0x00L << 42) |
|
||||
(0x05L << 49) | (0x26L << 56) | (0x01L << 63));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given bytes using writeRawLittleEndian32() and checks
|
||||
* that the result matches the given value.
|
||||
*/
|
||||
private void assertWriteLittleEndian32(byte[] data, int value)
|
||||
throws Exception {
|
||||
ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
|
||||
CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
|
||||
output.writeRawLittleEndian32(value);
|
||||
output.flush();
|
||||
assertEqualBytes(data, rawOutput.toByteArray());
|
||||
|
||||
// Try different block sizes.
|
||||
for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
|
||||
rawOutput = new ByteArrayOutputStream();
|
||||
output = CodedOutputStream.newInstance(rawOutput, blockSize);
|
||||
output.writeRawLittleEndian32(value);
|
||||
output.flush();
|
||||
assertEqualBytes(data, rawOutput.toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given bytes using writeRawLittleEndian64() and checks
|
||||
* that the result matches the given value.
|
||||
*/
|
||||
private void assertWriteLittleEndian64(byte[] data, long value)
|
||||
throws Exception {
|
||||
ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
|
||||
CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
|
||||
output.writeRawLittleEndian64(value);
|
||||
output.flush();
|
||||
assertEqualBytes(data, rawOutput.toByteArray());
|
||||
|
||||
// Try different block sizes.
|
||||
for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
|
||||
rawOutput = new ByteArrayOutputStream();
|
||||
output = CodedOutputStream.newInstance(rawOutput, blockSize);
|
||||
output.writeRawLittleEndian64(value);
|
||||
output.flush();
|
||||
assertEqualBytes(data, rawOutput.toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests writeRawLittleEndian32() and writeRawLittleEndian64(). */
|
||||
public void testWriteLittleEndian() throws Exception {
|
||||
assertWriteLittleEndian32(bytes(0x78, 0x56, 0x34, 0x12), 0x12345678);
|
||||
assertWriteLittleEndian32(bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0);
|
||||
|
||||
assertWriteLittleEndian64(
|
||||
bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12),
|
||||
0x123456789abcdef0L);
|
||||
assertWriteLittleEndian64(
|
||||
bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a),
|
||||
0x9abcdef012345678L);
|
||||
}
|
||||
|
||||
/** Test encodeZigZag32() and encodeZigZag64(). */
|
||||
public void testEncodeZigZag() throws Exception {
|
||||
assertEquals(0, CodedOutputStream.encodeZigZag32( 0));
|
||||
assertEquals(1, CodedOutputStream.encodeZigZag32(-1));
|
||||
assertEquals(2, CodedOutputStream.encodeZigZag32( 1));
|
||||
assertEquals(3, CodedOutputStream.encodeZigZag32(-2));
|
||||
assertEquals(0x7FFFFFFE, CodedOutputStream.encodeZigZag32(0x3FFFFFFF));
|
||||
assertEquals(0x7FFFFFFF, CodedOutputStream.encodeZigZag32(0xC0000000));
|
||||
assertEquals(0xFFFFFFFE, CodedOutputStream.encodeZigZag32(0x7FFFFFFF));
|
||||
assertEquals(0xFFFFFFFF, CodedOutputStream.encodeZigZag32(0x80000000));
|
||||
|
||||
assertEquals(0, CodedOutputStream.encodeZigZag64( 0));
|
||||
assertEquals(1, CodedOutputStream.encodeZigZag64(-1));
|
||||
assertEquals(2, CodedOutputStream.encodeZigZag64( 1));
|
||||
assertEquals(3, CodedOutputStream.encodeZigZag64(-2));
|
||||
assertEquals(0x000000007FFFFFFEL,
|
||||
CodedOutputStream.encodeZigZag64(0x000000003FFFFFFFL));
|
||||
assertEquals(0x000000007FFFFFFFL,
|
||||
CodedOutputStream.encodeZigZag64(0xFFFFFFFFC0000000L));
|
||||
assertEquals(0x00000000FFFFFFFEL,
|
||||
CodedOutputStream.encodeZigZag64(0x000000007FFFFFFFL));
|
||||
assertEquals(0x00000000FFFFFFFFL,
|
||||
CodedOutputStream.encodeZigZag64(0xFFFFFFFF80000000L));
|
||||
assertEquals(0xFFFFFFFFFFFFFFFEL,
|
||||
CodedOutputStream.encodeZigZag64(0x7FFFFFFFFFFFFFFFL));
|
||||
assertEquals(0xFFFFFFFFFFFFFFFFL,
|
||||
CodedOutputStream.encodeZigZag64(0x8000000000000000L));
|
||||
|
||||
// Some easier-to-verify round-trip tests. The inputs (other than 0, 1, -1)
|
||||
// were chosen semi-randomly via keyboard bashing.
|
||||
assertEquals(0,
|
||||
CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(0)));
|
||||
assertEquals(1,
|
||||
CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(1)));
|
||||
assertEquals(-1,
|
||||
CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(-1)));
|
||||
assertEquals(14927,
|
||||
CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(14927)));
|
||||
assertEquals(-3612,
|
||||
CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(-3612)));
|
||||
|
||||
assertEquals(0,
|
||||
CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(0)));
|
||||
assertEquals(1,
|
||||
CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(1)));
|
||||
assertEquals(-1,
|
||||
CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(-1)));
|
||||
assertEquals(14927,
|
||||
CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(14927)));
|
||||
assertEquals(-3612,
|
||||
CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(-3612)));
|
||||
|
||||
assertEquals(856912304801416L,
|
||||
CodedOutputStream.encodeZigZag64(
|
||||
CodedInputStream.decodeZigZag64(
|
||||
856912304801416L)));
|
||||
assertEquals(-75123905439571256L,
|
||||
CodedOutputStream.encodeZigZag64(
|
||||
CodedInputStream.decodeZigZag64(
|
||||
-75123905439571256L)));
|
||||
}
|
||||
|
||||
/** Tests writing a whole message with every field type. */
|
||||
public void testWriteWholeMessage() throws Exception {
|
||||
TestAllTypes message = TestUtil.getAllSet();
|
||||
|
||||
byte[] rawBytes = message.toByteArray();
|
||||
assertEqualBytes(TestUtil.getGoldenMessage().toByteArray(), rawBytes);
|
||||
|
||||
// Try different block sizes.
|
||||
for (int blockSize = 1; blockSize < 256; blockSize *= 2) {
|
||||
ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
|
||||
CodedOutputStream output =
|
||||
CodedOutputStream.newInstance(rawOutput, blockSize);
|
||||
message.writeTo(output);
|
||||
output.flush();
|
||||
assertEqualBytes(rawBytes, rawOutput.toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests writing a whole message with every packed field type. Ensures the
|
||||
* wire format of packed fields is compatible with C++. */
|
||||
public void testWriteWholePackedFieldsMessage() throws Exception {
|
||||
TestPackedTypes message = TestUtil.getPackedSet();
|
||||
|
||||
byte[] rawBytes = message.toByteArray();
|
||||
assertEqualBytes(TestUtil.getGoldenPackedFieldsMessage().toByteArray(),
|
||||
rawBytes);
|
||||
}
|
||||
|
||||
/** Test writing a message containing a negative enum value. This used to
|
||||
* fail because the size was not properly computed as a sign-extended varint.
|
||||
*/
|
||||
public void testWriteMessageWithNegativeEnumValue() throws Exception {
|
||||
SparseEnumMessage message = SparseEnumMessage.newBuilder()
|
||||
.setSparseEnum(TestSparseEnum.SPARSE_E) .build();
|
||||
assertTrue(message.getSparseEnum().getNumber() < 0);
|
||||
byte[] rawBytes = message.toByteArray();
|
||||
SparseEnumMessage message2 = SparseEnumMessage.parseFrom(rawBytes);
|
||||
assertEquals(TestSparseEnum.SPARSE_E, message2.getSparseEnum());
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,648 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.DescriptorProtos.DescriptorProto;
|
||||
import com.google.protobuf.DescriptorProtos.EnumDescriptorProto;
|
||||
import com.google.protobuf.DescriptorProtos.EnumValueDescriptorProto;
|
||||
import com.google.protobuf.DescriptorProtos.FieldDescriptorProto;
|
||||
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
|
||||
import com.google.protobuf.Descriptors.DescriptorValidationException;
|
||||
import com.google.protobuf.Descriptors.FileDescriptor;
|
||||
import com.google.protobuf.Descriptors.Descriptor;
|
||||
import com.google.protobuf.Descriptors.FieldDescriptor;
|
||||
import com.google.protobuf.Descriptors.EnumDescriptor;
|
||||
import com.google.protobuf.Descriptors.EnumValueDescriptor;
|
||||
import com.google.protobuf.Descriptors.ServiceDescriptor;
|
||||
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;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestExtremeDefaultValues;
|
||||
import protobuf_unittest.UnittestProto.TestRequired;
|
||||
import protobuf_unittest.UnittestProto.TestService;
|
||||
import protobuf_unittest.UnittestCustomOptions;
|
||||
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Unit test for {@link Descriptors}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class DescriptorsTest extends TestCase {
|
||||
|
||||
// Regression test for bug where referencing a FieldDescriptor.Type value
|
||||
// before a FieldDescriptorProto.Type value would yield a
|
||||
// ExceptionInInitializerError.
|
||||
@SuppressWarnings("unused")
|
||||
private static final Object STATIC_INIT_TEST = FieldDescriptor.Type.BOOL;
|
||||
|
||||
public void testFieldTypeEnumMapping() throws Exception {
|
||||
assertEquals(FieldDescriptor.Type.values().length,
|
||||
FieldDescriptorProto.Type.values().length);
|
||||
for (FieldDescriptor.Type type : FieldDescriptor.Type.values()) {
|
||||
FieldDescriptorProto.Type protoType = type.toProto();
|
||||
assertEquals("TYPE_" + type.name(), protoType.name());
|
||||
assertEquals(type, FieldDescriptor.Type.valueOf(protoType));
|
||||
}
|
||||
}
|
||||
|
||||
public void testFileDescriptor() throws Exception {
|
||||
FileDescriptor file = UnittestProto.getDescriptor();
|
||||
|
||||
assertEquals("google/protobuf/unittest.proto", file.getName());
|
||||
assertEquals("protobuf_unittest", file.getPackage());
|
||||
|
||||
assertEquals("UnittestProto", file.getOptions().getJavaOuterClassname());
|
||||
assertEquals("google/protobuf/unittest.proto",
|
||||
file.toProto().getName());
|
||||
|
||||
assertEquals(Arrays.asList(UnittestImport.getDescriptor()),
|
||||
file.getDependencies());
|
||||
|
||||
Descriptor messageType = TestAllTypes.getDescriptor();
|
||||
assertEquals(messageType, file.getMessageTypes().get(0));
|
||||
assertEquals(messageType, file.findMessageTypeByName("TestAllTypes"));
|
||||
assertNull(file.findMessageTypeByName("NoSuchType"));
|
||||
assertNull(file.findMessageTypeByName("protobuf_unittest.TestAllTypes"));
|
||||
for (int i = 0; i < file.getMessageTypes().size(); i++) {
|
||||
assertEquals(i, file.getMessageTypes().get(i).getIndex());
|
||||
}
|
||||
|
||||
EnumDescriptor enumType = ForeignEnum.getDescriptor();
|
||||
assertEquals(enumType, file.getEnumTypes().get(0));
|
||||
assertEquals(enumType, file.findEnumTypeByName("ForeignEnum"));
|
||||
assertNull(file.findEnumTypeByName("NoSuchType"));
|
||||
assertNull(file.findEnumTypeByName("protobuf_unittest.ForeignEnum"));
|
||||
assertEquals(Arrays.asList(ImportEnum.getDescriptor()),
|
||||
UnittestImport.getDescriptor().getEnumTypes());
|
||||
for (int i = 0; i < file.getEnumTypes().size(); i++) {
|
||||
assertEquals(i, file.getEnumTypes().get(i).getIndex());
|
||||
}
|
||||
|
||||
ServiceDescriptor service = TestService.getDescriptor();
|
||||
assertEquals(service, file.getServices().get(0));
|
||||
assertEquals(service, file.findServiceByName("TestService"));
|
||||
assertNull(file.findServiceByName("NoSuchType"));
|
||||
assertNull(file.findServiceByName("protobuf_unittest.TestService"));
|
||||
assertEquals(Collections.emptyList(),
|
||||
UnittestImport.getDescriptor().getServices());
|
||||
for (int i = 0; i < file.getServices().size(); i++) {
|
||||
assertEquals(i, file.getServices().get(i).getIndex());
|
||||
}
|
||||
|
||||
FieldDescriptor extension =
|
||||
UnittestProto.optionalInt32Extension.getDescriptor();
|
||||
assertEquals(extension, file.getExtensions().get(0));
|
||||
assertEquals(extension,
|
||||
file.findExtensionByName("optional_int32_extension"));
|
||||
assertNull(file.findExtensionByName("no_such_ext"));
|
||||
assertNull(file.findExtensionByName(
|
||||
"protobuf_unittest.optional_int32_extension"));
|
||||
assertEquals(Collections.emptyList(),
|
||||
UnittestImport.getDescriptor().getExtensions());
|
||||
for (int i = 0; i < file.getExtensions().size(); i++) {
|
||||
assertEquals(i, file.getExtensions().get(i).getIndex());
|
||||
}
|
||||
}
|
||||
|
||||
public void testDescriptor() throws Exception {
|
||||
Descriptor messageType = TestAllTypes.getDescriptor();
|
||||
Descriptor nestedType = TestAllTypes.NestedMessage.getDescriptor();
|
||||
|
||||
assertEquals("TestAllTypes", messageType.getName());
|
||||
assertEquals("protobuf_unittest.TestAllTypes", messageType.getFullName());
|
||||
assertEquals(UnittestProto.getDescriptor(), messageType.getFile());
|
||||
assertNull(messageType.getContainingType());
|
||||
assertEquals(DescriptorProtos.MessageOptions.getDefaultInstance(),
|
||||
messageType.getOptions());
|
||||
assertEquals("TestAllTypes", messageType.toProto().getName());
|
||||
|
||||
assertEquals("NestedMessage", nestedType.getName());
|
||||
assertEquals("protobuf_unittest.TestAllTypes.NestedMessage",
|
||||
nestedType.getFullName());
|
||||
assertEquals(UnittestProto.getDescriptor(), nestedType.getFile());
|
||||
assertEquals(messageType, nestedType.getContainingType());
|
||||
|
||||
FieldDescriptor field = messageType.getFields().get(0);
|
||||
assertEquals("optional_int32", field.getName());
|
||||
assertEquals(field, messageType.findFieldByName("optional_int32"));
|
||||
assertNull(messageType.findFieldByName("no_such_field"));
|
||||
assertEquals(field, messageType.findFieldByNumber(1));
|
||||
assertNull(messageType.findFieldByNumber(571283));
|
||||
for (int i = 0; i < messageType.getFields().size(); i++) {
|
||||
assertEquals(i, messageType.getFields().get(i).getIndex());
|
||||
}
|
||||
|
||||
assertEquals(nestedType, messageType.getNestedTypes().get(0));
|
||||
assertEquals(nestedType, messageType.findNestedTypeByName("NestedMessage"));
|
||||
assertNull(messageType.findNestedTypeByName("NoSuchType"));
|
||||
for (int i = 0; i < messageType.getNestedTypes().size(); i++) {
|
||||
assertEquals(i, messageType.getNestedTypes().get(i).getIndex());
|
||||
}
|
||||
|
||||
EnumDescriptor enumType = TestAllTypes.NestedEnum.getDescriptor();
|
||||
assertEquals(enumType, messageType.getEnumTypes().get(0));
|
||||
assertEquals(enumType, messageType.findEnumTypeByName("NestedEnum"));
|
||||
assertNull(messageType.findEnumTypeByName("NoSuchType"));
|
||||
for (int i = 0; i < messageType.getEnumTypes().size(); i++) {
|
||||
assertEquals(i, messageType.getEnumTypes().get(i).getIndex());
|
||||
}
|
||||
}
|
||||
|
||||
public void testFieldDescriptor() throws Exception {
|
||||
Descriptor messageType = TestAllTypes.getDescriptor();
|
||||
FieldDescriptor primitiveField =
|
||||
messageType.findFieldByName("optional_int32");
|
||||
FieldDescriptor enumField =
|
||||
messageType.findFieldByName("optional_nested_enum");
|
||||
FieldDescriptor messageField =
|
||||
messageType.findFieldByName("optional_foreign_message");
|
||||
FieldDescriptor cordField =
|
||||
messageType.findFieldByName("optional_cord");
|
||||
FieldDescriptor extension =
|
||||
UnittestProto.optionalInt32Extension.getDescriptor();
|
||||
FieldDescriptor nestedExtension = TestRequired.single.getDescriptor();
|
||||
|
||||
assertEquals("optional_int32", primitiveField.getName());
|
||||
assertEquals("protobuf_unittest.TestAllTypes.optional_int32",
|
||||
primitiveField.getFullName());
|
||||
assertEquals(1, primitiveField.getNumber());
|
||||
assertEquals(messageType, primitiveField.getContainingType());
|
||||
assertEquals(UnittestProto.getDescriptor(), primitiveField.getFile());
|
||||
assertEquals(FieldDescriptor.Type.INT32, primitiveField.getType());
|
||||
assertEquals(FieldDescriptor.JavaType.INT, primitiveField.getJavaType());
|
||||
assertEquals(DescriptorProtos.FieldOptions.getDefaultInstance(),
|
||||
primitiveField.getOptions());
|
||||
assertFalse(primitiveField.isExtension());
|
||||
assertEquals("optional_int32", primitiveField.toProto().getName());
|
||||
|
||||
assertEquals("optional_nested_enum", enumField.getName());
|
||||
assertEquals(FieldDescriptor.Type.ENUM, enumField.getType());
|
||||
assertEquals(FieldDescriptor.JavaType.ENUM, enumField.getJavaType());
|
||||
assertEquals(TestAllTypes.NestedEnum.getDescriptor(),
|
||||
enumField.getEnumType());
|
||||
|
||||
assertEquals("optional_foreign_message", messageField.getName());
|
||||
assertEquals(FieldDescriptor.Type.MESSAGE, messageField.getType());
|
||||
assertEquals(FieldDescriptor.JavaType.MESSAGE, messageField.getJavaType());
|
||||
assertEquals(ForeignMessage.getDescriptor(), messageField.getMessageType());
|
||||
|
||||
assertEquals("optional_cord", cordField.getName());
|
||||
assertEquals(FieldDescriptor.Type.STRING, cordField.getType());
|
||||
assertEquals(FieldDescriptor.JavaType.STRING, cordField.getJavaType());
|
||||
assertEquals(DescriptorProtos.FieldOptions.CType.CORD,
|
||||
cordField.getOptions().getCtype());
|
||||
|
||||
assertEquals("optional_int32_extension", extension.getName());
|
||||
assertEquals("protobuf_unittest.optional_int32_extension",
|
||||
extension.getFullName());
|
||||
assertEquals(1, extension.getNumber());
|
||||
assertEquals(TestAllExtensions.getDescriptor(),
|
||||
extension.getContainingType());
|
||||
assertEquals(UnittestProto.getDescriptor(), extension.getFile());
|
||||
assertEquals(FieldDescriptor.Type.INT32, extension.getType());
|
||||
assertEquals(FieldDescriptor.JavaType.INT, extension.getJavaType());
|
||||
assertEquals(DescriptorProtos.FieldOptions.getDefaultInstance(),
|
||||
extension.getOptions());
|
||||
assertTrue(extension.isExtension());
|
||||
assertEquals(null, extension.getExtensionScope());
|
||||
assertEquals("optional_int32_extension", extension.toProto().getName());
|
||||
|
||||
assertEquals("single", nestedExtension.getName());
|
||||
assertEquals("protobuf_unittest.TestRequired.single",
|
||||
nestedExtension.getFullName());
|
||||
assertEquals(TestRequired.getDescriptor(),
|
||||
nestedExtension.getExtensionScope());
|
||||
}
|
||||
|
||||
public void testFieldDescriptorLabel() throws Exception {
|
||||
FieldDescriptor requiredField =
|
||||
TestRequired.getDescriptor().findFieldByName("a");
|
||||
FieldDescriptor optionalField =
|
||||
TestAllTypes.getDescriptor().findFieldByName("optional_int32");
|
||||
FieldDescriptor repeatedField =
|
||||
TestAllTypes.getDescriptor().findFieldByName("repeated_int32");
|
||||
|
||||
assertTrue(requiredField.isRequired());
|
||||
assertFalse(requiredField.isRepeated());
|
||||
assertFalse(optionalField.isRequired());
|
||||
assertFalse(optionalField.isRepeated());
|
||||
assertFalse(repeatedField.isRequired());
|
||||
assertTrue(repeatedField.isRepeated());
|
||||
}
|
||||
|
||||
public void testFieldDescriptorDefault() throws Exception {
|
||||
Descriptor d = TestAllTypes.getDescriptor();
|
||||
assertFalse(d.findFieldByName("optional_int32").hasDefaultValue());
|
||||
assertEquals(0, d.findFieldByName("optional_int32").getDefaultValue());
|
||||
assertTrue(d.findFieldByName("default_int32").hasDefaultValue());
|
||||
assertEquals(41, d.findFieldByName("default_int32").getDefaultValue());
|
||||
|
||||
d = TestExtremeDefaultValues.getDescriptor();
|
||||
assertEquals(
|
||||
ByteString.copyFrom(
|
||||
"\0\001\007\b\f\n\r\t\013\\\'\"\u00fe".getBytes("ISO-8859-1")),
|
||||
d.findFieldByName("escaped_bytes").getDefaultValue());
|
||||
assertEquals(-1, d.findFieldByName("large_uint32").getDefaultValue());
|
||||
assertEquals(-1L, d.findFieldByName("large_uint64").getDefaultValue());
|
||||
}
|
||||
|
||||
public void testEnumDescriptor() throws Exception {
|
||||
EnumDescriptor enumType = ForeignEnum.getDescriptor();
|
||||
EnumDescriptor nestedType = TestAllTypes.NestedEnum.getDescriptor();
|
||||
|
||||
assertEquals("ForeignEnum", enumType.getName());
|
||||
assertEquals("protobuf_unittest.ForeignEnum", enumType.getFullName());
|
||||
assertEquals(UnittestProto.getDescriptor(), enumType.getFile());
|
||||
assertNull(enumType.getContainingType());
|
||||
assertEquals(DescriptorProtos.EnumOptions.getDefaultInstance(),
|
||||
enumType.getOptions());
|
||||
|
||||
assertEquals("NestedEnum", nestedType.getName());
|
||||
assertEquals("protobuf_unittest.TestAllTypes.NestedEnum",
|
||||
nestedType.getFullName());
|
||||
assertEquals(UnittestProto.getDescriptor(), nestedType.getFile());
|
||||
assertEquals(TestAllTypes.getDescriptor(), nestedType.getContainingType());
|
||||
|
||||
EnumValueDescriptor value = ForeignEnum.FOREIGN_FOO.getValueDescriptor();
|
||||
assertEquals(value, enumType.getValues().get(0));
|
||||
assertEquals("FOREIGN_FOO", value.getName());
|
||||
assertEquals(4, value.getNumber());
|
||||
assertEquals(value, enumType.findValueByName("FOREIGN_FOO"));
|
||||
assertEquals(value, enumType.findValueByNumber(4));
|
||||
assertNull(enumType.findValueByName("NO_SUCH_VALUE"));
|
||||
for (int i = 0; i < enumType.getValues().size(); i++) {
|
||||
assertEquals(i, enumType.getValues().get(i).getIndex());
|
||||
}
|
||||
}
|
||||
|
||||
public void testServiceDescriptor() throws Exception {
|
||||
ServiceDescriptor service = TestService.getDescriptor();
|
||||
|
||||
assertEquals("TestService", service.getName());
|
||||
assertEquals("protobuf_unittest.TestService", service.getFullName());
|
||||
assertEquals(UnittestProto.getDescriptor(), service.getFile());
|
||||
|
||||
assertEquals(2, service.getMethods().size());
|
||||
|
||||
MethodDescriptor fooMethod = service.getMethods().get(0);
|
||||
assertEquals("Foo", fooMethod.getName());
|
||||
assertEquals(UnittestProto.FooRequest.getDescriptor(),
|
||||
fooMethod.getInputType());
|
||||
assertEquals(UnittestProto.FooResponse.getDescriptor(),
|
||||
fooMethod.getOutputType());
|
||||
assertEquals(fooMethod, service.findMethodByName("Foo"));
|
||||
|
||||
MethodDescriptor barMethod = service.getMethods().get(1);
|
||||
assertEquals("Bar", barMethod.getName());
|
||||
assertEquals(UnittestProto.BarRequest.getDescriptor(),
|
||||
barMethod.getInputType());
|
||||
assertEquals(UnittestProto.BarResponse.getDescriptor(),
|
||||
barMethod.getOutputType());
|
||||
assertEquals(barMethod, service.findMethodByName("Bar"));
|
||||
|
||||
assertNull(service.findMethodByName("NoSuchMethod"));
|
||||
|
||||
for (int i = 0; i < service.getMethods().size(); i++) {
|
||||
assertEquals(i, service.getMethods().get(i).getIndex());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void testCustomOptions() throws Exception {
|
||||
Descriptor descriptor =
|
||||
UnittestCustomOptions.TestMessageWithCustomOptions.getDescriptor();
|
||||
|
||||
assertTrue(
|
||||
descriptor.getOptions().hasExtension(UnittestCustomOptions.messageOpt1));
|
||||
assertEquals(Integer.valueOf(-56),
|
||||
descriptor.getOptions().getExtension(UnittestCustomOptions.messageOpt1));
|
||||
|
||||
FieldDescriptor field = descriptor.findFieldByName("field1");
|
||||
assertNotNull(field);
|
||||
|
||||
assertTrue(
|
||||
field.getOptions().hasExtension(UnittestCustomOptions.fieldOpt1));
|
||||
assertEquals(Long.valueOf(8765432109L),
|
||||
field.getOptions().getExtension(UnittestCustomOptions.fieldOpt1));
|
||||
|
||||
EnumDescriptor enumType =
|
||||
UnittestCustomOptions.TestMessageWithCustomOptions.AnEnum.getDescriptor();
|
||||
|
||||
assertTrue(
|
||||
enumType.getOptions().hasExtension(UnittestCustomOptions.enumOpt1));
|
||||
assertEquals(Integer.valueOf(-789),
|
||||
enumType.getOptions().getExtension(UnittestCustomOptions.enumOpt1));
|
||||
|
||||
ServiceDescriptor service =
|
||||
UnittestCustomOptions.TestServiceWithCustomOptions.getDescriptor();
|
||||
|
||||
assertTrue(
|
||||
service.getOptions().hasExtension(UnittestCustomOptions.serviceOpt1));
|
||||
assertEquals(Long.valueOf(-9876543210L),
|
||||
service.getOptions().getExtension(UnittestCustomOptions.serviceOpt1));
|
||||
|
||||
MethodDescriptor method = service.findMethodByName("Foo");
|
||||
assertNotNull(method);
|
||||
|
||||
assertTrue(
|
||||
method.getOptions().hasExtension(UnittestCustomOptions.methodOpt1));
|
||||
assertEquals(UnittestCustomOptions.MethodOpt1.METHODOPT1_VAL2,
|
||||
method.getOptions().getExtension(UnittestCustomOptions.methodOpt1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the FieldDescriptor.Type enum is the same as the
|
||||
* WireFormat.FieldType enum.
|
||||
*/
|
||||
public void testFieldTypeTablesMatch() throws Exception {
|
||||
FieldDescriptor.Type[] values1 = FieldDescriptor.Type.values();
|
||||
WireFormat.FieldType[] values2 = WireFormat.FieldType.values();
|
||||
|
||||
assertEquals(values1.length, values2.length);
|
||||
|
||||
for (int i = 0; i < values1.length; i++) {
|
||||
assertEquals(values1[i].toString(), values2[i].toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the FieldDescriptor.JavaType enum is the same as the
|
||||
* WireFormat.JavaType enum.
|
||||
*/
|
||||
public void testJavaTypeTablesMatch() throws Exception {
|
||||
FieldDescriptor.JavaType[] values1 = FieldDescriptor.JavaType.values();
|
||||
WireFormat.JavaType[] values2 = WireFormat.JavaType.values();
|
||||
|
||||
assertEquals(values1.length, values2.length);
|
||||
|
||||
for (int i = 0; i < values1.length; i++) {
|
||||
assertEquals(values1[i].toString(), values2[i].toString());
|
||||
}
|
||||
}
|
||||
|
||||
public void testEnormousDescriptor() throws Exception {
|
||||
// The descriptor for this file is larger than 64k, yet it did not cause
|
||||
// a compiler error due to an over-long string literal.
|
||||
assertTrue(
|
||||
UnittestEnormousDescriptor.getDescriptor()
|
||||
.toProto().getSerializedSize() > 65536);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the DescriptorValidationException works as intended.
|
||||
*/
|
||||
public void testDescriptorValidatorException() throws Exception {
|
||||
FileDescriptorProto fileDescriptorProto = FileDescriptorProto.newBuilder()
|
||||
.setName("foo.proto")
|
||||
.addMessageType(DescriptorProto.newBuilder()
|
||||
.setName("Foo")
|
||||
.addField(FieldDescriptorProto.newBuilder()
|
||||
.setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
|
||||
.setType(FieldDescriptorProto.Type.TYPE_INT32)
|
||||
.setName("foo")
|
||||
.setNumber(1)
|
||||
.setDefaultValue("invalid")
|
||||
.build())
|
||||
.build())
|
||||
.build();
|
||||
try {
|
||||
Descriptors.FileDescriptor.buildFrom(fileDescriptorProto,
|
||||
new FileDescriptor[0]);
|
||||
fail("DescriptorValidationException expected");
|
||||
} catch (DescriptorValidationException e) {
|
||||
// Expected; check that the error message contains some useful hints
|
||||
assertTrue(e.getMessage().indexOf("foo") != -1);
|
||||
assertTrue(e.getMessage().indexOf("Foo") != -1);
|
||||
assertTrue(e.getMessage().indexOf("invalid") != -1);
|
||||
assertTrue(e.getCause() instanceof NumberFormatException);
|
||||
assertTrue(e.getCause().getMessage().indexOf("invalid") != -1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the translate/crosslink for an example where a message field's name
|
||||
* and type name are the same.
|
||||
*/
|
||||
public void testDescriptorComplexCrosslink() throws Exception {
|
||||
FileDescriptorProto fileDescriptorProto = FileDescriptorProto.newBuilder()
|
||||
.setName("foo.proto")
|
||||
.addMessageType(DescriptorProto.newBuilder()
|
||||
.setName("Foo")
|
||||
.addField(FieldDescriptorProto.newBuilder()
|
||||
.setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
|
||||
.setType(FieldDescriptorProto.Type.TYPE_INT32)
|
||||
.setName("foo")
|
||||
.setNumber(1)
|
||||
.build())
|
||||
.build())
|
||||
.addMessageType(DescriptorProto.newBuilder()
|
||||
.setName("Bar")
|
||||
.addField(FieldDescriptorProto.newBuilder()
|
||||
.setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
|
||||
.setTypeName("Foo")
|
||||
.setName("Foo")
|
||||
.setNumber(1)
|
||||
.build())
|
||||
.build())
|
||||
.build();
|
||||
// translate and crosslink
|
||||
FileDescriptor file =
|
||||
Descriptors.FileDescriptor.buildFrom(fileDescriptorProto,
|
||||
new FileDescriptor[0]);
|
||||
// verify resulting descriptors
|
||||
assertNotNull(file);
|
||||
List<Descriptor> msglist = file.getMessageTypes();
|
||||
assertNotNull(msglist);
|
||||
assertTrue(msglist.size() == 2);
|
||||
boolean barFound = false;
|
||||
for (Descriptor desc : msglist) {
|
||||
if (desc.getName().equals("Bar")) {
|
||||
barFound = true;
|
||||
assertNotNull(desc.getFields());
|
||||
List<FieldDescriptor> fieldlist = desc.getFields();
|
||||
assertNotNull(fieldlist);
|
||||
assertTrue(fieldlist.size() == 1);
|
||||
assertTrue(fieldlist.get(0).getType() == FieldDescriptor.Type.MESSAGE);
|
||||
assertTrue(fieldlist.get(0).getMessageType().getName().equals("Foo"));
|
||||
}
|
||||
}
|
||||
assertTrue(barFound);
|
||||
}
|
||||
|
||||
public void testInvalidPublicDependency() throws Exception {
|
||||
FileDescriptorProto fooProto = FileDescriptorProto.newBuilder()
|
||||
.setName("foo.proto") .build();
|
||||
FileDescriptorProto barProto = FileDescriptorProto.newBuilder()
|
||||
.setName("boo.proto")
|
||||
.addDependency("foo.proto")
|
||||
.addPublicDependency(1) // Error, should be 0.
|
||||
.build();
|
||||
FileDescriptor fooFile = Descriptors.FileDescriptor.buildFrom(fooProto,
|
||||
new FileDescriptor[0]);
|
||||
try {
|
||||
Descriptors.FileDescriptor.buildFrom(barProto,
|
||||
new FileDescriptor[] {fooFile});
|
||||
fail("DescriptorValidationException expected");
|
||||
} catch (DescriptorValidationException e) {
|
||||
assertTrue(
|
||||
e.getMessage().indexOf("Invalid public dependency index.") != -1);
|
||||
}
|
||||
}
|
||||
|
||||
public void testHiddenDependency() throws Exception {
|
||||
FileDescriptorProto barProto = FileDescriptorProto.newBuilder()
|
||||
.setName("bar.proto")
|
||||
.addMessageType(DescriptorProto.newBuilder().setName("Bar"))
|
||||
.build();
|
||||
FileDescriptorProto forwardProto = FileDescriptorProto.newBuilder()
|
||||
.setName("forward.proto")
|
||||
.addDependency("bar.proto")
|
||||
.build();
|
||||
FileDescriptorProto fooProto = FileDescriptorProto.newBuilder()
|
||||
.setName("foo.proto")
|
||||
.addDependency("forward.proto")
|
||||
.addMessageType(DescriptorProto.newBuilder()
|
||||
.setName("Foo")
|
||||
.addField(FieldDescriptorProto.newBuilder()
|
||||
.setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
|
||||
.setTypeName("Bar")
|
||||
.setName("bar")
|
||||
.setNumber(1)))
|
||||
.build();
|
||||
FileDescriptor barFile = Descriptors.FileDescriptor.buildFrom(
|
||||
barProto, new FileDescriptor[0]);
|
||||
FileDescriptor forwardFile = Descriptors.FileDescriptor.buildFrom(
|
||||
forwardProto, new FileDescriptor[] {barFile});
|
||||
|
||||
try {
|
||||
Descriptors.FileDescriptor.buildFrom(
|
||||
fooProto, new FileDescriptor[] {forwardFile});
|
||||
fail("DescriptorValidationException expected");
|
||||
} catch (DescriptorValidationException e) {
|
||||
assertTrue(e.getMessage().indexOf("Bar") != -1);
|
||||
assertTrue(e.getMessage().indexOf("is not defined") != -1);
|
||||
}
|
||||
}
|
||||
|
||||
public void testPublicDependency() throws Exception {
|
||||
FileDescriptorProto barProto = FileDescriptorProto.newBuilder()
|
||||
.setName("bar.proto")
|
||||
.addMessageType(DescriptorProto.newBuilder().setName("Bar"))
|
||||
.build();
|
||||
FileDescriptorProto forwardProto = FileDescriptorProto.newBuilder()
|
||||
.setName("forward.proto")
|
||||
.addDependency("bar.proto")
|
||||
.addPublicDependency(0)
|
||||
.build();
|
||||
FileDescriptorProto fooProto = FileDescriptorProto.newBuilder()
|
||||
.setName("foo.proto")
|
||||
.addDependency("forward.proto")
|
||||
.addMessageType(DescriptorProto.newBuilder()
|
||||
.setName("Foo")
|
||||
.addField(FieldDescriptorProto.newBuilder()
|
||||
.setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
|
||||
.setTypeName("Bar")
|
||||
.setName("bar")
|
||||
.setNumber(1)))
|
||||
.build();
|
||||
FileDescriptor barFile = Descriptors.FileDescriptor.buildFrom(
|
||||
barProto, new FileDescriptor[0]);
|
||||
FileDescriptor forwardFile = Descriptors.FileDescriptor.buildFrom(
|
||||
forwardProto, new FileDescriptor[]{barFile});
|
||||
Descriptors.FileDescriptor.buildFrom(
|
||||
fooProto, new FileDescriptor[] {forwardFile});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the translate/crosslink for an example with a more complex namespace
|
||||
* referencing.
|
||||
*/
|
||||
public void testComplexNamespacePublicDependency() throws Exception {
|
||||
FileDescriptorProto fooProto = FileDescriptorProto.newBuilder()
|
||||
.setName("bar.proto")
|
||||
.setPackage("a.b.c.d.bar.shared")
|
||||
.addEnumType(EnumDescriptorProto.newBuilder()
|
||||
.setName("MyEnum")
|
||||
.addValue(EnumValueDescriptorProto.newBuilder()
|
||||
.setName("BLAH")
|
||||
.setNumber(1)))
|
||||
.build();
|
||||
FileDescriptorProto barProto = FileDescriptorProto.newBuilder()
|
||||
.setName("foo.proto")
|
||||
.addDependency("bar.proto")
|
||||
.setPackage("a.b.c.d.foo.shared")
|
||||
.addMessageType(DescriptorProto.newBuilder()
|
||||
.setName("MyMessage")
|
||||
.addField(FieldDescriptorProto.newBuilder()
|
||||
.setLabel(FieldDescriptorProto.Label.LABEL_REPEATED)
|
||||
.setTypeName("bar.shared.MyEnum")
|
||||
.setName("MyField")
|
||||
.setNumber(1)))
|
||||
.build();
|
||||
// translate and crosslink
|
||||
FileDescriptor fooFile = Descriptors.FileDescriptor.buildFrom(
|
||||
fooProto, new FileDescriptor[0]);
|
||||
FileDescriptor barFile = Descriptors.FileDescriptor.buildFrom(
|
||||
barProto, new FileDescriptor[]{fooFile});
|
||||
// verify resulting descriptors
|
||||
assertNotNull(barFile);
|
||||
List<Descriptor> msglist = barFile.getMessageTypes();
|
||||
assertNotNull(msglist);
|
||||
assertTrue(msglist.size() == 1);
|
||||
Descriptor desc = msglist.get(0);
|
||||
if (desc.getName().equals("MyMessage")) {
|
||||
assertNotNull(desc.getFields());
|
||||
List<FieldDescriptor> fieldlist = desc.getFields();
|
||||
assertNotNull(fieldlist);
|
||||
assertTrue(fieldlist.size() == 1);
|
||||
FieldDescriptor field = fieldlist.get(0);
|
||||
assertTrue(field.getType() == FieldDescriptor.Type.ENUM);
|
||||
assertTrue(field.getEnumType().getName().equals("MyEnum"));
|
||||
assertTrue(field.getEnumType().getFile().getName().equals("bar.proto"));
|
||||
assertTrue(field.getEnumType().getFile().getPackage().equals(
|
||||
"a.b.c.d.bar.shared"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,264 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestEmptyMessage;
|
||||
import protobuf_unittest.UnittestProto.TestPackedTypes;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Unit test for {@link DynamicMessage}. See also {@link MessageTest}, which
|
||||
* tests some {@link DynamicMessage} functionality.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class DynamicMessageTest extends TestCase {
|
||||
TestUtil.ReflectionTester reflectionTester =
|
||||
new TestUtil.ReflectionTester(TestAllTypes.getDescriptor(), null);
|
||||
|
||||
TestUtil.ReflectionTester extensionsReflectionTester =
|
||||
new TestUtil.ReflectionTester(TestAllExtensions.getDescriptor(),
|
||||
TestUtil.getExtensionRegistry());
|
||||
TestUtil.ReflectionTester packedReflectionTester =
|
||||
new TestUtil.ReflectionTester(TestPackedTypes.getDescriptor(), null);
|
||||
|
||||
public void testDynamicMessageAccessors() throws Exception {
|
||||
Message.Builder builder =
|
||||
DynamicMessage.newBuilder(TestAllTypes.getDescriptor());
|
||||
reflectionTester.setAllFieldsViaReflection(builder);
|
||||
Message message = builder.build();
|
||||
reflectionTester.assertAllFieldsSetViaReflection(message);
|
||||
}
|
||||
|
||||
public void testSettersAfterBuild() throws Exception {
|
||||
Message.Builder builder =
|
||||
DynamicMessage.newBuilder(TestAllTypes.getDescriptor());
|
||||
Message firstMessage = builder.build();
|
||||
// double build()
|
||||
builder.build();
|
||||
// clear() after build()
|
||||
builder.clear();
|
||||
// setters after build()
|
||||
reflectionTester.setAllFieldsViaReflection(builder);
|
||||
Message message = builder.build();
|
||||
reflectionTester.assertAllFieldsSetViaReflection(message);
|
||||
// repeated setters after build()
|
||||
reflectionTester.modifyRepeatedFieldsViaReflection(builder);
|
||||
message = builder.build();
|
||||
reflectionTester.assertRepeatedFieldsModifiedViaReflection(message);
|
||||
// firstMessage shouldn't have been modified.
|
||||
reflectionTester.assertClearViaReflection(firstMessage);
|
||||
}
|
||||
|
||||
public void testUnknownFields() throws Exception {
|
||||
Message.Builder builder =
|
||||
DynamicMessage.newBuilder(TestEmptyMessage.getDescriptor());
|
||||
builder.setUnknownFields(UnknownFieldSet.newBuilder()
|
||||
.addField(1, UnknownFieldSet.Field.newBuilder().addVarint(1).build())
|
||||
.addField(2, UnknownFieldSet.Field.newBuilder().addFixed32(1).build())
|
||||
.build());
|
||||
Message message = builder.build();
|
||||
assertEquals(2, message.getUnknownFields().asMap().size());
|
||||
// clone() with unknown fields
|
||||
Message.Builder newBuilder = builder.clone();
|
||||
assertEquals(2, newBuilder.getUnknownFields().asMap().size());
|
||||
// clear() with unknown fields
|
||||
newBuilder.clear();
|
||||
assertTrue(newBuilder.getUnknownFields().asMap().isEmpty());
|
||||
// serialize/parse with unknown fields
|
||||
newBuilder.mergeFrom(message.toByteString());
|
||||
assertEquals(2, newBuilder.getUnknownFields().asMap().size());
|
||||
}
|
||||
|
||||
public void testDynamicMessageSettersRejectNull() throws Exception {
|
||||
Message.Builder builder =
|
||||
DynamicMessage.newBuilder(TestAllTypes.getDescriptor());
|
||||
reflectionTester.assertReflectionSettersRejectNull(builder);
|
||||
}
|
||||
|
||||
public void testDynamicMessageExtensionAccessors() throws Exception {
|
||||
// We don't need to extensively test DynamicMessage's handling of
|
||||
// extensions because, frankly, it doesn't do anything special with them.
|
||||
// It treats them just like any other fields.
|
||||
Message.Builder builder =
|
||||
DynamicMessage.newBuilder(TestAllExtensions.getDescriptor());
|
||||
extensionsReflectionTester.setAllFieldsViaReflection(builder);
|
||||
Message message = builder.build();
|
||||
extensionsReflectionTester.assertAllFieldsSetViaReflection(message);
|
||||
}
|
||||
|
||||
public void testDynamicMessageExtensionSettersRejectNull() throws Exception {
|
||||
Message.Builder builder =
|
||||
DynamicMessage.newBuilder(TestAllExtensions.getDescriptor());
|
||||
extensionsReflectionTester.assertReflectionSettersRejectNull(builder);
|
||||
}
|
||||
|
||||
public void testDynamicMessageRepeatedSetters() throws Exception {
|
||||
Message.Builder builder =
|
||||
DynamicMessage.newBuilder(TestAllTypes.getDescriptor());
|
||||
reflectionTester.setAllFieldsViaReflection(builder);
|
||||
reflectionTester.modifyRepeatedFieldsViaReflection(builder);
|
||||
Message message = builder.build();
|
||||
reflectionTester.assertRepeatedFieldsModifiedViaReflection(message);
|
||||
}
|
||||
|
||||
public void testDynamicMessageRepeatedSettersRejectNull() throws Exception {
|
||||
Message.Builder builder =
|
||||
DynamicMessage.newBuilder(TestAllTypes.getDescriptor());
|
||||
reflectionTester.assertReflectionRepeatedSettersRejectNull(builder);
|
||||
}
|
||||
|
||||
public void testDynamicMessageDefaults() throws Exception {
|
||||
reflectionTester.assertClearViaReflection(
|
||||
DynamicMessage.getDefaultInstance(TestAllTypes.getDescriptor()));
|
||||
reflectionTester.assertClearViaReflection(
|
||||
DynamicMessage.newBuilder(TestAllTypes.getDescriptor()).build());
|
||||
}
|
||||
|
||||
public void testDynamicMessageSerializedSize() throws Exception {
|
||||
TestAllTypes message = TestUtil.getAllSet();
|
||||
|
||||
Message.Builder dynamicBuilder =
|
||||
DynamicMessage.newBuilder(TestAllTypes.getDescriptor());
|
||||
reflectionTester.setAllFieldsViaReflection(dynamicBuilder);
|
||||
Message dynamicMessage = dynamicBuilder.build();
|
||||
|
||||
assertEquals(message.getSerializedSize(),
|
||||
dynamicMessage.getSerializedSize());
|
||||
}
|
||||
|
||||
public void testDynamicMessageSerialization() throws Exception {
|
||||
Message.Builder builder =
|
||||
DynamicMessage.newBuilder(TestAllTypes.getDescriptor());
|
||||
reflectionTester.setAllFieldsViaReflection(builder);
|
||||
Message message = builder.build();
|
||||
|
||||
ByteString rawBytes = message.toByteString();
|
||||
TestAllTypes message2 = TestAllTypes.parseFrom(rawBytes);
|
||||
|
||||
TestUtil.assertAllFieldsSet(message2);
|
||||
|
||||
// In fact, the serialized forms should be exactly the same, byte-for-byte.
|
||||
assertEquals(TestUtil.getAllSet().toByteString(), rawBytes);
|
||||
}
|
||||
|
||||
public void testDynamicMessageParsing() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TestUtil.setAllFields(builder);
|
||||
TestAllTypes message = builder.build();
|
||||
|
||||
ByteString rawBytes = message.toByteString();
|
||||
|
||||
Message message2 =
|
||||
DynamicMessage.parseFrom(TestAllTypes.getDescriptor(), rawBytes);
|
||||
reflectionTester.assertAllFieldsSetViaReflection(message2);
|
||||
|
||||
// Test Parser interface.
|
||||
Message message3 = message2.getParserForType().parseFrom(rawBytes);
|
||||
reflectionTester.assertAllFieldsSetViaReflection(message3);
|
||||
}
|
||||
|
||||
public void testDynamicMessageExtensionParsing() throws Exception {
|
||||
ByteString rawBytes = TestUtil.getAllExtensionsSet().toByteString();
|
||||
Message message = DynamicMessage.parseFrom(
|
||||
TestAllExtensions.getDescriptor(), rawBytes,
|
||||
TestUtil.getExtensionRegistry());
|
||||
extensionsReflectionTester.assertAllFieldsSetViaReflection(message);
|
||||
|
||||
// Test Parser interface.
|
||||
Message message2 = message.getParserForType().parseFrom(
|
||||
rawBytes, TestUtil.getExtensionRegistry());
|
||||
extensionsReflectionTester.assertAllFieldsSetViaReflection(message2);
|
||||
}
|
||||
|
||||
public void testDynamicMessagePackedSerialization() throws Exception {
|
||||
Message.Builder builder =
|
||||
DynamicMessage.newBuilder(TestPackedTypes.getDescriptor());
|
||||
packedReflectionTester.setPackedFieldsViaReflection(builder);
|
||||
Message message = builder.build();
|
||||
|
||||
ByteString rawBytes = message.toByteString();
|
||||
TestPackedTypes message2 = TestPackedTypes.parseFrom(rawBytes);
|
||||
|
||||
TestUtil.assertPackedFieldsSet(message2);
|
||||
|
||||
// In fact, the serialized forms should be exactly the same, byte-for-byte.
|
||||
assertEquals(TestUtil.getPackedSet().toByteString(), rawBytes);
|
||||
}
|
||||
|
||||
public void testDynamicMessagePackedParsing() throws Exception {
|
||||
TestPackedTypes.Builder builder = TestPackedTypes.newBuilder();
|
||||
TestUtil.setPackedFields(builder);
|
||||
TestPackedTypes message = builder.build();
|
||||
|
||||
ByteString rawBytes = message.toByteString();
|
||||
|
||||
Message message2 =
|
||||
DynamicMessage.parseFrom(TestPackedTypes.getDescriptor(), rawBytes);
|
||||
packedReflectionTester.assertPackedFieldsSetViaReflection(message2);
|
||||
|
||||
// Test Parser interface.
|
||||
Message message3 = message2.getParserForType().parseFrom(rawBytes);
|
||||
packedReflectionTester.assertPackedFieldsSetViaReflection(message3);
|
||||
}
|
||||
|
||||
public void testDynamicMessageCopy() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TestUtil.setAllFields(builder);
|
||||
TestAllTypes message = builder.build();
|
||||
|
||||
DynamicMessage copy = DynamicMessage.newBuilder(message).build();
|
||||
reflectionTester.assertAllFieldsSetViaReflection(copy);
|
||||
}
|
||||
|
||||
public void testToBuilder() throws Exception {
|
||||
DynamicMessage.Builder builder =
|
||||
DynamicMessage.newBuilder(TestAllTypes.getDescriptor());
|
||||
reflectionTester.setAllFieldsViaReflection(builder);
|
||||
int unknownFieldNum = 9;
|
||||
long unknownFieldVal = 90;
|
||||
builder.setUnknownFields(UnknownFieldSet.newBuilder()
|
||||
.addField(unknownFieldNum,
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addVarint(unknownFieldVal).build())
|
||||
.build());
|
||||
DynamicMessage message = builder.build();
|
||||
|
||||
DynamicMessage derived = message.toBuilder().build();
|
||||
reflectionTester.assertAllFieldsSetViaReflection(derived);
|
||||
assertEquals(Arrays.asList(unknownFieldVal),
|
||||
derived.getUnknownFields().getField(unknownFieldNum).getVarintList());
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
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 (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void run() {
|
||||
GeneratedMessage.enableAlwaysUseFieldBuildersForTesting();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,180 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.IsValidUtf8TestUtil.Shard;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* Tests cases for {@link ByteString#isValidUtf8()}. This includes three
|
||||
* brute force tests that actually test every permutation of one byte, two byte,
|
||||
* and three byte sequences to ensure that the method produces the right result
|
||||
* for every possible byte encoding where "right" means it's consistent with
|
||||
* java's UTF-8 string encoding/decoding such that the method returns true for
|
||||
* any sequence that will round trip when converted to a String and then back to
|
||||
* bytes and will return false for any sequence that will not round trip.
|
||||
* See also {@link IsValidUtf8FourByteTest}. It also includes some
|
||||
* other more targeted tests.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
* @author martinrb@google.com (Martin Buchholz)
|
||||
*/
|
||||
public class IsValidUtf8Test extends TestCase {
|
||||
|
||||
/**
|
||||
* Tests that round tripping of all two byte permutations work.
|
||||
*/
|
||||
public void testIsValidUtf8_1Byte() throws UnsupportedEncodingException {
|
||||
IsValidUtf8TestUtil.testBytes(1,
|
||||
IsValidUtf8TestUtil.EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that round tripping of all two byte permutations work.
|
||||
*/
|
||||
public void testIsValidUtf8_2Bytes() throws UnsupportedEncodingException {
|
||||
IsValidUtf8TestUtil.testBytes(2,
|
||||
IsValidUtf8TestUtil.EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that round tripping of all three byte permutations work.
|
||||
*/
|
||||
public void testIsValidUtf8_3Bytes() throws UnsupportedEncodingException {
|
||||
IsValidUtf8TestUtil.testBytes(3,
|
||||
IsValidUtf8TestUtil.EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that round tripping of a sample of four byte permutations work.
|
||||
* All permutations are prohibitively expensive to test for automated runs;
|
||||
* {@link IsValidUtf8FourByteTest} is used for full coverage. This method
|
||||
* tests specific four-byte cases.
|
||||
*/
|
||||
public void testIsValidUtf8_4BytesSamples()
|
||||
throws UnsupportedEncodingException {
|
||||
// Valid 4 byte.
|
||||
assertValidUtf8(0xF0, 0xA4, 0xAD, 0xA2);
|
||||
|
||||
// Bad trailing bytes
|
||||
assertInvalidUtf8(0xF0, 0xA4, 0xAD, 0x7F);
|
||||
assertInvalidUtf8(0xF0, 0xA4, 0xAD, 0xC0);
|
||||
|
||||
// Special cases for byte2
|
||||
assertInvalidUtf8(0xF0, 0x8F, 0xAD, 0xA2);
|
||||
assertInvalidUtf8(0xF4, 0x90, 0xAD, 0xA2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests some hard-coded test cases.
|
||||
*/
|
||||
public void testSomeSequences() {
|
||||
// Empty
|
||||
assertTrue(asBytes("").isValidUtf8());
|
||||
|
||||
// One-byte characters, including control characters
|
||||
assertTrue(asBytes("\u0000abc\u007f").isValidUtf8());
|
||||
|
||||
// Two-byte characters
|
||||
assertTrue(asBytes("\u00a2\u00a2").isValidUtf8());
|
||||
|
||||
// Three-byte characters
|
||||
assertTrue(asBytes("\u020ac\u020ac").isValidUtf8());
|
||||
|
||||
// Four-byte characters
|
||||
assertTrue(asBytes("\u024B62\u024B62").isValidUtf8());
|
||||
|
||||
// Mixed string
|
||||
assertTrue(
|
||||
asBytes("a\u020ac\u00a2b\\u024B62u020acc\u00a2de\u024B62")
|
||||
.isValidUtf8());
|
||||
|
||||
// Not a valid string
|
||||
assertInvalidUtf8(-1, 0, -1, 0);
|
||||
}
|
||||
|
||||
private byte[] toByteArray(int... bytes) {
|
||||
byte[] realBytes = new byte[bytes.length];
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
realBytes[i] = (byte) bytes[i];
|
||||
}
|
||||
return realBytes;
|
||||
}
|
||||
|
||||
private ByteString toByteString(int... bytes) {
|
||||
return ByteString.copyFrom(toByteArray(bytes));
|
||||
}
|
||||
|
||||
private void assertValidUtf8(int[] bytes, boolean not) {
|
||||
byte[] realBytes = toByteArray(bytes);
|
||||
assertTrue(not ^ Utf8.isValidUtf8(realBytes));
|
||||
assertTrue(not ^ Utf8.isValidUtf8(realBytes, 0, bytes.length));
|
||||
ByteString lit = ByteString.copyFrom(realBytes);
|
||||
ByteString sub = lit.substring(0, bytes.length);
|
||||
assertTrue(not ^ lit.isValidUtf8());
|
||||
assertTrue(not ^ sub.isValidUtf8());
|
||||
ByteString[] ropes = {
|
||||
RopeByteString.newInstanceForTest(ByteString.EMPTY, lit),
|
||||
RopeByteString.newInstanceForTest(ByteString.EMPTY, sub),
|
||||
RopeByteString.newInstanceForTest(lit, ByteString.EMPTY),
|
||||
RopeByteString.newInstanceForTest(sub, ByteString.EMPTY),
|
||||
RopeByteString.newInstanceForTest(sub, lit)
|
||||
};
|
||||
for (ByteString rope : ropes) {
|
||||
assertTrue(not ^ rope.isValidUtf8());
|
||||
}
|
||||
}
|
||||
|
||||
private void assertValidUtf8(int... bytes) {
|
||||
assertValidUtf8(bytes, false);
|
||||
}
|
||||
|
||||
private void assertInvalidUtf8(int... bytes) {
|
||||
assertValidUtf8(bytes, true);
|
||||
}
|
||||
|
||||
private static ByteString asBytes(String s) {
|
||||
return ByteString.copyFromUtf8(s);
|
||||
}
|
||||
|
||||
public void testShardsHaveExpectedRoundTrippables() {
|
||||
// A sanity check.
|
||||
int actual = 0;
|
||||
for (Shard shard : IsValidUtf8TestUtil.FOUR_BYTE_SHARDS) {
|
||||
actual += shard.expected;
|
||||
}
|
||||
assertEquals(IsValidUtf8TestUtil.EXPECTED_FOUR_BYTE_ROUNDTRIPPABLE_COUNT,
|
||||
actual);
|
||||
}
|
||||
}
|
||||
@@ -1,421 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import static junit.framework.Assert.*;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.logging.Logger;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CodingErrorAction;
|
||||
import java.nio.charset.CharsetEncoder;
|
||||
import java.nio.charset.CoderResult;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
|
||||
/**
|
||||
* Shared testing code for {@link IsValidUtf8Test} and
|
||||
* {@link IsValidUtf8FourByteTest}.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
* @author martinrb@google.com (Martin Buchholz)
|
||||
*/
|
||||
class IsValidUtf8TestUtil {
|
||||
private static Logger logger = Logger.getLogger(
|
||||
IsValidUtf8TestUtil.class.getName());
|
||||
|
||||
// 128 - [chars 0x0000 to 0x007f]
|
||||
static long ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x007f - 0x0000 + 1;
|
||||
|
||||
// 128
|
||||
static long EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT =
|
||||
ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS;
|
||||
|
||||
// 1920 [chars 0x0080 to 0x07FF]
|
||||
static long TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x07FF - 0x0080 + 1;
|
||||
|
||||
// 18,304
|
||||
static long EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT =
|
||||
// Both bytes are one byte characters
|
||||
(long) Math.pow(EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT, 2) +
|
||||
// The possible number of two byte characters
|
||||
TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS;
|
||||
|
||||
// 2048
|
||||
static long THREE_BYTE_SURROGATES = 2 * 1024;
|
||||
|
||||
// 61,440 [chars 0x0800 to 0xFFFF, minus surrogates]
|
||||
static long THREE_BYTE_ROUNDTRIPPABLE_CHARACTERS =
|
||||
0xFFFF - 0x0800 + 1 - THREE_BYTE_SURROGATES;
|
||||
|
||||
// 2,650,112
|
||||
static long EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT =
|
||||
// All one byte characters
|
||||
(long) Math.pow(EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT, 3) +
|
||||
// One two byte character and a one byte character
|
||||
2 * TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS *
|
||||
ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS +
|
||||
// Three byte characters
|
||||
THREE_BYTE_ROUNDTRIPPABLE_CHARACTERS;
|
||||
|
||||
// 1,048,576 [chars 0x10000L to 0x10FFFF]
|
||||
static long FOUR_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x10FFFF - 0x10000L + 1;
|
||||
|
||||
// 289,571,839
|
||||
static long EXPECTED_FOUR_BYTE_ROUNDTRIPPABLE_COUNT =
|
||||
// All one byte characters
|
||||
(long) Math.pow(EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT, 4) +
|
||||
// One and three byte characters
|
||||
2 * THREE_BYTE_ROUNDTRIPPABLE_CHARACTERS *
|
||||
ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS +
|
||||
// Two two byte characters
|
||||
TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS * TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS +
|
||||
// Permutations of one and two byte characters
|
||||
3 * TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS *
|
||||
ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS *
|
||||
ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS +
|
||||
// Four byte characters
|
||||
FOUR_BYTE_ROUNDTRIPPABLE_CHARACTERS;
|
||||
|
||||
static class Shard {
|
||||
final long index;
|
||||
final long start;
|
||||
final long lim;
|
||||
final long expected;
|
||||
|
||||
|
||||
public Shard(long index, long start, long lim, long expected) {
|
||||
assertTrue(start < lim);
|
||||
this.index = index;
|
||||
this.start = start;
|
||||
this.lim = lim;
|
||||
this.expected = expected;
|
||||
}
|
||||
}
|
||||
|
||||
static final long[] FOUR_BYTE_SHARDS_EXPECTED_ROUNTRIPPABLES =
|
||||
generateFourByteShardsExpectedRunnables();
|
||||
|
||||
private static long[] generateFourByteShardsExpectedRunnables() {
|
||||
long[] expected = new long[128];
|
||||
|
||||
// 0-63 are all 5300224
|
||||
for (int i = 0; i <= 63; i++) {
|
||||
expected[i] = 5300224;
|
||||
}
|
||||
|
||||
// 97-111 are all 2342912
|
||||
for (int i = 97; i <= 111; i++) {
|
||||
expected[i] = 2342912;
|
||||
}
|
||||
|
||||
// 113-117 are all 1048576
|
||||
for (int i = 113; i <= 117; i++) {
|
||||
expected[i] = 1048576;
|
||||
}
|
||||
|
||||
// One offs
|
||||
expected[112] = 786432;
|
||||
expected[118] = 786432;
|
||||
expected[119] = 1048576;
|
||||
expected[120] = 458752;
|
||||
expected[121] = 524288;
|
||||
expected[122] = 65536;
|
||||
|
||||
// Anything not assigned was the default 0.
|
||||
return expected;
|
||||
}
|
||||
|
||||
static final List<Shard> FOUR_BYTE_SHARDS = generateFourByteShards(
|
||||
128, FOUR_BYTE_SHARDS_EXPECTED_ROUNTRIPPABLES);
|
||||
|
||||
|
||||
private static List<Shard> generateFourByteShards(
|
||||
int numShards, long[] expected) {
|
||||
assertEquals(numShards, expected.length);
|
||||
List<Shard> shards = new ArrayList<Shard>(numShards);
|
||||
long LIM = 1L << 32;
|
||||
long increment = LIM / numShards;
|
||||
assertTrue(LIM % numShards == 0);
|
||||
for (int i = 0; i < numShards; i++) {
|
||||
shards.add(new Shard(i,
|
||||
increment * i,
|
||||
increment * (i + 1),
|
||||
expected[i]));
|
||||
}
|
||||
return shards;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to run the loop to test all the permutations for the number of bytes
|
||||
* specified.
|
||||
*
|
||||
* @param numBytes the number of bytes in the byte array
|
||||
* @param expectedCount the expected number of roundtrippable permutations
|
||||
*/
|
||||
static void testBytes(int numBytes, long expectedCount)
|
||||
throws UnsupportedEncodingException {
|
||||
testBytes(numBytes, expectedCount, 0, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to run the loop to test all the permutations for the number of bytes
|
||||
* specified. This overload is useful for debugging to get the loop to start
|
||||
* at a certain character.
|
||||
*
|
||||
* @param numBytes the number of bytes in the byte array
|
||||
* @param expectedCount the expected number of roundtrippable permutations
|
||||
* @param start the starting bytes encoded as a long as big-endian
|
||||
* @param lim the limit of bytes to process encoded as a long as big-endian,
|
||||
* or -1 to mean the max limit for numBytes
|
||||
*/
|
||||
static void testBytes(int numBytes, long expectedCount, long start, long lim)
|
||||
throws UnsupportedEncodingException {
|
||||
Random rnd = new Random();
|
||||
byte[] bytes = new byte[numBytes];
|
||||
|
||||
if (lim == -1) {
|
||||
lim = 1L << (numBytes * 8);
|
||||
}
|
||||
long count = 0;
|
||||
long countRoundTripped = 0;
|
||||
for (long byteChar = start; byteChar < lim; byteChar++) {
|
||||
long tmpByteChar = byteChar;
|
||||
for (int i = 0; i < numBytes; i++) {
|
||||
bytes[bytes.length - i - 1] = (byte) tmpByteChar;
|
||||
tmpByteChar = tmpByteChar >> 8;
|
||||
}
|
||||
ByteString bs = ByteString.copyFrom(bytes);
|
||||
boolean isRoundTrippable = bs.isValidUtf8();
|
||||
String s = new String(bytes, "UTF-8");
|
||||
byte[] bytesReencoded = s.getBytes("UTF-8");
|
||||
boolean bytesEqual = Arrays.equals(bytes, bytesReencoded);
|
||||
|
||||
if (bytesEqual != isRoundTrippable) {
|
||||
outputFailure(byteChar, bytes, bytesReencoded);
|
||||
}
|
||||
|
||||
// Check agreement with static Utf8 methods.
|
||||
assertEquals(isRoundTrippable, Utf8.isValidUtf8(bytes));
|
||||
assertEquals(isRoundTrippable, Utf8.isValidUtf8(bytes, 0, numBytes));
|
||||
|
||||
// Test partial sequences.
|
||||
// Partition numBytes into three segments (not necessarily non-empty).
|
||||
int i = rnd.nextInt(numBytes);
|
||||
int j = rnd.nextInt(numBytes);
|
||||
if (j < i) {
|
||||
int tmp = i; i = j; j = tmp;
|
||||
}
|
||||
int state1 = Utf8.partialIsValidUtf8(Utf8.COMPLETE, bytes, 0, i);
|
||||
int state2 = Utf8.partialIsValidUtf8(state1, bytes, i, j);
|
||||
int state3 = Utf8.partialIsValidUtf8(state2, bytes, j, numBytes);
|
||||
if (isRoundTrippable != (state3 == Utf8.COMPLETE)) {
|
||||
System.out.printf("state=%04x %04x %04x i=%d j=%d%n",
|
||||
state1, state2, state3, i, j);
|
||||
outputFailure(byteChar, bytes, bytesReencoded);
|
||||
}
|
||||
assertEquals(isRoundTrippable, (state3 == Utf8.COMPLETE));
|
||||
|
||||
// Test ropes built out of small partial sequences
|
||||
ByteString rope = RopeByteString.newInstanceForTest(
|
||||
bs.substring(0, i),
|
||||
RopeByteString.newInstanceForTest(
|
||||
bs.substring(i, j),
|
||||
bs.substring(j, numBytes)));
|
||||
assertSame(RopeByteString.class, rope.getClass());
|
||||
|
||||
ByteString[] byteStrings = { bs, bs.substring(0, numBytes), rope };
|
||||
for (ByteString x : byteStrings) {
|
||||
assertEquals(isRoundTrippable,
|
||||
x.isValidUtf8());
|
||||
assertEquals(state3,
|
||||
x.partialIsValidUtf8(Utf8.COMPLETE, 0, numBytes));
|
||||
|
||||
assertEquals(state1,
|
||||
x.partialIsValidUtf8(Utf8.COMPLETE, 0, i));
|
||||
assertEquals(state1,
|
||||
x.substring(0, i).partialIsValidUtf8(Utf8.COMPLETE, 0, i));
|
||||
assertEquals(state2,
|
||||
x.partialIsValidUtf8(state1, i, j - i));
|
||||
assertEquals(state2,
|
||||
x.substring(i, j).partialIsValidUtf8(state1, 0, j - i));
|
||||
assertEquals(state3,
|
||||
x.partialIsValidUtf8(state2, j, numBytes - j));
|
||||
assertEquals(state3,
|
||||
x.substring(j, numBytes)
|
||||
.partialIsValidUtf8(state2, 0, numBytes - j));
|
||||
}
|
||||
|
||||
// ByteString reduplication should not affect its UTF-8 validity.
|
||||
ByteString ropeADope =
|
||||
RopeByteString.newInstanceForTest(bs, bs.substring(0, numBytes));
|
||||
assertEquals(isRoundTrippable, ropeADope.isValidUtf8());
|
||||
|
||||
if (isRoundTrippable) {
|
||||
countRoundTripped++;
|
||||
}
|
||||
count++;
|
||||
if (byteChar != 0 && byteChar % 1000000L == 0) {
|
||||
logger.info("Processed " + (byteChar / 1000000L) +
|
||||
" million characters");
|
||||
}
|
||||
}
|
||||
logger.info("Round tripped " + countRoundTripped + " of " + count);
|
||||
assertEquals(expectedCount, countRoundTripped);
|
||||
}
|
||||
|
||||
/**
|
||||
* Variation of {@link #testBytes} that does less allocation using the
|
||||
* low-level encoders/decoders directly. Checked in because it's useful for
|
||||
* debugging when trying to process bytes faster, but since it doesn't use the
|
||||
* actual String class, it's possible for incompatibilities to develop
|
||||
* (although unlikely).
|
||||
*
|
||||
* @param numBytes the number of bytes in the byte array
|
||||
* @param expectedCount the expected number of roundtrippable permutations
|
||||
* @param start the starting bytes encoded as a long as big-endian
|
||||
* @param lim the limit of bytes to process encoded as a long as big-endian,
|
||||
* or -1 to mean the max limit for numBytes
|
||||
*/
|
||||
void testBytesUsingByteBuffers(
|
||||
int numBytes, long expectedCount, long start, long lim)
|
||||
throws UnsupportedEncodingException {
|
||||
CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder()
|
||||
.onMalformedInput(CodingErrorAction.REPLACE)
|
||||
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
||||
CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder()
|
||||
.onMalformedInput(CodingErrorAction.REPLACE)
|
||||
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
||||
byte[] bytes = new byte[numBytes];
|
||||
int maxChars = (int) (decoder.maxCharsPerByte() * numBytes) + 1;
|
||||
char[] charsDecoded =
|
||||
new char[(int) (decoder.maxCharsPerByte() * numBytes) + 1];
|
||||
int maxBytes = (int) (encoder.maxBytesPerChar() * maxChars) + 1;
|
||||
byte[] bytesReencoded = new byte[maxBytes];
|
||||
|
||||
ByteBuffer bb = ByteBuffer.wrap(bytes);
|
||||
CharBuffer cb = CharBuffer.wrap(charsDecoded);
|
||||
ByteBuffer bbReencoded = ByteBuffer.wrap(bytesReencoded);
|
||||
if (lim == -1) {
|
||||
lim = 1L << (numBytes * 8);
|
||||
}
|
||||
long count = 0;
|
||||
long countRoundTripped = 0;
|
||||
for (long byteChar = start; byteChar < lim; byteChar++) {
|
||||
bb.rewind();
|
||||
bb.limit(bytes.length);
|
||||
cb.rewind();
|
||||
cb.limit(charsDecoded.length);
|
||||
bbReencoded.rewind();
|
||||
bbReencoded.limit(bytesReencoded.length);
|
||||
encoder.reset();
|
||||
decoder.reset();
|
||||
long tmpByteChar = byteChar;
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
bytes[bytes.length - i - 1] = (byte) tmpByteChar;
|
||||
tmpByteChar = tmpByteChar >> 8;
|
||||
}
|
||||
boolean isRoundTrippable = ByteString.copyFrom(bytes).isValidUtf8();
|
||||
CoderResult result = decoder.decode(bb, cb, true);
|
||||
assertFalse(result.isError());
|
||||
result = decoder.flush(cb);
|
||||
assertFalse(result.isError());
|
||||
|
||||
int charLen = cb.position();
|
||||
cb.rewind();
|
||||
cb.limit(charLen);
|
||||
result = encoder.encode(cb, bbReencoded, true);
|
||||
assertFalse(result.isError());
|
||||
result = encoder.flush(bbReencoded);
|
||||
assertFalse(result.isError());
|
||||
|
||||
boolean bytesEqual = true;
|
||||
int bytesLen = bbReencoded.position();
|
||||
if (bytesLen != numBytes) {
|
||||
bytesEqual = false;
|
||||
} else {
|
||||
for (int i = 0; i < numBytes; i++) {
|
||||
if (bytes[i] != bytesReencoded[i]) {
|
||||
bytesEqual = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bytesEqual != isRoundTrippable) {
|
||||
outputFailure(byteChar, bytes, bytesReencoded, bytesLen);
|
||||
}
|
||||
|
||||
count++;
|
||||
if (isRoundTrippable) {
|
||||
countRoundTripped++;
|
||||
}
|
||||
if (byteChar != 0 && byteChar % 1000000 == 0) {
|
||||
logger.info("Processed " + (byteChar / 1000000) +
|
||||
" million characters");
|
||||
}
|
||||
}
|
||||
logger.info("Round tripped " + countRoundTripped + " of " + count);
|
||||
assertEquals(expectedCount, countRoundTripped);
|
||||
}
|
||||
|
||||
private static void outputFailure(long byteChar, byte[] bytes, byte[] after) {
|
||||
outputFailure(byteChar, bytes, after, after.length);
|
||||
}
|
||||
|
||||
private static void outputFailure(long byteChar, byte[] bytes, byte[] after,
|
||||
int len) {
|
||||
fail("Failure: (" + Long.toHexString(byteChar) + ") " +
|
||||
toHexString(bytes) + " => " + toHexString(after, len));
|
||||
}
|
||||
|
||||
private static String toHexString(byte[] b) {
|
||||
return toHexString(b, b.length);
|
||||
}
|
||||
|
||||
private static String toHexString(byte[] b, int len) {
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append("\"");
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (i > 0) {
|
||||
s.append(" ");
|
||||
}
|
||||
s.append(String.format("%02x", b[i] & 0xFF));
|
||||
}
|
||||
s.append("\"");
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
public void testCopyConstructorCopiesByReference() {
|
||||
LazyStringArrayList list1 = new LazyStringArrayList();
|
||||
list1.add(STRING_A);
|
||||
list1.add(BYTE_STRING_B);
|
||||
list1.add(BYTE_STRING_C);
|
||||
|
||||
LazyStringArrayList list2 = new LazyStringArrayList(list1);
|
||||
assertEquals(3, list2.size());
|
||||
assertSame(STRING_A, list2.get(0));
|
||||
assertSame(BYTE_STRING_B, list2.getByteString(1));
|
||||
assertSame(BYTE_STRING_C, list2.getByteString(2));
|
||||
}
|
||||
|
||||
public void testListCopyConstructor() {
|
||||
List<String> list1 = new ArrayList<String>();
|
||||
list1.add(STRING_A);
|
||||
list1.add(STRING_B);
|
||||
list1.add(STRING_C);
|
||||
|
||||
LazyStringArrayList list2 = new LazyStringArrayList(list1);
|
||||
assertEquals(3, list2.size());
|
||||
assertSame(STRING_A, list2.get(0));
|
||||
assertSame(STRING_B, list2.get(1));
|
||||
assertSame(STRING_C, list2.get(2));
|
||||
}
|
||||
|
||||
public void testAddAllCopiesByReferenceIfPossible() {
|
||||
LazyStringArrayList list1 = new LazyStringArrayList();
|
||||
list1.add(STRING_A);
|
||||
list1.add(BYTE_STRING_B);
|
||||
list1.add(BYTE_STRING_C);
|
||||
|
||||
LazyStringArrayList list2 = new LazyStringArrayList();
|
||||
list2.addAll(list1);
|
||||
|
||||
assertEquals(3, list2.size());
|
||||
assertSame(STRING_A, list2.get(0));
|
||||
assertSame(BYTE_STRING_B, list2.getByteString(1));
|
||||
assertSame(BYTE_STRING_C, list2.getByteString(2));
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
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, });
|
||||
|
||||
private ByteString encodedTestAllTypes;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
this.encodedTestAllTypes = UnittestProto.TestAllTypes.newBuilder()
|
||||
.setOptionalString("foo")
|
||||
.addRepeatedString("bar")
|
||||
.addRepeatedString("baz")
|
||||
.build()
|
||||
.toByteString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
public void testNoStringCachingIfOnlyBytesAccessed() throws Exception {
|
||||
UnittestProto.TestAllTypes proto =
|
||||
UnittestProto.TestAllTypes.parseFrom(encodedTestAllTypes);
|
||||
ByteString optional = proto.getOptionalStringBytes();
|
||||
assertSame(optional, proto.getOptionalStringBytes());
|
||||
assertSame(optional, proto.toBuilder().getOptionalStringBytes());
|
||||
|
||||
ByteString repeated0 = proto.getRepeatedStringBytes(0);
|
||||
ByteString repeated1 = proto.getRepeatedStringBytes(1);
|
||||
assertSame(repeated0, proto.getRepeatedStringBytes(0));
|
||||
assertSame(repeated1, proto.getRepeatedStringBytes(1));
|
||||
assertSame(repeated0, proto.toBuilder().getRepeatedStringBytes(0));
|
||||
assertSame(repeated1, proto.toBuilder().getRepeatedStringBytes(1));
|
||||
}
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.UnittestLite;
|
||||
import com.google.protobuf.UnittestLite.TestAllTypesLite;
|
||||
import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
|
||||
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.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class LiteTest extends TestCase {
|
||||
public void setUp() throws Exception {
|
||||
// Test that nested extensions are initialized correctly even if the outer
|
||||
// class has not been accessed directly. This was once a bug with lite
|
||||
// messages.
|
||||
//
|
||||
// We put this in setUp() rather than in its own test method because we
|
||||
// need to make sure it runs before any actual tests.
|
||||
assertTrue(TestNestedExtensionLite.nestedExtension != null);
|
||||
}
|
||||
|
||||
public void testLite() throws Exception {
|
||||
// Since lite messages are a subset of regular messages, we can mostly
|
||||
// assume that the functionality of lite messages is already thoroughly
|
||||
// tested by the regular tests. All this test really verifies is that
|
||||
// a proto with optimize_for = LITE_RUNTIME compiles correctly when
|
||||
// linked only against the lite library. That is all tested at compile
|
||||
// time, leaving not much to do in this method. Let's just do some random
|
||||
// stuff to make sure the lite message is actually here and usable.
|
||||
|
||||
TestAllTypesLite message =
|
||||
TestAllTypesLite.newBuilder()
|
||||
.setOptionalInt32(123)
|
||||
.addRepeatedString("hello")
|
||||
.setOptionalNestedMessage(
|
||||
TestAllTypesLite.NestedMessage.newBuilder().setBb(7))
|
||||
.build();
|
||||
|
||||
ByteString data = message.toByteString();
|
||||
|
||||
TestAllTypesLite message2 = TestAllTypesLite.parseFrom(data);
|
||||
|
||||
assertEquals(123, message2.getOptionalInt32());
|
||||
assertEquals(1, message2.getRepeatedStringCount());
|
||||
assertEquals("hello", message2.getRepeatedString(0));
|
||||
assertEquals(7, message2.getOptionalNestedMessage().getBb());
|
||||
}
|
||||
|
||||
public void testLiteExtensions() throws Exception {
|
||||
// TODO(kenton): Unlike other features of the lite library, extensions are
|
||||
// implemented completely differently from the regular library. We
|
||||
// should probably test them more thoroughly.
|
||||
|
||||
TestAllExtensionsLite message =
|
||||
TestAllExtensionsLite.newBuilder()
|
||||
.setExtension(UnittestLite.optionalInt32ExtensionLite, 123)
|
||||
.addExtension(UnittestLite.repeatedStringExtensionLite, "hello")
|
||||
.setExtension(UnittestLite.optionalNestedEnumExtensionLite,
|
||||
TestAllTypesLite.NestedEnum.BAZ)
|
||||
.setExtension(UnittestLite.optionalNestedMessageExtensionLite,
|
||||
TestAllTypesLite.NestedMessage.newBuilder().setBb(7).build())
|
||||
.build();
|
||||
|
||||
// Test copying a message, since coping extensions actually does use a
|
||||
// different code path between lite and regular libraries, and as of this
|
||||
// writing, parsing hasn't been implemented yet.
|
||||
TestAllExtensionsLite message2 = message.toBuilder().build();
|
||||
|
||||
assertEquals(123, (int) message2.getExtension(
|
||||
UnittestLite.optionalInt32ExtensionLite));
|
||||
assertEquals(1, message2.getExtensionCount(
|
||||
UnittestLite.repeatedStringExtensionLite));
|
||||
assertEquals(1, message2.getExtension(
|
||||
UnittestLite.repeatedStringExtensionLite).size());
|
||||
assertEquals("hello", message2.getExtension(
|
||||
UnittestLite.repeatedStringExtensionLite, 0));
|
||||
assertEquals(TestAllTypesLite.NestedEnum.BAZ, message2.getExtension(
|
||||
UnittestLite.optionalNestedEnumExtensionLite));
|
||||
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);
|
||||
try {
|
||||
out.writeObject(expected);
|
||||
} finally {
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -1,396 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* Test {@link LiteralByteString} by setting up a reference string in {@link #setUp()}.
|
||||
* This class is designed to be extended for testing extensions of {@link LiteralByteString}
|
||||
* such as {@link BoundedByteString}, see {@link BoundedByteStringTest}.
|
||||
*
|
||||
* @author carlanton@google.com (Carl Haverl)
|
||||
*/
|
||||
public class LiteralByteStringTest extends TestCase {
|
||||
protected static final String UTF_8 = "UTF-8";
|
||||
|
||||
protected String classUnderTest;
|
||||
protected byte[] referenceBytes;
|
||||
protected ByteString stringUnderTest;
|
||||
protected int expectedHashCode;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
classUnderTest = "LiteralByteString";
|
||||
referenceBytes = ByteStringTest.getTestBytes(1234, 11337766L);
|
||||
stringUnderTest = ByteString.copyFrom(referenceBytes);
|
||||
expectedHashCode = 331161852;
|
||||
}
|
||||
|
||||
public void testExpectedType() {
|
||||
String actualClassName = getActualClassName(stringUnderTest);
|
||||
assertEquals(classUnderTest + " should match type exactly", classUnderTest, actualClassName);
|
||||
}
|
||||
|
||||
protected String getActualClassName(Object object) {
|
||||
String actualClassName = object.getClass().getName();
|
||||
actualClassName = actualClassName.substring(actualClassName.lastIndexOf('.') + 1);
|
||||
return actualClassName;
|
||||
}
|
||||
|
||||
public void testByteAt() {
|
||||
boolean stillEqual = true;
|
||||
for (int i = 0; stillEqual && i < referenceBytes.length; ++i) {
|
||||
stillEqual = (referenceBytes[i] == stringUnderTest.byteAt(i));
|
||||
}
|
||||
assertTrue(classUnderTest + " must capture the right bytes", stillEqual);
|
||||
}
|
||||
|
||||
public void testByteIterator() {
|
||||
boolean stillEqual = true;
|
||||
ByteString.ByteIterator iter = stringUnderTest.iterator();
|
||||
for (int i = 0; stillEqual && i < referenceBytes.length; ++i) {
|
||||
stillEqual = (iter.hasNext() && referenceBytes[i] == iter.nextByte());
|
||||
}
|
||||
assertTrue(classUnderTest + " must capture the right bytes", stillEqual);
|
||||
assertFalse(classUnderTest + " must have exhausted the itertor", iter.hasNext());
|
||||
|
||||
try {
|
||||
iter.nextByte();
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (NoSuchElementException e) {
|
||||
// This is success
|
||||
}
|
||||
}
|
||||
|
||||
public void testByteIterable() {
|
||||
boolean stillEqual = true;
|
||||
int j = 0;
|
||||
for (byte quantum : stringUnderTest) {
|
||||
stillEqual = (referenceBytes[j] == quantum);
|
||||
++j;
|
||||
}
|
||||
assertTrue(classUnderTest + " must capture the right bytes as Bytes", stillEqual);
|
||||
assertEquals(classUnderTest + " iterable character count", referenceBytes.length, j);
|
||||
}
|
||||
|
||||
public void testSize() {
|
||||
assertEquals(classUnderTest + " must have the expected size", referenceBytes.length,
|
||||
stringUnderTest.size());
|
||||
}
|
||||
|
||||
public void testGetTreeDepth() {
|
||||
assertEquals(classUnderTest + " must have depth 0", 0, stringUnderTest.getTreeDepth());
|
||||
}
|
||||
|
||||
public void testIsBalanced() {
|
||||
assertTrue(classUnderTest + " is technically balanced", stringUnderTest.isBalanced());
|
||||
}
|
||||
|
||||
public void testCopyTo_ByteArrayOffsetLength() {
|
||||
int destinationOffset = 50;
|
||||
int length = 100;
|
||||
byte[] destination = new byte[destinationOffset + length];
|
||||
int sourceOffset = 213;
|
||||
stringUnderTest.copyTo(destination, sourceOffset, destinationOffset, length);
|
||||
boolean stillEqual = true;
|
||||
for (int i = 0; stillEqual && i < length; ++i) {
|
||||
stillEqual = referenceBytes[i + sourceOffset] == destination[i + destinationOffset];
|
||||
}
|
||||
assertTrue(classUnderTest + ".copyTo(4 arg) must give the expected bytes", stillEqual);
|
||||
}
|
||||
|
||||
public void testCopyTo_ByteArrayOffsetLengthErrors() {
|
||||
int destinationOffset = 50;
|
||||
int length = 100;
|
||||
byte[] destination = new byte[destinationOffset + length];
|
||||
|
||||
try {
|
||||
// Copy one too many bytes
|
||||
stringUnderTest.copyTo(destination, stringUnderTest.size() + 1 - length,
|
||||
destinationOffset, length);
|
||||
fail("Should have thrown an exception when copying too many bytes of a "
|
||||
+ classUnderTest);
|
||||
} catch (IndexOutOfBoundsException expected) {
|
||||
// This is success
|
||||
}
|
||||
|
||||
try {
|
||||
// Copy with illegal negative sourceOffset
|
||||
stringUnderTest.copyTo(destination, -1, destinationOffset, length);
|
||||
fail("Should have thrown an exception when given a negative sourceOffset in "
|
||||
+ classUnderTest);
|
||||
} catch (IndexOutOfBoundsException expected) {
|
||||
// This is success
|
||||
}
|
||||
|
||||
try {
|
||||
// Copy with illegal negative destinationOffset
|
||||
stringUnderTest.copyTo(destination, 0, -1, length);
|
||||
fail("Should have thrown an exception when given a negative destinationOffset in "
|
||||
+ classUnderTest);
|
||||
} catch (IndexOutOfBoundsException expected) {
|
||||
// This is success
|
||||
}
|
||||
|
||||
try {
|
||||
// Copy with illegal negative size
|
||||
stringUnderTest.copyTo(destination, 0, 0, -1);
|
||||
fail("Should have thrown an exception when given a negative size in "
|
||||
+ classUnderTest);
|
||||
} catch (IndexOutOfBoundsException expected) {
|
||||
// This is success
|
||||
}
|
||||
|
||||
try {
|
||||
// Copy with illegal too-large sourceOffset
|
||||
stringUnderTest.copyTo(destination, 2 * stringUnderTest.size(), 0, length);
|
||||
fail("Should have thrown an exception when the destinationOffset is too large in "
|
||||
+ classUnderTest);
|
||||
} catch (IndexOutOfBoundsException expected) {
|
||||
// This is success
|
||||
}
|
||||
|
||||
try {
|
||||
// Copy with illegal too-large destinationOffset
|
||||
stringUnderTest.copyTo(destination, 0, 2 * destination.length, length);
|
||||
fail("Should have thrown an exception when the destinationOffset is too large in "
|
||||
+ classUnderTest);
|
||||
} catch (IndexOutOfBoundsException expected) {
|
||||
// This is success
|
||||
}
|
||||
}
|
||||
|
||||
public void testCopyTo_ByteBuffer() {
|
||||
ByteBuffer myBuffer = ByteBuffer.allocate(referenceBytes.length);
|
||||
stringUnderTest.copyTo(myBuffer);
|
||||
assertTrue(classUnderTest + ".copyTo(ByteBuffer) must give back the same bytes",
|
||||
Arrays.equals(referenceBytes, myBuffer.array()));
|
||||
}
|
||||
|
||||
public void testAsReadOnlyByteBuffer() {
|
||||
ByteBuffer byteBuffer = stringUnderTest.asReadOnlyByteBuffer();
|
||||
byte[] roundTripBytes = new byte[referenceBytes.length];
|
||||
assertTrue(byteBuffer.remaining() == referenceBytes.length);
|
||||
assertTrue(byteBuffer.isReadOnly());
|
||||
byteBuffer.get(roundTripBytes);
|
||||
assertTrue(classUnderTest + ".asReadOnlyByteBuffer() must give back the same bytes",
|
||||
Arrays.equals(referenceBytes, roundTripBytes));
|
||||
}
|
||||
|
||||
public void testAsReadOnlyByteBufferList() {
|
||||
List<ByteBuffer> byteBuffers = stringUnderTest.asReadOnlyByteBufferList();
|
||||
int bytesSeen = 0;
|
||||
byte[] roundTripBytes = new byte[referenceBytes.length];
|
||||
for (ByteBuffer byteBuffer : byteBuffers) {
|
||||
int thisLength = byteBuffer.remaining();
|
||||
assertTrue(byteBuffer.isReadOnly());
|
||||
assertTrue(bytesSeen + thisLength <= referenceBytes.length);
|
||||
byteBuffer.get(roundTripBytes, bytesSeen, thisLength);
|
||||
bytesSeen += thisLength;
|
||||
}
|
||||
assertTrue(bytesSeen == referenceBytes.length);
|
||||
assertTrue(classUnderTest + ".asReadOnlyByteBufferTest() must give back the same bytes",
|
||||
Arrays.equals(referenceBytes, roundTripBytes));
|
||||
}
|
||||
|
||||
public void testToByteArray() {
|
||||
byte[] roundTripBytes = stringUnderTest.toByteArray();
|
||||
assertTrue(classUnderTest + ".toByteArray() must give back the same bytes",
|
||||
Arrays.equals(referenceBytes, roundTripBytes));
|
||||
}
|
||||
|
||||
public void testWriteTo() throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
stringUnderTest.writeTo(bos);
|
||||
byte[] roundTripBytes = bos.toByteArray();
|
||||
assertTrue(classUnderTest + ".writeTo() must give back the same bytes",
|
||||
Arrays.equals(referenceBytes, roundTripBytes));
|
||||
}
|
||||
|
||||
public void testWriteTo_mutating() throws IOException {
|
||||
OutputStream os = new OutputStream() {
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) {
|
||||
for (int x = 0; x < len; ++x) {
|
||||
b[off + x] = (byte) 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) {
|
||||
// Purposefully left blank.
|
||||
}
|
||||
};
|
||||
|
||||
stringUnderTest.writeTo(os);
|
||||
byte[] newBytes = stringUnderTest.toByteArray();
|
||||
assertTrue(classUnderTest + ".writeTo() must not grant access to underlying array",
|
||||
Arrays.equals(referenceBytes, newBytes));
|
||||
}
|
||||
|
||||
public void testNewOutput() throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ByteString.Output output = ByteString.newOutput();
|
||||
stringUnderTest.writeTo(output);
|
||||
assertEquals("Output Size returns correct result",
|
||||
output.size(), stringUnderTest.size());
|
||||
output.writeTo(bos);
|
||||
assertTrue("Output.writeTo() must give back the same bytes",
|
||||
Arrays.equals(referenceBytes, bos.toByteArray()));
|
||||
|
||||
// write the output stream to itself! This should cause it to double
|
||||
output.writeTo(output);
|
||||
assertEquals("Writing an output stream to itself is successful",
|
||||
stringUnderTest.concat(stringUnderTest), output.toByteString());
|
||||
|
||||
output.reset();
|
||||
assertEquals("Output.reset() resets the output", 0, output.size());
|
||||
assertEquals("Output.reset() resets the output",
|
||||
ByteString.EMPTY, output.toByteString());
|
||||
|
||||
}
|
||||
|
||||
public void testToString() throws UnsupportedEncodingException {
|
||||
String testString = "I love unicode \u1234\u5678 characters";
|
||||
LiteralByteString unicode = new LiteralByteString(testString.getBytes(UTF_8));
|
||||
String roundTripString = unicode.toString(UTF_8);
|
||||
assertEquals(classUnderTest + " unicode must match", testString, roundTripString);
|
||||
}
|
||||
|
||||
public void testEquals() {
|
||||
assertEquals(classUnderTest + " must not equal null", false, stringUnderTest.equals(null));
|
||||
assertEquals(classUnderTest + " must equal self", stringUnderTest, stringUnderTest);
|
||||
assertFalse(classUnderTest + " must not equal the empty string",
|
||||
stringUnderTest.equals(ByteString.EMPTY));
|
||||
assertEquals(classUnderTest + " empty strings must be equal",
|
||||
new LiteralByteString(new byte[]{}), stringUnderTest.substring(55, 55));
|
||||
assertEquals(classUnderTest + " must equal another string with the same value",
|
||||
stringUnderTest, new LiteralByteString(referenceBytes));
|
||||
|
||||
byte[] mungedBytes = new byte[referenceBytes.length];
|
||||
System.arraycopy(referenceBytes, 0, mungedBytes, 0, referenceBytes.length);
|
||||
mungedBytes[mungedBytes.length - 5] ^= 0xFF;
|
||||
assertFalse(classUnderTest + " must not equal every string with the same length",
|
||||
stringUnderTest.equals(new LiteralByteString(mungedBytes)));
|
||||
}
|
||||
|
||||
public void testHashCode() {
|
||||
int hash = stringUnderTest.hashCode();
|
||||
assertEquals(classUnderTest + " must have expected hashCode", expectedHashCode, hash);
|
||||
}
|
||||
|
||||
public void testPeekCachedHashCode() {
|
||||
assertEquals(classUnderTest + ".peekCachedHashCode() should return zero at first", 0,
|
||||
stringUnderTest.peekCachedHashCode());
|
||||
stringUnderTest.hashCode();
|
||||
assertEquals(classUnderTest + ".peekCachedHashCode should return zero at first",
|
||||
expectedHashCode, stringUnderTest.peekCachedHashCode());
|
||||
}
|
||||
|
||||
public void testPartialHash() {
|
||||
// partialHash() is more strenuously tested elsewhere by testing hashes of substrings.
|
||||
// This test would fail if the expected hash were 1. It's not.
|
||||
int hash = stringUnderTest.partialHash(stringUnderTest.size(), 0, stringUnderTest.size());
|
||||
assertEquals(classUnderTest + ".partialHash() must yield expected hashCode",
|
||||
expectedHashCode, hash);
|
||||
}
|
||||
|
||||
public void testNewInput() throws IOException {
|
||||
InputStream input = stringUnderTest.newInput();
|
||||
assertEquals("InputStream.available() returns correct value",
|
||||
stringUnderTest.size(), input.available());
|
||||
boolean stillEqual = true;
|
||||
for (byte referenceByte : referenceBytes) {
|
||||
int expectedInt = (referenceByte & 0xFF);
|
||||
stillEqual = (expectedInt == input.read());
|
||||
}
|
||||
assertEquals("InputStream.available() returns correct value",
|
||||
0, input.available());
|
||||
assertTrue(classUnderTest + " must give the same bytes from the InputStream", stillEqual);
|
||||
assertEquals(classUnderTest + " InputStream must now be exhausted", -1, input.read());
|
||||
}
|
||||
|
||||
public void testNewInput_skip() throws IOException {
|
||||
InputStream input = stringUnderTest.newInput();
|
||||
int stringSize = stringUnderTest.size();
|
||||
int nearEndIndex = stringSize * 2 / 3;
|
||||
long skipped1 = input.skip(nearEndIndex);
|
||||
assertEquals("InputStream.skip()", skipped1, nearEndIndex);
|
||||
assertEquals("InputStream.available()",
|
||||
stringSize - skipped1, input.available());
|
||||
assertTrue("InputStream.mark() is available", input.markSupported());
|
||||
input.mark(0);
|
||||
assertEquals("InputStream.skip(), read()",
|
||||
stringUnderTest.byteAt(nearEndIndex) & 0xFF, input.read());
|
||||
assertEquals("InputStream.available()",
|
||||
stringSize - skipped1 - 1, input.available());
|
||||
long skipped2 = input.skip(stringSize);
|
||||
assertEquals("InputStream.skip() incomplete",
|
||||
skipped2, stringSize - skipped1 - 1);
|
||||
assertEquals("InputStream.skip(), no more input", 0, input.available());
|
||||
assertEquals("InputStream.skip(), no more input", -1, input.read());
|
||||
input.reset();
|
||||
assertEquals("InputStream.reset() succeded",
|
||||
stringSize - skipped1, input.available());
|
||||
assertEquals("InputStream.reset(), read()",
|
||||
stringUnderTest.byteAt(nearEndIndex) & 0xFF, input.read());
|
||||
}
|
||||
|
||||
public void testNewCodedInput() throws IOException {
|
||||
CodedInputStream cis = stringUnderTest.newCodedInput();
|
||||
byte[] roundTripBytes = cis.readRawBytes(referenceBytes.length);
|
||||
assertTrue(classUnderTest + " must give the same bytes back from the CodedInputStream",
|
||||
Arrays.equals(referenceBytes, roundTripBytes));
|
||||
assertTrue(classUnderTest + " CodedInputStream must now be exhausted", cis.isAtEnd());
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure we keep things simple when concatenating with empty. See also
|
||||
* {@link ByteStringTest#testConcat_empty()}.
|
||||
*/
|
||||
public void testConcat_empty() {
|
||||
assertSame(classUnderTest + " concatenated with empty must give " + classUnderTest,
|
||||
stringUnderTest.concat(ByteString.EMPTY), stringUnderTest);
|
||||
assertSame("empty concatenated with " + classUnderTest + " must give " + classUnderTest,
|
||||
ByteString.EMPTY.concat(stringUnderTest), stringUnderTest);
|
||||
}
|
||||
}
|
||||
@@ -1,353 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestRequired;
|
||||
import protobuf_unittest.UnittestProto.TestRequiredForeign;
|
||||
import protobuf_unittest.UnittestProto.ForeignMessage;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Misc. unit tests for message operations that apply to both generated
|
||||
* and dynamic messages.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class MessageTest extends TestCase {
|
||||
// =================================================================
|
||||
// Message-merging tests.
|
||||
|
||||
static final TestAllTypes MERGE_SOURCE =
|
||||
TestAllTypes.newBuilder()
|
||||
.setOptionalInt32(1)
|
||||
.setOptionalString("foo")
|
||||
.setOptionalForeignMessage(ForeignMessage.getDefaultInstance())
|
||||
.addRepeatedString("bar")
|
||||
.build();
|
||||
|
||||
static final TestAllTypes MERGE_DEST =
|
||||
TestAllTypes.newBuilder()
|
||||
.setOptionalInt64(2)
|
||||
.setOptionalString("baz")
|
||||
.setOptionalForeignMessage(ForeignMessage.newBuilder().setC(3).build())
|
||||
.addRepeatedString("qux")
|
||||
.build();
|
||||
|
||||
static final String MERGE_RESULT_TEXT =
|
||||
"optional_int32: 1\n" +
|
||||
"optional_int64: 2\n" +
|
||||
"optional_string: \"foo\"\n" +
|
||||
"optional_foreign_message {\n" +
|
||||
" c: 3\n" +
|
||||
"}\n" +
|
||||
"repeated_string: \"qux\"\n" +
|
||||
"repeated_string: \"bar\"\n";
|
||||
|
||||
public void testMergeFrom() throws Exception {
|
||||
TestAllTypes result =
|
||||
TestAllTypes.newBuilder(MERGE_DEST)
|
||||
.mergeFrom(MERGE_SOURCE).build();
|
||||
|
||||
assertEquals(MERGE_RESULT_TEXT, result.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test merging a DynamicMessage into a GeneratedMessage. As long as they
|
||||
* have the same descriptor, this should work, but it is an entirely different
|
||||
* code path.
|
||||
*/
|
||||
public void testMergeFromDynamic() throws Exception {
|
||||
TestAllTypes result =
|
||||
TestAllTypes.newBuilder(MERGE_DEST)
|
||||
.mergeFrom(DynamicMessage.newBuilder(MERGE_SOURCE).build())
|
||||
.build();
|
||||
|
||||
assertEquals(MERGE_RESULT_TEXT, result.toString());
|
||||
}
|
||||
|
||||
/** Test merging two DynamicMessages. */
|
||||
public void testDynamicMergeFrom() throws Exception {
|
||||
DynamicMessage result =
|
||||
DynamicMessage.newBuilder(MERGE_DEST)
|
||||
.mergeFrom(DynamicMessage.newBuilder(MERGE_SOURCE).build())
|
||||
.build();
|
||||
|
||||
assertEquals(MERGE_RESULT_TEXT, result.toString());
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Required-field-related tests.
|
||||
|
||||
private static final TestRequired TEST_REQUIRED_UNINITIALIZED =
|
||||
TestRequired.getDefaultInstance();
|
||||
private static final TestRequired TEST_REQUIRED_INITIALIZED =
|
||||
TestRequired.newBuilder().setA(1).setB(2).setC(3).build();
|
||||
|
||||
public void testRequired() throws Exception {
|
||||
TestRequired.Builder builder = TestRequired.newBuilder();
|
||||
|
||||
assertFalse(builder.isInitialized());
|
||||
builder.setA(1);
|
||||
assertFalse(builder.isInitialized());
|
||||
builder.setB(1);
|
||||
assertFalse(builder.isInitialized());
|
||||
builder.setC(1);
|
||||
assertTrue(builder.isInitialized());
|
||||
}
|
||||
|
||||
public void testRequiredForeign() throws Exception {
|
||||
TestRequiredForeign.Builder builder = TestRequiredForeign.newBuilder();
|
||||
|
||||
assertTrue(builder.isInitialized());
|
||||
|
||||
builder.setOptionalMessage(TEST_REQUIRED_UNINITIALIZED);
|
||||
assertFalse(builder.isInitialized());
|
||||
|
||||
builder.setOptionalMessage(TEST_REQUIRED_INITIALIZED);
|
||||
assertTrue(builder.isInitialized());
|
||||
|
||||
builder.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED);
|
||||
assertFalse(builder.isInitialized());
|
||||
|
||||
builder.setRepeatedMessage(0, TEST_REQUIRED_INITIALIZED);
|
||||
assertTrue(builder.isInitialized());
|
||||
}
|
||||
|
||||
public void testRequiredExtension() throws Exception {
|
||||
TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
|
||||
|
||||
assertTrue(builder.isInitialized());
|
||||
|
||||
builder.setExtension(TestRequired.single, TEST_REQUIRED_UNINITIALIZED);
|
||||
assertFalse(builder.isInitialized());
|
||||
|
||||
builder.setExtension(TestRequired.single, TEST_REQUIRED_INITIALIZED);
|
||||
assertTrue(builder.isInitialized());
|
||||
|
||||
builder.addExtension(TestRequired.multi, TEST_REQUIRED_UNINITIALIZED);
|
||||
assertFalse(builder.isInitialized());
|
||||
|
||||
builder.setExtension(TestRequired.multi, 0, TEST_REQUIRED_INITIALIZED);
|
||||
assertTrue(builder.isInitialized());
|
||||
}
|
||||
|
||||
public void testRequiredDynamic() throws Exception {
|
||||
Descriptors.Descriptor descriptor = TestRequired.getDescriptor();
|
||||
DynamicMessage.Builder builder = DynamicMessage.newBuilder(descriptor);
|
||||
|
||||
assertFalse(builder.isInitialized());
|
||||
builder.setField(descriptor.findFieldByName("a"), 1);
|
||||
assertFalse(builder.isInitialized());
|
||||
builder.setField(descriptor.findFieldByName("b"), 1);
|
||||
assertFalse(builder.isInitialized());
|
||||
builder.setField(descriptor.findFieldByName("c"), 1);
|
||||
assertTrue(builder.isInitialized());
|
||||
}
|
||||
|
||||
public void testRequiredDynamicForeign() throws Exception {
|
||||
Descriptors.Descriptor descriptor = TestRequiredForeign.getDescriptor();
|
||||
DynamicMessage.Builder builder = DynamicMessage.newBuilder(descriptor);
|
||||
|
||||
assertTrue(builder.isInitialized());
|
||||
|
||||
builder.setField(descriptor.findFieldByName("optional_message"),
|
||||
TEST_REQUIRED_UNINITIALIZED);
|
||||
assertFalse(builder.isInitialized());
|
||||
|
||||
builder.setField(descriptor.findFieldByName("optional_message"),
|
||||
TEST_REQUIRED_INITIALIZED);
|
||||
assertTrue(builder.isInitialized());
|
||||
|
||||
builder.addRepeatedField(descriptor.findFieldByName("repeated_message"),
|
||||
TEST_REQUIRED_UNINITIALIZED);
|
||||
assertFalse(builder.isInitialized());
|
||||
|
||||
builder.setRepeatedField(descriptor.findFieldByName("repeated_message"), 0,
|
||||
TEST_REQUIRED_INITIALIZED);
|
||||
assertTrue(builder.isInitialized());
|
||||
}
|
||||
|
||||
public void testUninitializedException() throws Exception {
|
||||
try {
|
||||
TestRequired.newBuilder().build();
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (UninitializedMessageException e) {
|
||||
assertEquals("Message missing required fields: a, b, c", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testBuildPartial() throws Exception {
|
||||
// We're mostly testing that no exception is thrown.
|
||||
TestRequired message = TestRequired.newBuilder().buildPartial();
|
||||
assertFalse(message.isInitialized());
|
||||
}
|
||||
|
||||
public void testNestedUninitializedException() throws Exception {
|
||||
try {
|
||||
TestRequiredForeign.newBuilder()
|
||||
.setOptionalMessage(TEST_REQUIRED_UNINITIALIZED)
|
||||
.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED)
|
||||
.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED)
|
||||
.build();
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (UninitializedMessageException e) {
|
||||
assertEquals(
|
||||
"Message missing required fields: " +
|
||||
"optional_message.a, " +
|
||||
"optional_message.b, " +
|
||||
"optional_message.c, " +
|
||||
"repeated_message[0].a, " +
|
||||
"repeated_message[0].b, " +
|
||||
"repeated_message[0].c, " +
|
||||
"repeated_message[1].a, " +
|
||||
"repeated_message[1].b, " +
|
||||
"repeated_message[1].c",
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testBuildNestedPartial() throws Exception {
|
||||
// We're mostly testing that no exception is thrown.
|
||||
TestRequiredForeign message =
|
||||
TestRequiredForeign.newBuilder()
|
||||
.setOptionalMessage(TEST_REQUIRED_UNINITIALIZED)
|
||||
.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED)
|
||||
.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED)
|
||||
.buildPartial();
|
||||
assertFalse(message.isInitialized());
|
||||
}
|
||||
|
||||
public void testParseUnititialized() throws Exception {
|
||||
try {
|
||||
TestRequired.parseFrom(ByteString.EMPTY);
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
assertEquals("Message missing required fields: a, b, c", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testParseNestedUnititialized() throws Exception {
|
||||
ByteString data =
|
||||
TestRequiredForeign.newBuilder()
|
||||
.setOptionalMessage(TEST_REQUIRED_UNINITIALIZED)
|
||||
.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED)
|
||||
.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED)
|
||||
.buildPartial().toByteString();
|
||||
|
||||
try {
|
||||
TestRequiredForeign.parseFrom(data);
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
assertEquals(
|
||||
"Message missing required fields: " +
|
||||
"optional_message.a, " +
|
||||
"optional_message.b, " +
|
||||
"optional_message.c, " +
|
||||
"repeated_message[0].a, " +
|
||||
"repeated_message[0].b, " +
|
||||
"repeated_message[0].c, " +
|
||||
"repeated_message[1].a, " +
|
||||
"repeated_message[1].b, " +
|
||||
"repeated_message[1].c",
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testDynamicUninitializedException() throws Exception {
|
||||
try {
|
||||
DynamicMessage.newBuilder(TestRequired.getDescriptor()).build();
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (UninitializedMessageException e) {
|
||||
assertEquals("Message missing required fields: a, b, c", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testDynamicBuildPartial() throws Exception {
|
||||
// We're mostly testing that no exception is thrown.
|
||||
DynamicMessage message =
|
||||
DynamicMessage.newBuilder(TestRequired.getDescriptor())
|
||||
.buildPartial();
|
||||
assertFalse(message.isInitialized());
|
||||
}
|
||||
|
||||
public void testDynamicParseUnititialized() throws Exception {
|
||||
try {
|
||||
Descriptors.Descriptor descriptor = TestRequired.getDescriptor();
|
||||
DynamicMessage.parseFrom(descriptor, ByteString.EMPTY);
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
assertEquals("Message missing required fields: a, b, c", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/** Test reading unset repeated message from DynamicMessage. */
|
||||
public void testDynamicRepeatedMessageNull() throws Exception {
|
||||
Descriptors.Descriptor descriptor = TestRequired.getDescriptor();
|
||||
DynamicMessage result =
|
||||
DynamicMessage.newBuilder(TestAllTypes.getDescriptor())
|
||||
.mergeFrom(DynamicMessage.newBuilder(MERGE_SOURCE).build())
|
||||
.build();
|
||||
|
||||
assertTrue(result.getField(result.getDescriptorForType()
|
||||
.findFieldByName("repeated_foreign_message")) instanceof List<?>);
|
||||
assertEquals(result.getRepeatedFieldCount(result.getDescriptorForType()
|
||||
.findFieldByName("repeated_foreign_message")), 0);
|
||||
}
|
||||
|
||||
/** Test reading repeated message from DynamicMessage. */
|
||||
public void testDynamicRepeatedMessageNotNull() throws Exception {
|
||||
|
||||
TestAllTypes REPEATED_NESTED =
|
||||
TestAllTypes.newBuilder()
|
||||
.setOptionalInt32(1)
|
||||
.setOptionalString("foo")
|
||||
.setOptionalForeignMessage(ForeignMessage.getDefaultInstance())
|
||||
.addRepeatedString("bar")
|
||||
.addRepeatedForeignMessage(ForeignMessage.getDefaultInstance())
|
||||
.addRepeatedForeignMessage(ForeignMessage.getDefaultInstance())
|
||||
.build();
|
||||
Descriptors.Descriptor descriptor = TestRequired.getDescriptor();
|
||||
DynamicMessage result =
|
||||
DynamicMessage.newBuilder(TestAllTypes.getDescriptor())
|
||||
.mergeFrom(DynamicMessage.newBuilder(REPEATED_NESTED).build())
|
||||
.build();
|
||||
|
||||
assertTrue(result.getField(result.getDescriptorForType()
|
||||
.findFieldByName("repeated_foreign_message")) instanceof List<?>);
|
||||
assertEquals(result.getRepeatedFieldCount(result.getDescriptorForType()
|
||||
.findFieldByName("repeated_foreign_message")), 2);
|
||||
}
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -1,375 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.UnittestLite.TestAllTypesLite;
|
||||
import com.google.protobuf.UnittestLite.TestPackedExtensionsLite;
|
||||
import com.google.protobuf.UnittestLite.TestParsingMergeLite;
|
||||
import com.google.protobuf.UnittestLite;
|
||||
import protobuf_unittest.UnittestOptimizeFor.TestOptimizedForSize;
|
||||
import protobuf_unittest.UnittestOptimizeFor.TestRequiredOptimizedForSize;
|
||||
import protobuf_unittest.UnittestOptimizeFor;
|
||||
import protobuf_unittest.UnittestProto.ForeignMessage;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestEmptyMessage;
|
||||
import protobuf_unittest.UnittestProto.TestRequired;
|
||||
import protobuf_unittest.UnittestProto.TestParsingMerge;
|
||||
import protobuf_unittest.UnittestProto;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Unit test for {@link Parser}.
|
||||
*
|
||||
* @author liujisi@google.com (Pherl Liu)
|
||||
*/
|
||||
public class ParserTest extends TestCase {
|
||||
public void testGeneratedMessageParserSingleton() throws Exception {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assertEquals(TestAllTypes.PARSER,
|
||||
TestUtil.getAllSet().getParserForType());
|
||||
}
|
||||
}
|
||||
|
||||
private void assertRoundTripEquals(MessageLite message,
|
||||
ExtensionRegistryLite registry)
|
||||
throws Exception {
|
||||
final byte[] data = message.toByteArray();
|
||||
final int offset = 20;
|
||||
final int length = data.length;
|
||||
final int padding = 30;
|
||||
Parser<? extends MessageLite> parser = message.getParserForType();
|
||||
assertMessageEquals(message, parser.parseFrom(data, registry));
|
||||
assertMessageEquals(message, parser.parseFrom(
|
||||
generatePaddingArray(data, offset, padding),
|
||||
offset, length, registry));
|
||||
assertMessageEquals(message, parser.parseFrom(
|
||||
message.toByteString(), registry));
|
||||
assertMessageEquals(message, parser.parseFrom(
|
||||
new ByteArrayInputStream(data), registry));
|
||||
assertMessageEquals(message, parser.parseFrom(
|
||||
CodedInputStream.newInstance(data), registry));
|
||||
}
|
||||
|
||||
private void assertRoundTripEquals(MessageLite message) throws Exception {
|
||||
final byte[] data = message.toByteArray();
|
||||
final int offset = 20;
|
||||
final int length = data.length;
|
||||
final int padding = 30;
|
||||
Parser<? extends MessageLite> parser = message.getParserForType();
|
||||
assertMessageEquals(message, parser.parseFrom(data));
|
||||
assertMessageEquals(message, parser.parseFrom(
|
||||
generatePaddingArray(data, offset, padding),
|
||||
offset, length));
|
||||
assertMessageEquals(message, parser.parseFrom(message.toByteString()));
|
||||
assertMessageEquals(message, parser.parseFrom(
|
||||
new ByteArrayInputStream(data)));
|
||||
assertMessageEquals(message, parser.parseFrom(
|
||||
CodedInputStream.newInstance(data)));
|
||||
}
|
||||
|
||||
private void assertMessageEquals(MessageLite expected, MessageLite actual)
|
||||
throws Exception {
|
||||
if (expected instanceof Message) {
|
||||
assertEquals(expected, actual);
|
||||
} else {
|
||||
assertEquals(expected.toByteString(), actual.toByteString());
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] generatePaddingArray(byte[] data, int offset, int padding) {
|
||||
byte[] result = new byte[offset + data.length + padding];
|
||||
System.arraycopy(data, 0, result, offset, data.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void testNormalMessage() throws Exception {
|
||||
assertRoundTripEquals(TestUtil.getAllSet());
|
||||
}
|
||||
|
||||
public void testParsePartial() throws Exception {
|
||||
Parser<TestRequired> parser = TestRequired.PARSER;
|
||||
final String errorString =
|
||||
"Should throw exceptions when the parsed message isn't initialized.";
|
||||
|
||||
// TestRequired.b and TestRequired.c are not set.
|
||||
TestRequired partialMessage = TestRequired.newBuilder()
|
||||
.setA(1).buildPartial();
|
||||
|
||||
// parsePartialFrom should pass.
|
||||
byte[] data = partialMessage.toByteArray();
|
||||
assertEquals(partialMessage, parser.parsePartialFrom(data));
|
||||
assertEquals(partialMessage, parser.parsePartialFrom(
|
||||
partialMessage.toByteString()));
|
||||
assertEquals(partialMessage, parser.parsePartialFrom(
|
||||
new ByteArrayInputStream(data)));
|
||||
assertEquals(partialMessage, parser.parsePartialFrom(
|
||||
CodedInputStream.newInstance(data)));
|
||||
|
||||
// parseFrom(ByteArray)
|
||||
try {
|
||||
parser.parseFrom(partialMessage.toByteArray());
|
||||
fail(errorString);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// pass.
|
||||
}
|
||||
|
||||
// parseFrom(ByteString)
|
||||
try {
|
||||
parser.parseFrom(partialMessage.toByteString());
|
||||
fail(errorString);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// pass.
|
||||
}
|
||||
|
||||
// parseFrom(InputStream)
|
||||
try {
|
||||
parser.parseFrom(new ByteArrayInputStream(partialMessage.toByteArray()));
|
||||
fail(errorString);
|
||||
} catch (IOException e) {
|
||||
// pass.
|
||||
}
|
||||
|
||||
// parseFrom(CodedInputStream)
|
||||
try {
|
||||
parser.parseFrom(CodedInputStream.newInstance(
|
||||
partialMessage.toByteArray()));
|
||||
fail(errorString);
|
||||
} catch (IOException e) {
|
||||
// pass.
|
||||
}
|
||||
}
|
||||
|
||||
public void testParseExtensions() throws Exception {
|
||||
assertRoundTripEquals(TestUtil.getAllExtensionsSet(),
|
||||
TestUtil.getExtensionRegistry());
|
||||
assertRoundTripEquals(TestUtil.getAllLiteExtensionsSet(),
|
||||
TestUtil.getExtensionRegistryLite());
|
||||
}
|
||||
|
||||
public void testParsePacked() throws Exception {
|
||||
assertRoundTripEquals(TestUtil.getPackedSet());
|
||||
assertRoundTripEquals(TestUtil.getPackedExtensionsSet(),
|
||||
TestUtil.getExtensionRegistry());
|
||||
assertRoundTripEquals(TestUtil.getLitePackedExtensionsSet(),
|
||||
TestUtil.getExtensionRegistryLite());
|
||||
}
|
||||
|
||||
public void testParseDelimitedTo() throws Exception {
|
||||
// Write normal Message.
|
||||
TestAllTypes normalMessage = TestUtil.getAllSet();
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
normalMessage.writeDelimitedTo(output);
|
||||
|
||||
// Write MessageLite with packed extension fields.
|
||||
TestPackedExtensionsLite packedMessage =
|
||||
TestUtil.getLitePackedExtensionsSet();
|
||||
packedMessage.writeDelimitedTo(output);
|
||||
|
||||
InputStream input = new ByteArrayInputStream(output.toByteArray());
|
||||
assertMessageEquals(
|
||||
normalMessage,
|
||||
normalMessage.getParserForType().parseDelimitedFrom(input));
|
||||
assertMessageEquals(
|
||||
packedMessage,
|
||||
packedMessage.getParserForType().parseDelimitedFrom(
|
||||
input, TestUtil.getExtensionRegistryLite()));
|
||||
}
|
||||
|
||||
public void testParseUnknownFields() throws Exception {
|
||||
// All fields will be treated as unknown fields in emptyMessage.
|
||||
TestEmptyMessage emptyMessage = TestEmptyMessage.PARSER.parseFrom(
|
||||
TestUtil.getAllSet().toByteString());
|
||||
assertEquals(
|
||||
TestUtil.getAllSet().toByteString(),
|
||||
emptyMessage.toByteString());
|
||||
}
|
||||
|
||||
public void testOptimizeForSize() throws Exception {
|
||||
TestOptimizedForSize.Builder builder = TestOptimizedForSize.newBuilder();
|
||||
builder.setI(12).setMsg(ForeignMessage.newBuilder().setC(34).build());
|
||||
builder.setExtension(TestOptimizedForSize.testExtension, 56);
|
||||
builder.setExtension(TestOptimizedForSize.testExtension2,
|
||||
TestRequiredOptimizedForSize.newBuilder().setX(78).build());
|
||||
|
||||
TestOptimizedForSize message = builder.build();
|
||||
ExtensionRegistry registry = ExtensionRegistry.newInstance();
|
||||
UnittestOptimizeFor.registerAllExtensions(registry);
|
||||
|
||||
assertRoundTripEquals(message, registry);
|
||||
}
|
||||
|
||||
/** Helper method for {@link #testParsingMerge()}.*/
|
||||
private void assertMessageMerged(TestAllTypes allTypes)
|
||||
throws Exception {
|
||||
assertEquals(3, allTypes.getOptionalInt32());
|
||||
assertEquals(2, allTypes.getOptionalInt64());
|
||||
assertEquals("hello", allTypes.getOptionalString());
|
||||
}
|
||||
|
||||
/** Helper method for {@link #testParsingMergeLite()}.*/
|
||||
private void assertMessageMerged(TestAllTypesLite allTypes)
|
||||
throws Exception {
|
||||
assertEquals(3, allTypes.getOptionalInt32());
|
||||
assertEquals(2, allTypes.getOptionalInt64());
|
||||
assertEquals("hello", allTypes.getOptionalString());
|
||||
}
|
||||
|
||||
public void testParsingMerge() throws Exception {
|
||||
// Build messages.
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TestAllTypes msg1 = builder.setOptionalInt32(1).build();
|
||||
builder.clear();
|
||||
TestAllTypes msg2 = builder.setOptionalInt64(2).build();
|
||||
builder.clear();
|
||||
TestAllTypes msg3 = builder.setOptionalInt32(3)
|
||||
.setOptionalString("hello").build();
|
||||
|
||||
// Build groups.
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group1 optionalG1 =
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group1.newBuilder()
|
||||
.setField1(msg1).build();
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group1 optionalG2 =
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group1.newBuilder()
|
||||
.setField1(msg2).build();
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group1 optionalG3 =
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group1.newBuilder()
|
||||
.setField1(msg3).build();
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group2 repeatedG1 =
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group2.newBuilder()
|
||||
.setField1(msg1).build();
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group2 repeatedG2 =
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group2.newBuilder()
|
||||
.setField1(msg2).build();
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group2 repeatedG3 =
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group2.newBuilder()
|
||||
.setField1(msg3).build();
|
||||
|
||||
// Assign and serialize RepeatedFieldsGenerator.
|
||||
ByteString data = TestParsingMerge.RepeatedFieldsGenerator.newBuilder()
|
||||
.addField1(msg1).addField1(msg2).addField1(msg3)
|
||||
.addField2(msg1).addField2(msg2).addField2(msg3)
|
||||
.addField3(msg1).addField3(msg2).addField3(msg3)
|
||||
.addGroup1(optionalG1).addGroup1(optionalG2).addGroup1(optionalG3)
|
||||
.addGroup2(repeatedG1).addGroup2(repeatedG2).addGroup2(repeatedG3)
|
||||
.addExt1(msg1).addExt1(msg2).addExt1(msg3)
|
||||
.addExt2(msg1).addExt2(msg2).addExt2(msg3)
|
||||
.build().toByteString();
|
||||
|
||||
// Parse TestParsingMerge.
|
||||
ExtensionRegistry registry = ExtensionRegistry.newInstance();
|
||||
UnittestProto.registerAllExtensions(registry);
|
||||
TestParsingMerge parsingMerge =
|
||||
TestParsingMerge.PARSER.parseFrom(data, registry);
|
||||
|
||||
// Required and optional fields should be merged.
|
||||
assertMessageMerged(parsingMerge.getRequiredAllTypes());
|
||||
assertMessageMerged(parsingMerge.getOptionalAllTypes());
|
||||
assertMessageMerged(
|
||||
parsingMerge.getOptionalGroup().getOptionalGroupAllTypes());
|
||||
assertMessageMerged(parsingMerge.getExtension(
|
||||
TestParsingMerge.optionalExt));
|
||||
|
||||
// Repeated fields should not be merged.
|
||||
assertEquals(3, parsingMerge.getRepeatedAllTypesCount());
|
||||
assertEquals(3, parsingMerge.getRepeatedGroupCount());
|
||||
assertEquals(3, parsingMerge.getExtensionCount(
|
||||
TestParsingMerge.repeatedExt));
|
||||
}
|
||||
|
||||
public void testParsingMergeLite() throws Exception {
|
||||
// Build messages.
|
||||
TestAllTypesLite.Builder builder =
|
||||
TestAllTypesLite.newBuilder();
|
||||
TestAllTypesLite msg1 = builder.setOptionalInt32(1).build();
|
||||
builder.clear();
|
||||
TestAllTypesLite msg2 = builder.setOptionalInt64(2).build();
|
||||
builder.clear();
|
||||
TestAllTypesLite msg3 = builder.setOptionalInt32(3)
|
||||
.setOptionalString("hello").build();
|
||||
|
||||
// Build groups.
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group1 optionalG1 =
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group1.newBuilder()
|
||||
.setField1(msg1).build();
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group1 optionalG2 =
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group1.newBuilder()
|
||||
.setField1(msg2).build();
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group1 optionalG3 =
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group1.newBuilder()
|
||||
.setField1(msg3).build();
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group2 repeatedG1 =
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group2.newBuilder()
|
||||
.setField1(msg1).build();
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group2 repeatedG2 =
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group2.newBuilder()
|
||||
.setField1(msg2).build();
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group2 repeatedG3 =
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group2.newBuilder()
|
||||
.setField1(msg3).build();
|
||||
|
||||
// Assign and serialize RepeatedFieldsGenerator.
|
||||
ByteString data = TestParsingMergeLite.RepeatedFieldsGenerator.newBuilder()
|
||||
.addField1(msg1).addField1(msg2).addField1(msg3)
|
||||
.addField2(msg1).addField2(msg2).addField2(msg3)
|
||||
.addField3(msg1).addField3(msg2).addField3(msg3)
|
||||
.addGroup1(optionalG1).addGroup1(optionalG2).addGroup1(optionalG3)
|
||||
.addGroup2(repeatedG1).addGroup2(repeatedG2).addGroup2(repeatedG3)
|
||||
.addExt1(msg1).addExt1(msg2).addExt1(msg3)
|
||||
.addExt2(msg1).addExt2(msg2).addExt2(msg3)
|
||||
.build().toByteString();
|
||||
|
||||
// Parse TestParsingMergeLite.
|
||||
ExtensionRegistry registry = ExtensionRegistry.newInstance();
|
||||
UnittestLite.registerAllExtensions(registry);
|
||||
TestParsingMergeLite parsingMerge =
|
||||
TestParsingMergeLite.PARSER.parseFrom(data, registry);
|
||||
|
||||
// Required and optional fields should be merged.
|
||||
assertMessageMerged(parsingMerge.getRequiredAllTypes());
|
||||
assertMessageMerged(parsingMerge.getOptionalAllTypes());
|
||||
assertMessageMerged(
|
||||
parsingMerge.getOptionalGroup().getOptionalGroupAllTypes());
|
||||
assertMessageMerged(parsingMerge.getExtension(
|
||||
TestParsingMergeLite.optionalExt));
|
||||
|
||||
// Repeated fields should not be merged.
|
||||
assertEquals(3, parsingMerge.getRepeatedAllTypesCount());
|
||||
assertEquals(3, parsingMerge.getRepeatedGroupCount());
|
||||
assertEquals(3, parsingMerge.getExtensionCount(
|
||||
TestParsingMergeLite.repeatedExt));
|
||||
}
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* This class tests {@link RopeByteString#substring(int, int)} by inheriting the tests from
|
||||
* {@link LiteralByteStringTest}. Only a couple of methods are overridden.
|
||||
*
|
||||
* @author carlanton@google.com (Carl Haverl)
|
||||
*/
|
||||
public class RopeByteStringSubstringTest extends LiteralByteStringTest {
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
classUnderTest = "RopeByteString";
|
||||
byte[] sourceBytes = ByteStringTest.getTestBytes(22341, 22337766L);
|
||||
Iterator<ByteString> iter = ByteStringTest.makeConcretePieces(sourceBytes).iterator();
|
||||
ByteString sourceString = iter.next();
|
||||
while (iter.hasNext()) {
|
||||
sourceString = sourceString.concat(iter.next());
|
||||
}
|
||||
|
||||
int from = 1130;
|
||||
int to = sourceBytes.length - 5555;
|
||||
stringUnderTest = sourceString.substring(from, to);
|
||||
referenceBytes = new byte[to - from];
|
||||
System.arraycopy(sourceBytes, from, referenceBytes, 0, to - from);
|
||||
expectedHashCode = -1259260680;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testGetTreeDepth() {
|
||||
assertEquals(classUnderTest + " must have the expected tree depth",
|
||||
3, stringUnderTest.getTreeDepth());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testToString() throws UnsupportedEncodingException {
|
||||
String sourceString = "I love unicode \u1234\u5678 characters";
|
||||
ByteString sourceByteString = ByteString.copyFromUtf8(sourceString);
|
||||
int copies = 250;
|
||||
|
||||
// By building the RopeByteString by concatenating, this is actually a fairly strenuous test.
|
||||
StringBuilder builder = new StringBuilder(copies * sourceString.length());
|
||||
ByteString unicode = ByteString.EMPTY;
|
||||
for (int i = 0; i < copies; ++i) {
|
||||
builder.append(sourceString);
|
||||
unicode = RopeByteString.concatenate(unicode, sourceByteString);
|
||||
}
|
||||
String testString = builder.toString();
|
||||
|
||||
// Do the substring part
|
||||
testString = testString.substring(2, testString.length() - 6);
|
||||
unicode = unicode.substring(2, unicode.size() - 6);
|
||||
|
||||
assertEquals(classUnderTest + " from string must have the expected type",
|
||||
classUnderTest, getActualClassName(unicode));
|
||||
String roundTripString = unicode.toString(UTF_8);
|
||||
assertEquals(classUnderTest + " unicode bytes must match",
|
||||
testString, roundTripString);
|
||||
ByteString flatString = ByteString.copyFromUtf8(testString);
|
||||
assertEquals(classUnderTest + " string must equal the flat string", flatString, unicode);
|
||||
assertEquals(classUnderTest + " string must must have same hashCode as the flat string",
|
||||
flatString.hashCode(), unicode.hashCode());
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* This class tests {@link RopeByteString} by inheriting the tests from
|
||||
* {@link LiteralByteStringTest}. Only a couple of methods are overridden.
|
||||
*
|
||||
* <p>A full test of the result of {@link RopeByteString#substring(int, int)} is found in the
|
||||
* separate class {@link RopeByteStringSubstringTest}.
|
||||
*
|
||||
* @author carlanton@google.com (Carl Haverl)
|
||||
*/
|
||||
public class RopeByteStringTest extends LiteralByteStringTest {
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
classUnderTest = "RopeByteString";
|
||||
referenceBytes = ByteStringTest.getTestBytes(22341, 22337766L);
|
||||
Iterator<ByteString> iter = ByteStringTest.makeConcretePieces(referenceBytes).iterator();
|
||||
stringUnderTest = iter.next();
|
||||
while (iter.hasNext()) {
|
||||
stringUnderTest = stringUnderTest.concat(iter.next());
|
||||
}
|
||||
expectedHashCode = -1214197238;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testGetTreeDepth() {
|
||||
assertEquals(classUnderTest + " must have the expected tree depth",
|
||||
4, stringUnderTest.getTreeDepth());
|
||||
}
|
||||
|
||||
public void testBalance() {
|
||||
int numberOfPieces = 10000;
|
||||
int pieceSize = 64;
|
||||
byte[] testBytes = ByteStringTest.getTestBytes(numberOfPieces * pieceSize, 113377L);
|
||||
|
||||
// Build up a big ByteString from smaller pieces to force a rebalance
|
||||
ByteString concatenated = ByteString.EMPTY;
|
||||
for (int i = 0; i < numberOfPieces; ++i) {
|
||||
concatenated = concatenated.concat(ByteString.copyFrom(testBytes, i * pieceSize, pieceSize));
|
||||
}
|
||||
|
||||
assertEquals(classUnderTest + " from string must have the expected type",
|
||||
classUnderTest, getActualClassName(concatenated));
|
||||
assertTrue(classUnderTest + " underlying bytes must match after balancing",
|
||||
Arrays.equals(testBytes, concatenated.toByteArray()));
|
||||
ByteString testString = ByteString.copyFrom(testBytes);
|
||||
assertTrue(classUnderTest + " balanced string must equal flat string",
|
||||
concatenated.equals(testString));
|
||||
assertTrue(classUnderTest + " flat string must equal balanced string",
|
||||
testString.equals(concatenated));
|
||||
assertEquals(classUnderTest + " balanced string must have same hash code as flat string",
|
||||
testString.hashCode(), concatenated.hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testToString() throws UnsupportedEncodingException {
|
||||
String sourceString = "I love unicode \u1234\u5678 characters";
|
||||
ByteString sourceByteString = ByteString.copyFromUtf8(sourceString);
|
||||
int copies = 250;
|
||||
|
||||
// By building the RopeByteString by concatenating, this is actually a fairly strenuous test.
|
||||
StringBuilder builder = new StringBuilder(copies * sourceString.length());
|
||||
ByteString unicode = ByteString.EMPTY;
|
||||
for (int i = 0; i < copies; ++i) {
|
||||
builder.append(sourceString);
|
||||
unicode = RopeByteString.concatenate(unicode, sourceByteString);
|
||||
}
|
||||
String testString = builder.toString();
|
||||
|
||||
assertEquals(classUnderTest + " from string must have the expected type",
|
||||
classUnderTest, getActualClassName(unicode));
|
||||
String roundTripString = unicode.toString(UTF_8);
|
||||
assertEquals(classUnderTest + " unicode bytes must match",
|
||||
testString, roundTripString);
|
||||
ByteString flatString = ByteString.copyFromUtf8(testString);
|
||||
assertEquals(classUnderTest + " string must equal the flat string", flatString, unicode);
|
||||
assertEquals(classUnderTest + " string must must have same hashCode as the flat string",
|
||||
flatString.hashCode(), unicode.hashCode());
|
||||
}
|
||||
}
|
||||
@@ -1,320 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.Descriptors.FileDescriptor;
|
||||
import com.google.protobuf.Descriptors.MethodDescriptor;
|
||||
import google.protobuf.no_generic_services_test.UnittestNoGenericServices;
|
||||
import protobuf_unittest.MessageWithNoOuter;
|
||||
import protobuf_unittest.ServiceWithNoOuter;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestService;
|
||||
import protobuf_unittest.UnittestProto.FooRequest;
|
||||
import protobuf_unittest.UnittestProto.FooResponse;
|
||||
import protobuf_unittest.UnittestProto.BarRequest;
|
||||
import protobuf_unittest.UnittestProto.BarResponse;
|
||||
|
||||
import org.easymock.classextension.EasyMock;
|
||||
import org.easymock.classextension.IMocksControl;
|
||||
import org.easymock.IArgumentMatcher;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests services and stubs.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class ServiceTest extends TestCase {
|
||||
private IMocksControl control;
|
||||
private RpcController mockController;
|
||||
|
||||
private final Descriptors.MethodDescriptor fooDescriptor =
|
||||
TestService.getDescriptor().getMethods().get(0);
|
||||
private final Descriptors.MethodDescriptor barDescriptor =
|
||||
TestService.getDescriptor().getMethods().get(1);
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
control = EasyMock.createStrictControl();
|
||||
mockController = control.createMock(RpcController.class);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
/** Tests Service.callMethod(). */
|
||||
public void testCallMethod() throws Exception {
|
||||
FooRequest fooRequest = FooRequest.newBuilder().build();
|
||||
BarRequest barRequest = BarRequest.newBuilder().build();
|
||||
MockCallback<Message> fooCallback = new MockCallback<Message>();
|
||||
MockCallback<Message> barCallback = new MockCallback<Message>();
|
||||
TestService mockService = control.createMock(TestService.class);
|
||||
|
||||
mockService.foo(EasyMock.same(mockController), EasyMock.same(fooRequest),
|
||||
this.<FooResponse>wrapsCallback(fooCallback));
|
||||
mockService.bar(EasyMock.same(mockController), EasyMock.same(barRequest),
|
||||
this.<BarResponse>wrapsCallback(barCallback));
|
||||
control.replay();
|
||||
|
||||
mockService.callMethod(fooDescriptor, mockController,
|
||||
fooRequest, fooCallback);
|
||||
mockService.callMethod(barDescriptor, mockController,
|
||||
barRequest, barCallback);
|
||||
control.verify();
|
||||
}
|
||||
|
||||
/** Tests Service.get{Request,Response}Prototype(). */
|
||||
public void testGetPrototype() throws Exception {
|
||||
TestService mockService = control.createMock(TestService.class);
|
||||
|
||||
assertSame(mockService.getRequestPrototype(fooDescriptor),
|
||||
FooRequest.getDefaultInstance());
|
||||
assertSame(mockService.getResponsePrototype(fooDescriptor),
|
||||
FooResponse.getDefaultInstance());
|
||||
assertSame(mockService.getRequestPrototype(barDescriptor),
|
||||
BarRequest.getDefaultInstance());
|
||||
assertSame(mockService.getResponsePrototype(barDescriptor),
|
||||
BarResponse.getDefaultInstance());
|
||||
}
|
||||
|
||||
/** Tests generated stubs. */
|
||||
public void testStub() throws Exception {
|
||||
FooRequest fooRequest = FooRequest.newBuilder().build();
|
||||
BarRequest barRequest = BarRequest.newBuilder().build();
|
||||
MockCallback<FooResponse> fooCallback = new MockCallback<FooResponse>();
|
||||
MockCallback<BarResponse> barCallback = new MockCallback<BarResponse>();
|
||||
RpcChannel mockChannel = control.createMock(RpcChannel.class);
|
||||
TestService stub = TestService.newStub(mockChannel);
|
||||
|
||||
mockChannel.callMethod(
|
||||
EasyMock.same(fooDescriptor),
|
||||
EasyMock.same(mockController),
|
||||
EasyMock.same(fooRequest),
|
||||
EasyMock.same(FooResponse.getDefaultInstance()),
|
||||
this.<Message>wrapsCallback(fooCallback));
|
||||
mockChannel.callMethod(
|
||||
EasyMock.same(barDescriptor),
|
||||
EasyMock.same(mockController),
|
||||
EasyMock.same(barRequest),
|
||||
EasyMock.same(BarResponse.getDefaultInstance()),
|
||||
this.<Message>wrapsCallback(barCallback));
|
||||
control.replay();
|
||||
|
||||
stub.foo(mockController, fooRequest, fooCallback);
|
||||
stub.bar(mockController, barRequest, barCallback);
|
||||
control.verify();
|
||||
}
|
||||
|
||||
/** Tests generated blocking stubs. */
|
||||
public void testBlockingStub() throws Exception {
|
||||
FooRequest fooRequest = FooRequest.newBuilder().build();
|
||||
BarRequest barRequest = BarRequest.newBuilder().build();
|
||||
BlockingRpcChannel mockChannel =
|
||||
control.createMock(BlockingRpcChannel.class);
|
||||
TestService.BlockingInterface stub =
|
||||
TestService.newBlockingStub(mockChannel);
|
||||
|
||||
FooResponse fooResponse = FooResponse.newBuilder().build();
|
||||
BarResponse barResponse = BarResponse.newBuilder().build();
|
||||
|
||||
EasyMock.expect(mockChannel.callBlockingMethod(
|
||||
EasyMock.same(fooDescriptor),
|
||||
EasyMock.same(mockController),
|
||||
EasyMock.same(fooRequest),
|
||||
EasyMock.same(FooResponse.getDefaultInstance()))).andReturn(fooResponse);
|
||||
EasyMock.expect(mockChannel.callBlockingMethod(
|
||||
EasyMock.same(barDescriptor),
|
||||
EasyMock.same(mockController),
|
||||
EasyMock.same(barRequest),
|
||||
EasyMock.same(BarResponse.getDefaultInstance()))).andReturn(barResponse);
|
||||
control.replay();
|
||||
|
||||
assertSame(fooResponse, stub.foo(mockController, fooRequest));
|
||||
assertSame(barResponse, stub.bar(mockController, barRequest));
|
||||
control.verify();
|
||||
}
|
||||
|
||||
public void testNewReflectiveService() {
|
||||
ServiceWithNoOuter.Interface impl =
|
||||
control.createMock(ServiceWithNoOuter.Interface.class);
|
||||
RpcController controller = control.createMock(RpcController.class);
|
||||
Service service = ServiceWithNoOuter.newReflectiveService(impl);
|
||||
|
||||
MethodDescriptor fooMethod =
|
||||
ServiceWithNoOuter.getDescriptor().findMethodByName("Foo");
|
||||
MessageWithNoOuter request = MessageWithNoOuter.getDefaultInstance();
|
||||
RpcCallback<Message> callback = new RpcCallback<Message>() {
|
||||
public void run(Message parameter) {
|
||||
// No reason this should be run.
|
||||
fail();
|
||||
}
|
||||
};
|
||||
RpcCallback<TestAllTypes> specializedCallback =
|
||||
RpcUtil.specializeCallback(callback);
|
||||
|
||||
impl.foo(EasyMock.same(controller), EasyMock.same(request),
|
||||
EasyMock.same(specializedCallback));
|
||||
EasyMock.expectLastCall();
|
||||
|
||||
control.replay();
|
||||
|
||||
service.callMethod(fooMethod, controller, request, callback);
|
||||
|
||||
control.verify();
|
||||
}
|
||||
|
||||
public void testNewReflectiveBlockingService() throws ServiceException {
|
||||
ServiceWithNoOuter.BlockingInterface impl =
|
||||
control.createMock(ServiceWithNoOuter.BlockingInterface.class);
|
||||
RpcController controller = control.createMock(RpcController.class);
|
||||
BlockingService service =
|
||||
ServiceWithNoOuter.newReflectiveBlockingService(impl);
|
||||
|
||||
MethodDescriptor fooMethod =
|
||||
ServiceWithNoOuter.getDescriptor().findMethodByName("Foo");
|
||||
MessageWithNoOuter request = MessageWithNoOuter.getDefaultInstance();
|
||||
|
||||
TestAllTypes expectedResponse = TestAllTypes.getDefaultInstance();
|
||||
EasyMock.expect(impl.foo(EasyMock.same(controller), EasyMock.same(request)))
|
||||
.andReturn(expectedResponse);
|
||||
|
||||
control.replay();
|
||||
|
||||
Message response =
|
||||
service.callBlockingMethod(fooMethod, controller, request);
|
||||
assertEquals(expectedResponse, response);
|
||||
|
||||
control.verify();
|
||||
}
|
||||
|
||||
public void testNoGenericServices() throws Exception {
|
||||
// Non-services should be usable.
|
||||
UnittestNoGenericServices.TestMessage message =
|
||||
UnittestNoGenericServices.TestMessage.newBuilder()
|
||||
.setA(123)
|
||||
.setExtension(UnittestNoGenericServices.testExtension, 456)
|
||||
.build();
|
||||
assertEquals(123, message.getA());
|
||||
assertEquals(1, UnittestNoGenericServices.TestEnum.FOO.getNumber());
|
||||
|
||||
// Build a list of the class names nested in UnittestNoGenericServices.
|
||||
String outerName = "google.protobuf.no_generic_services_test." +
|
||||
"UnittestNoGenericServices";
|
||||
Class<?> outerClass = Class.forName(outerName);
|
||||
|
||||
Set<String> innerClassNames = new HashSet<String>();
|
||||
for (Class<?> innerClass : outerClass.getClasses()) {
|
||||
String fullName = innerClass.getName();
|
||||
// Figure out the unqualified name of the inner class.
|
||||
// Note: Surprisingly, the full name of an inner class will be separated
|
||||
// from the outer class name by a '$' rather than a '.'. This is not
|
||||
// mentioned in the documentation for java.lang.Class. I don't want to
|
||||
// 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));
|
||||
}
|
||||
|
||||
// No service class should have been generated.
|
||||
assertTrue(innerClassNames.contains("TestMessage"));
|
||||
assertTrue(innerClassNames.contains("TestEnum"));
|
||||
assertFalse(innerClassNames.contains("TestService"));
|
||||
|
||||
// But descriptors are there.
|
||||
FileDescriptor file = UnittestNoGenericServices.getDescriptor();
|
||||
assertEquals(1, file.getServices().size());
|
||||
assertEquals("TestService", file.getServices().get(0).getName());
|
||||
assertEquals(1, file.getServices().get(0).getMethods().size());
|
||||
assertEquals("Foo",
|
||||
file.getServices().get(0).getMethods().get(0).getName());
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
/**
|
||||
* wrapsCallback() is an EasyMock argument predicate. wrapsCallback(c)
|
||||
* matches a callback if calling that callback causes c to be called.
|
||||
* In other words, c wraps the given callback.
|
||||
*/
|
||||
private <Type extends Message> RpcCallback<Type> wrapsCallback(
|
||||
MockCallback<?> callback) {
|
||||
EasyMock.reportMatcher(new WrapsCallback(callback));
|
||||
return null;
|
||||
}
|
||||
|
||||
/** The parameter to wrapsCallback() must be a MockCallback. */
|
||||
private static class MockCallback<Type extends Message>
|
||||
implements RpcCallback<Type> {
|
||||
private boolean called = false;
|
||||
|
||||
public boolean isCalled() { return called; }
|
||||
|
||||
public void reset() { called = false; }
|
||||
public void run(Type message) { called = true; }
|
||||
}
|
||||
|
||||
/** Implementation of the wrapsCallback() argument matcher. */
|
||||
private static class WrapsCallback implements IArgumentMatcher {
|
||||
private MockCallback<?> callback;
|
||||
|
||||
public WrapsCallback(MockCallback<?> callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean matches(Object actual) {
|
||||
if (!(actual instanceof RpcCallback)) {
|
||||
return false;
|
||||
}
|
||||
RpcCallback actualCallback = (RpcCallback)actual;
|
||||
|
||||
callback.reset();
|
||||
actualCallback.run(null);
|
||||
return callback.isCalled();
|
||||
}
|
||||
|
||||
public void appendTo(StringBuffer buffer) {
|
||||
buffer.append("wrapsCallback(mockCallback)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user