aboutsummaryrefslogtreecommitdiff
path: root/java
diff options
context:
space:
mode:
Diffstat (limited to 'java')
-rw-r--r--java/README.md (renamed from java/README.txt)8
-rw-r--r--java/pom.xml116
-rw-r--r--java/src/main/java/com/google/protobuf/AbstractMessage.java6
-rw-r--r--java/src/main/java/com/google/protobuf/AbstractMessageLite.java16
-rw-r--r--java/src/main/java/com/google/protobuf/AbstractProtobufList.java136
-rw-r--r--java/src/main/java/com/google/protobuf/BooleanArrayList.java244
-rw-r--r--java/src/main/java/com/google/protobuf/BoundedByteString.java2
-rw-r--r--java/src/main/java/com/google/protobuf/ByteString.java68
-rw-r--r--java/src/main/java/com/google/protobuf/CodedInputStream.java6
-rw-r--r--java/src/main/java/com/google/protobuf/CodedOutputStream.java17
-rw-r--r--java/src/main/java/com/google/protobuf/Descriptors.java104
-rw-r--r--java/src/main/java/com/google/protobuf/DoubleArrayList.java243
-rw-r--r--java/src/main/java/com/google/protobuf/DynamicMessage.java11
-rw-r--r--java/src/main/java/com/google/protobuf/FieldSet.java56
-rw-r--r--java/src/main/java/com/google/protobuf/FloatArrayList.java242
-rw-r--r--java/src/main/java/com/google/protobuf/GeneratedMessage.java256
-rw-r--r--java/src/main/java/com/google/protobuf/GeneratedMessageLite.java489
-rw-r--r--java/src/main/java/com/google/protobuf/IntArrayList.java242
-rw-r--r--java/src/main/java/com/google/protobuf/Internal.java223
-rw-r--r--java/src/main/java/com/google/protobuf/LazyFieldLite.java249
-rw-r--r--java/src/main/java/com/google/protobuf/LazyStringArrayList.java87
-rw-r--r--java/src/main/java/com/google/protobuf/LiteralByteString.java19
-rw-r--r--java/src/main/java/com/google/protobuf/LongArrayList.java242
-rw-r--r--java/src/main/java/com/google/protobuf/MapField.java63
-rw-r--r--java/src/main/java/com/google/protobuf/MapFieldLite.java376
-rw-r--r--java/src/main/java/com/google/protobuf/Message.java4
-rw-r--r--java/src/main/java/com/google/protobuf/MessageReflection.java60
-rw-r--r--java/src/main/java/com/google/protobuf/MutabilityOracle.java48
-rw-r--r--java/src/main/java/com/google/protobuf/Parser.java4
-rw-r--r--java/src/main/java/com/google/protobuf/ProtobufArrayList.java95
-rw-r--r--java/src/main/java/com/google/protobuf/RopeByteString.java11
-rw-r--r--java/src/main/java/com/google/protobuf/TextFormat.java123
-rw-r--r--java/src/main/java/com/google/protobuf/Utf8.java2
-rw-r--r--java/src/main/java/com/google/protobuf/WireFormat.java82
-rw-r--r--java/src/test/java/com/google/protobuf/BooleanArrayListTest.java473
-rw-r--r--java/src/test/java/com/google/protobuf/BoundedByteStringTest.java15
-rw-r--r--java/src/test/java/com/google/protobuf/ByteStringTest.java19
-rw-r--r--java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java14
-rw-r--r--java/src/test/java/com/google/protobuf/DescriptorsTest.java53
-rw-r--r--java/src/test/java/com/google/protobuf/DoubleArrayListTest.java473
-rw-r--r--java/src/test/java/com/google/protobuf/FloatArrayListTest.java473
-rw-r--r--java/src/test/java/com/google/protobuf/GeneratedMessageTest.java12
-rw-r--r--java/src/test/java/com/google/protobuf/IntArrayListTest.java473
-rw-r--r--java/src/test/java/com/google/protobuf/IsValidUtf8Test.java7
-rw-r--r--java/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java21
-rw-r--r--java/src/test/java/com/google/protobuf/LazyFieldLiteTest.java139
-rw-r--r--java/src/test/java/com/google/protobuf/LazyMessageLiteTest.java19
-rw-r--r--java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java188
-rw-r--r--java/src/test/java/com/google/protobuf/LiteTest.java1314
-rw-r--r--java/src/test/java/com/google/protobuf/LiteralByteStringTest.java36
-rw-r--r--java/src/test/java/com/google/protobuf/LongArrayListTest.java473
-rw-r--r--java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java161
-rw-r--r--java/src/test/java/com/google/protobuf/MapForProto2Test.java162
-rw-r--r--java/src/test/java/com/google/protobuf/MapTest.java303
-rw-r--r--java/src/test/java/com/google/protobuf/ProtobufArrayListTest.java303
-rw-r--r--java/src/test/java/com/google/protobuf/RopeByteStringSubstringTest.java32
-rw-r--r--java/src/test/java/com/google/protobuf/RopeByteStringTest.java56
-rw-r--r--java/src/test/java/com/google/protobuf/TestUtil.java6
-rw-r--r--java/src/test/java/com/google/protobuf/TextFormatTest.java6
-rw-r--r--java/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java2
-rw-r--r--java/src/test/java/com/google/protobuf/WellKnownTypesTest.java65
-rw-r--r--java/src/test/java/com/google/protobuf/field_presence_test.proto26
-rw-r--r--java/src/test/java/com/google/protobuf/lazy_fields_lite.proto9
-rw-r--r--java/src/test/java/com/google/protobuf/map_for_proto2_lite_test.proto7
-rw-r--r--java/src/test/java/com/google/protobuf/map_for_proto2_test.proto7
-rw-r--r--java/src/test/java/com/google/protobuf/map_initialization_order_test.proto61
-rw-r--r--java/src/test/java/com/google/protobuf/map_test.proto5
-rw-r--r--java/src/test/java/com/google/protobuf/test_bad_identifiers.proto1
68 files changed, 8691 insertions, 673 deletions
diff --git a/java/README.txt b/java/README.md
index 57e26a96..060d9ac6 100644
--- a/java/README.txt
+++ b/java/README.md
@@ -1,4 +1,8 @@
Protocol Buffers - Google's data interchange format
+===================================================
+
+[![Build Status](https://travis-ci.org/google/protobuf.svg?branch=master)](https://travis-ci.org/google/protobuf)
+
Copyright 2008 Google Inc.
This directory contains the Java Protocol Buffers runtime library.
@@ -50,17 +54,19 @@ 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
+ $ 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
============================
diff --git a/java/pom.xml b/java/pom.xml
index bbadc656..6877ac27 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -10,7 +10,7 @@
</parent>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
- <version>3.0.0-pre</version>
+ <version>3.0.0-alpha-4-pre</version>
<packaging>bundle</packaging>
<name>Protocol Buffer Java API</name>
<description>
@@ -81,7 +81,17 @@
<exec executable="../src/protoc">
<arg value="--java_out=target/generated-sources" />
<arg value="--proto_path=../src" />
+ <arg value="../src/google/protobuf/any.proto" />
+ <arg value="../src/google/protobuf/api.proto" />
<arg value="../src/google/protobuf/descriptor.proto" />
+ <arg value="../src/google/protobuf/duration.proto" />
+ <arg value="../src/google/protobuf/empty.proto" />
+ <arg value="../src/google/protobuf/field_mask.proto" />
+ <arg value="../src/google/protobuf/source_context.proto" />
+ <arg value="../src/google/protobuf/struct.proto" />
+ <arg value="../src/google/protobuf/timestamp.proto" />
+ <arg value="../src/google/protobuf/type.proto" />
+ <arg value="../src/google/protobuf/wrappers.proto" />
</exec>
</tasks>
<sourceRoot>target/generated-sources</sourceRoot>
@@ -130,10 +140,12 @@
<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" />
+ <arg value="../src/google/protobuf/unittest_well_known_types.proto" />
<arg value="src/test/java/com/google/protobuf/field_presence_test.proto" />
<arg value="src/test/java/com/google/protobuf/map_for_proto2_lite_test.proto" />
<arg value="src/test/java/com/google/protobuf/map_for_proto2_test.proto" />
<arg value="src/test/java/com/google/protobuf/map_test.proto" />
+ <arg value="src/test/java/com/google/protobuf/map_initialization_order_test.proto" />
</exec>
</tasks>
<testSourceRoot>target/generated-test-sources</testSourceRoot>
@@ -152,14 +164,100 @@
<instructions>
<Bundle-DocURL>https://developers.google.com/protocol-buffers/</Bundle-DocURL>
<Bundle-SymbolicName>com.google.protobuf</Bundle-SymbolicName>
- <Export-Package>com.google.protobuf;version=3.0.0-pre</Export-Package>
+ <Export-Package>com.google.protobuf;version=3.0.0-alpha-3</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
+ <resources>
+ <resource>
+ <directory>../src</directory>
+ <includes>
+ <include>google/protobuf/any.proto</include>
+ <include>google/protobuf/api.proto</include>
+ <include>google/protobuf/descriptor.proto</include>
+ <include>google/protobuf/duration.proto</include>
+ <include>google/protobuf/empty.proto</include>
+ <include>google/protobuf/field_mask.proto</include>
+ <include>google/protobuf/source_context.proto</include>
+ <include>google/protobuf/struct.proto</include>
+ <include>google/protobuf/timestamp.proto</include>
+ <include>google/protobuf/type.proto</include>
+ <include>google/protobuf/wrappers.proto</include>
+ </includes>
+ </resource>
+ </resources>
</build>
<profiles>
<profile>
+ <id>release</id>
+ <distributionManagement>
+ <snapshotRepository>
+ <id>sonatype-nexus-staging</id>
+ <url>https://oss.sonatype.org/content/repositories/snapshots</url>
+ </snapshotRepository>
+ <repository>
+ <id>sonatype-nexus-staging</id>
+ <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
+ </repository>
+ </distributionManagement>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>2.2.1</version>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <goals>
+ <goal>jar-no-fork</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.9.1</version>
+ <executions>
+ <execution>
+ <id>attach-javadocs</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-gpg-plugin</artifactId>
+ <version>1.5</version>
+ <executions>
+ <execution>
+ <id>sign-artifacts</id>
+ <phase>verify</phase>
+ <goals>
+ <goal>sign</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.sonatype.plugins</groupId>
+ <artifactId>nexus-staging-maven-plugin</artifactId>
+ <version>1.6.3</version>
+ <extensions>true</extensions>
+ <configuration>
+ <serverId>sonatype-nexus-staging</serverId>
+ <nexusUrl>https://oss.sonatype.org/</nexusUrl>
+ <autoReleaseAfterClose>false</autoReleaseAfterClose>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
<id>lite</id>
<build>
<plugins>
@@ -169,25 +267,33 @@
<includes>
<include>**/AbstractMessageLite.java</include>
<include>**/AbstractParser.java</include>
+ <include>**/AbstractProtobufList.java</include>
<include>**/BoundedByteString.java</include>
+ <include>**/BooleanArrayList.java</include>
<include>**/ByteString.java</include>
<include>**/CodedInputStream.java</include>
<include>**/CodedOutputStream.java</include>
+ <include>**/DoublerrayList.java</include>
<include>**/ExtensionLite.java</include>
<include>**/ExtensionRegistryLite.java</include>
<include>**/FieldSet.java</include>
+ <include>**/FloatArrayList.java</include>
<include>**/GeneratedMessageLite.java</include>
+ <include>**/IntArrayList.java</include>
<include>**/Internal.java</include>
<include>**/InvalidProtocolBufferException.java</include>
<include>**/LazyFieldLite.java</include>
<include>**/LazyStringArrayList.java</include>
<include>**/LazyStringList.java</include>
<include>**/LiteralByteString.java</include>
+ <include>**/LongArrayList.java</include>
<include>**/MapEntryLite.java</include>
<include>**/MapFieldLite.java</include>
<include>**/MessageLite.java</include>
<include>**/MessageLiteOrBuilder.java</include>
+ <include>**/MutabilityOracle.java</include>
<include>**/Parser.java</include>
+ <include>**/ProtobufArrayList.java</include>
<include>**/ProtocolStringList.java</include>
<include>**/RopeByteString.java</include>
<include>**/SmallSortedMap.java</include>
@@ -199,8 +305,14 @@
</includes>
<testIncludes>
<testInclude>**/*Lite.java</testInclude>
+ <testInclude>**/BooleanArrayListTest.java</testInclude>
+ <testInclude>**/DoubleArrayListTest.java</testInclude>
+ <testInclude>**/FloatArrayListTest.java</testInclude>
+ <testInclude>**/IntArrayListTest.java</testInclude>
<testInclude>**/LazyMessageLiteTest.java</testInclude>
<testInclude>**/LiteTest.java</testInclude>
+ <testInclude>**/LongArrayListTest.java</testInclude>
+ <testInclude>**/ProtobufArrayListTest.java</testInclude>
<testInclude>**/UnknownFieldSetLiteTest.java</testInclude>
</testIncludes>
</configuration>
diff --git a/java/src/main/java/com/google/protobuf/AbstractMessage.java b/java/src/main/java/com/google/protobuf/AbstractMessage.java
index 6de4cae3..cc89173a 100644
--- a/java/src/main/java/com/google/protobuf/AbstractMessage.java
+++ b/java/src/main/java/com/google/protobuf/AbstractMessage.java
@@ -83,10 +83,10 @@ public abstract class AbstractMessage extends AbstractMessageLite
}
public void writeTo(final CodedOutputStream output) throws IOException {
- MessageReflection.writeMessageTo(this, output, false);
+ MessageReflection.writeMessageTo(this, getAllFields(), output, false);
}
- private int memoizedSize = -1;
+ protected int memoizedSize = -1;
public int getSerializedSize() {
int size = memoizedSize;
@@ -94,7 +94,7 @@ public abstract class AbstractMessage extends AbstractMessageLite
return size;
}
- memoizedSize = MessageReflection.getSerializedSize(this);
+ memoizedSize = MessageReflection.getSerializedSize(this, getAllFields());
return memoizedSize;
}
diff --git a/java/src/main/java/com/google/protobuf/AbstractMessageLite.java b/java/src/main/java/com/google/protobuf/AbstractMessageLite.java
index aac4fa77..12384983 100644
--- a/java/src/main/java/com/google/protobuf/AbstractMessageLite.java
+++ b/java/src/main/java/com/google/protobuf/AbstractMessageLite.java
@@ -31,8 +31,8 @@
package com.google.protobuf;
import java.io.FilterInputStream;
-import java.io.InputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
@@ -109,6 +109,11 @@ public abstract class AbstractMessageLite implements MessageLite {
}
}
+ protected static <T> void addAll(final Iterable<T> values,
+ final Collection<? super T> list) {
+ Builder.addAll(values, list);
+ }
+
/**
* A partial implementation of the {@link Message.Builder} interface which
* implements as many methods of that interface as possible in terms of
@@ -320,12 +325,15 @@ public abstract class AbstractMessageLite implements MessageLite {
* 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. When that happens, some elements of {@code values} may have already
- * been added to the result {@code list}.
+ * @throws NullPointerException if {@code values} or any of the elements of
+ * {@code values} is null. When that happens, some elements of
+ * {@code values} may have already been added to the result {@code list}.
*/
protected static <T> void addAll(final Iterable<T> values,
final Collection<? super T> list) {
+ if (values == null) {
+ throw new NullPointerException();
+ }
if (values instanceof LazyStringList) {
// For StringOrByteStringLists, check the underlying elements to avoid
// forcing conversions of ByteStrings to Strings.
diff --git a/java/src/main/java/com/google/protobuf/AbstractProtobufList.java b/java/src/main/java/com/google/protobuf/AbstractProtobufList.java
new file mode 100644
index 00000000..bb6446b2
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/AbstractProtobufList.java
@@ -0,0 +1,136 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import com.google.protobuf.Internal.ProtobufList;
+
+import java.util.AbstractList;
+import java.util.Collection;
+
+/**
+ * An abstract implementation of {@link ProtobufList} which manages mutability semantics. All mutate
+ * methods are check if the list is mutable before proceeding. Subclasses must invoke
+ * {@link #ensureIsMutable()} manually when overriding those methods.
+ */
+abstract class AbstractProtobufList<E> extends AbstractList<E> implements ProtobufList<E> {
+
+ /**
+ * Whether or not this list is modifiable.
+ */
+ private boolean isMutable;
+
+ /**
+ * Constructs a mutable list by default.
+ */
+ AbstractProtobufList() {
+ isMutable = true;
+ }
+
+ @Override
+ public boolean add(E e) {
+ ensureIsMutable();
+ return super.add(e);
+ }
+
+ @Override
+ public void add(int index, E element) {
+ ensureIsMutable();
+ super.add(index, element);
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends E> c) {
+ ensureIsMutable();
+ return super.addAll(c);
+ }
+
+ @Override
+ public boolean addAll(int index, Collection<? extends E> c) {
+ ensureIsMutable();
+ return super.addAll(index, c);
+ }
+
+ @Override
+ public void clear() {
+ ensureIsMutable();
+ super.clear();
+ }
+
+ @Override
+ public boolean isModifiable() {
+ return isMutable;
+ }
+
+ @Override
+ public final void makeImmutable() {
+ isMutable = false;
+ }
+
+ @Override
+ public E remove(int index) {
+ ensureIsMutable();
+ return super.remove(index);
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ ensureIsMutable();
+ return super.remove(o);
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c) {
+ ensureIsMutable();
+ return super.removeAll(c);
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c) {
+ ensureIsMutable();
+ return super.retainAll(c);
+ }
+
+ @Override
+ public E set(int index, E element) {
+ ensureIsMutable();
+ return super.set(index, element);
+ }
+
+ /**
+ * Throws an {@link UnsupportedOperationException} if the list is immutable. Subclasses are
+ * responsible for invoking this method on mutate operations.
+ */
+ protected void ensureIsMutable() {
+ if (!isMutable) {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/java/src/main/java/com/google/protobuf/BooleanArrayList.java b/java/src/main/java/com/google/protobuf/BooleanArrayList.java
new file mode 100644
index 00000000..45492d2f
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/BooleanArrayList.java
@@ -0,0 +1,244 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import com.google.protobuf.Internal.BooleanList;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.RandomAccess;
+
+/**
+ * An implementation of {@link BooleanList} on top of a primitive array.
+ *
+ * @author dweis@google.com (Daniel Weis)
+ */
+final class BooleanArrayList
+ extends AbstractProtobufList<Boolean> implements BooleanList, RandomAccess {
+
+ private static final int DEFAULT_CAPACITY = 10;
+
+ private static final BooleanArrayList EMPTY_LIST = new BooleanArrayList();
+ static {
+ EMPTY_LIST.makeImmutable();
+ }
+
+ public static BooleanArrayList emptyList() {
+ return EMPTY_LIST;
+ }
+
+ /**
+ * The backing store for the list.
+ */
+ private boolean[] array;
+
+ /**
+ * The size of the list distinct from the length of the array. That is, it is the number of
+ * elements set in the list.
+ */
+ private int size;
+
+ /**
+ * Constructs a new mutable {@code BooleanArrayList}.
+ */
+ BooleanArrayList() {
+ array = new boolean[DEFAULT_CAPACITY];
+ size = 0;
+ }
+
+ /**
+ * Constructs a new mutable {@code BooleanArrayList} containing the same elements as
+ * {@code other}.
+ */
+ BooleanArrayList(List<Boolean> other) {
+ if (other instanceof BooleanArrayList) {
+ BooleanArrayList list = (BooleanArrayList) other;
+ array = list.array.clone();
+ size = list.size;
+ } else {
+ size = other.size();
+ array = new boolean[size];
+ for (int i = 0; i < size; i++) {
+ array[i] = other.get(i);
+ }
+ }
+ }
+
+ @Override
+ public Boolean get(int index) {
+ return getBoolean(index);
+ }
+
+ @Override
+ public boolean getBoolean(int index) {
+ ensureIndexInRange(index);
+ return array[index];
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+
+ @Override
+ public Boolean set(int index, Boolean element) {
+ return setBoolean(index, element);
+ }
+
+ @Override
+ public boolean setBoolean(int index, boolean element) {
+ ensureIsMutable();
+ ensureIndexInRange(index);
+ boolean previousValue = array[index];
+ array[index] = element;
+ return previousValue;
+ }
+
+ @Override
+ public void add(int index, Boolean element) {
+ addBoolean(index, element);
+ }
+
+ /**
+ * Like {@link #add(Boolean)} but more efficient in that it doesn't box the element.
+ */
+ @Override
+ public void addBoolean(boolean element) {
+ addBoolean(size, element);
+ }
+
+ /**
+ * Like {@link #add(int, Boolean)} but more efficient in that it doesn't box the element.
+ */
+ private void addBoolean(int index, boolean element) {
+ ensureIsMutable();
+ if (index < 0 || index > size) {
+ throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index));
+ }
+
+ if (size < array.length) {
+ // Shift everything over to make room
+ System.arraycopy(array, index, array, index + 1, size - index);
+ } else {
+ // Resize to 1.5x the size
+ int length = ((size * 3) / 2) + 1;
+ boolean[] newArray = new boolean[length];
+
+ // Copy the first part directly
+ System.arraycopy(array, 0, newArray, 0, index);
+
+ // Copy the rest shifted over by one to make room
+ System.arraycopy(array, index, newArray, index + 1, size - index);
+ array = newArray;
+ }
+
+ array[index] = element;
+ size++;
+ modCount++;
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends Boolean> collection) {
+ ensureIsMutable();
+
+ if (collection == null) {
+ throw new NullPointerException();
+ }
+
+ // We specialize when adding another BooleanArrayList to avoid boxing elements.
+ if (!(collection instanceof BooleanArrayList)) {
+ return super.addAll(collection);
+ }
+
+ BooleanArrayList list = (BooleanArrayList) collection;
+ if (list.size == 0) {
+ return false;
+ }
+
+ int overflow = Integer.MAX_VALUE - size;
+ if (overflow < list.size) {
+ // We can't actually represent a list this large.
+ throw new OutOfMemoryError();
+ }
+
+ int newSize = size + list.size;
+ if (newSize > array.length) {
+ array = Arrays.copyOf(array, newSize);
+ }
+
+ System.arraycopy(list.array, 0, array, size, list.size);
+ size = newSize;
+ modCount++;
+ return true;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ ensureIsMutable();
+ for (int i = 0; i < size; i++) {
+ if (o.equals(array[i])) {
+ System.arraycopy(array, i + 1, array, i, size - i);
+ size--;
+ modCount++;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Boolean remove(int index) {
+ ensureIsMutable();
+ ensureIndexInRange(index);
+ boolean value = array[index];
+ System.arraycopy(array, index + 1, array, index, size - index);
+ size--;
+ modCount++;
+ return value;
+ }
+
+ /**
+ * Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an
+ * {@link IndexOutOfBoundsException} if it is not.
+ *
+ * @param index the index to verify is in range
+ */
+ private void ensureIndexInRange(int index) {
+ if (index < 0 || index >= size) {
+ throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index));
+ }
+ }
+
+ private String makeOutOfBoundsExceptionMessage(int index) {
+ return "Index:" + index + ", Size:" + size;
+ }
+}
diff --git a/java/src/main/java/com/google/protobuf/BoundedByteString.java b/java/src/main/java/com/google/protobuf/BoundedByteString.java
index 8cb6f463..b4c3fb1b 100644
--- a/java/src/main/java/com/google/protobuf/BoundedByteString.java
+++ b/java/src/main/java/com/google/protobuf/BoundedByteString.java
@@ -30,8 +30,8 @@
package com.google.protobuf;
-import java.io.InvalidObjectException;
import java.io.IOException;
+import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.util.NoSuchElementException;
diff --git a/java/src/main/java/com/google/protobuf/ByteString.java b/java/src/main/java/com/google/protobuf/ByteString.java
index cff1ee51..1d5d4e8a 100644
--- a/java/src/main/java/com/google/protobuf/ByteString.java
+++ b/java/src/main/java/com/google/protobuf/ByteString.java
@@ -37,6 +37,8 @@ import java.io.OutputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
@@ -259,6 +261,18 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
}
/**
+ * 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 charset encode using this charset
+ * @return new {@code ByteString}
+ */
+ public static ByteString copyFrom(String text, Charset charset) {
+ return new LiteralByteString(text.getBytes(charset));
+ }
+
+ /**
* Encodes {@code text} into a sequence of UTF-8 bytes and returns the
* result as a {@code ByteString}.
*
@@ -266,11 +280,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* @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);
- }
+ return new LiteralByteString(text.getBytes(Internal.UTF_8));
}
// =================================================================
@@ -504,7 +514,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
/**
* Internal (package private) implementation of
* {@link #copyTo(byte[],int,int,int)}.
- * It assumes that all error checking has already been performed and that
+ * It assumes that all error checking has already been performed and that
* {@code numberToCopy > 0}.
*/
protected abstract void copyToInternal(byte[] target, int sourceOffset,
@@ -543,7 +553,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* @throws IOException if an I/O error occurs.
*/
public abstract void writeTo(OutputStream out) throws IOException;
-
+
/**
* Writes a specified part of this byte string to an output stream.
*
@@ -569,7 +579,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
if (numberToWrite > 0) {
writeToInternal(out, sourceOffset, numberToWrite);
}
-
+
}
/**
@@ -596,7 +606,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* <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();
@@ -609,8 +619,36 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* @return new string
* @throws UnsupportedEncodingException if charset isn't recognized
*/
- public abstract String toString(String charsetName)
- throws UnsupportedEncodingException;
+ public String toString(String charsetName)
+ throws UnsupportedEncodingException {
+ try {
+ return toString(Charset.forName(charsetName));
+ } catch (UnsupportedCharsetException e) {
+ UnsupportedEncodingException exception = new UnsupportedEncodingException(charsetName);
+ exception.initCause(e);
+ throw exception;
+ }
+ }
+
+ /**
+ * Constructs a new {@code String} by decoding the bytes using the
+ * specified charset. Returns the same empty String if empty.
+ *
+ * @param charset encode using this charset
+ * @return new string
+ */
+ public String toString(Charset charset) {
+ return size() == 0 ? "" : toStringInternal(charset);
+ }
+
+ /**
+ * Constructs a new {@code String} by decoding the bytes using the
+ * specified charset.
+ *
+ * @param charset encode using this charset
+ * @return new string
+ */
+ protected abstract String toStringInternal(Charset charset);
// =================================================================
// UTF-8 decoding
@@ -621,11 +659,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* @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);
- }
+ return toString(Internal.UTF_8);
}
/**
@@ -828,7 +862,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
flushLastBuffer();
return ByteString.copyFrom(flushedBuffers);
}
-
+
/**
* Implement java.util.Arrays.copyOf() for jdk 1.5.
*/
diff --git a/java/src/main/java/com/google/protobuf/CodedInputStream.java b/java/src/main/java/com/google/protobuf/CodedInputStream.java
index 0ca00bab..d201f7c5 100644
--- a/java/src/main/java/com/google/protobuf/CodedInputStream.java
+++ b/java/src/main/java/com/google/protobuf/CodedInputStream.java
@@ -373,14 +373,14 @@ public final class CodedInputStream {
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");
+ final String result = new String(buffer, bufferPos, size, Internal.UTF_8);
bufferPos += size;
return result;
} else if (size == 0) {
return "";
} else {
// Slow path: Build a byte array first then copy it.
- return new String(readRawBytesSlowPath(size), "UTF-8");
+ return new String(readRawBytesSlowPath(size), Internal.UTF_8);
}
}
@@ -409,7 +409,7 @@ public final class CodedInputStream {
if (!Utf8.isValidUtf8(bytes, pos, pos + size)) {
throw InvalidProtocolBufferException.invalidUtf8();
}
- return new String(bytes, pos, size, "UTF-8");
+ return new String(bytes, pos, size, Internal.UTF_8);
}
/** Read a {@code group} field value from the stream. */
diff --git a/java/src/main/java/com/google/protobuf/CodedOutputStream.java b/java/src/main/java/com/google/protobuf/CodedOutputStream.java
index fafe0356..954fde08 100644
--- a/java/src/main/java/com/google/protobuf/CodedOutputStream.java
+++ b/java/src/main/java/com/google/protobuf/CodedOutputStream.java
@@ -32,7 +32,6 @@ package com.google.protobuf;
import java.io.IOException;
import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
/**
@@ -144,7 +143,7 @@ public final class CodedOutputStream {
int bufferSize) {
return newInstance(new ByteBufferOutputStream(byteBuffer), bufferSize);
}
-
+
private static class ByteBufferOutputStream extends OutputStream {
private final ByteBuffer byteBuffer;
public ByteBufferOutputStream(ByteBuffer byteBuffer) {
@@ -420,7 +419,7 @@ public final class CodedOutputStream {
// Unfortunately there does not appear to be any way to tell Java to encode
// UTF-8 directly into our buffer, so we have to let it create its own byte
// array and then copy.
- final byte[] bytes = value.getBytes("UTF-8");
+ final byte[] bytes = value.getBytes(Internal.UTF_8);
writeRawVarint32(bytes.length);
writeRawBytes(bytes);
}
@@ -750,7 +749,7 @@ public final class CodedOutputStream {
computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) +
computeLazyFieldSize(WireFormat.MESSAGE_SET_MESSAGE, value);
}
-
+
// -----------------------------------------------------------------
/**
@@ -827,13 +826,9 @@ public final class CodedOutputStream {
* {@code string} field.
*/
public static int computeStringSizeNoTag(final String value) {
- try {
- final byte[] bytes = value.getBytes("UTF-8");
- return computeRawVarint32Size(bytes.length) +
- bytes.length;
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException("UTF-8 not supported.", e);
- }
+ final byte[] bytes = value.getBytes(Internal.UTF_8);
+ return computeRawVarint32Size(bytes.length) +
+ bytes.length;
}
/**
diff --git a/java/src/main/java/com/google/protobuf/Descriptors.java b/java/src/main/java/com/google/protobuf/Descriptors.java
index d65e8b49..3658410c 100644
--- a/java/src/main/java/com/google/protobuf/Descriptors.java
+++ b/java/src/main/java/com/google/protobuf/Descriptors.java
@@ -43,7 +43,6 @@ import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.logging.Logger;
-import java.io.UnsupportedEncodingException;
/**
* Contains a collection of classes which describe protocol message types.
@@ -256,7 +255,7 @@ public final class Descriptors {
throws DescriptorValidationException {
return buildFrom(proto, dependencies, false);
}
-
+
/**
* Construct a {@code FileDescriptor}.
@@ -319,12 +318,7 @@ public final class Descriptors {
}
final byte[] descriptorBytes;
- try {
- descriptorBytes = descriptorData.toString().getBytes("ISO-8859-1");
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException(
- "Standard encoding ISO-8859-1 not supported by JVM.", e);
- }
+ descriptorBytes = descriptorData.toString().getBytes(Internal.ISO_8859_1);
FileDescriptorProto proto;
try {
@@ -495,9 +489,9 @@ public final class Descriptors {
proto.getExtension(i), this, null, i, true);
}
}
-
+
/**
- * Create a placeholder FileDescriptor for a message Descriptor.
+ * Create a placeholder FileDescriptor for a message Descriptor.
*/
FileDescriptor(String packageName, Descriptor message)
throws DescriptorValidationException {
@@ -561,7 +555,7 @@ public final class Descriptors {
extensions[i].setProto(proto.getExtension(i));
}
}
-
+
boolean supportsUnknownEnumValue() {
return getSyntax() == Syntax.PROTO3;
}
@@ -650,6 +644,30 @@ public final class Descriptors {
return false;
}
+ /** Determines if the given field number is reserved. */
+ public boolean isReservedNumber(final int number) {
+ for (final DescriptorProto.ReservedRange range :
+ proto.getReservedRangeList()) {
+ if (range.getStart() <= number && number < range.getEnd()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Determines if the given field name is reserved. */
+ public boolean isReservedName(final String name) {
+ if (name == null) {
+ throw new NullPointerException();
+ }
+ for (final String reservedName : proto.getReservedNameList()) {
+ if (reservedName.equals(name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Indicates whether the message can be extended. That is, whether it has
* any "extensions x to y" ranges declared on it.
@@ -745,7 +763,7 @@ public final class Descriptors {
this.fields = new FieldDescriptor[0];
this.extensions = new FieldDescriptor[0];
this.oneofs = new OneofDescriptor[0];
-
+
// Create a placeholder FileDescriptor to hold this message.
this.file = new FileDescriptor(packageName, this);
}
@@ -897,7 +915,7 @@ public final class Descriptors {
return (type == Type.STRING) && (getFile().getOptions().getJavaStringCheckUtf8());
}
- boolean isMapField() {
+ public boolean isMapField() {
return getType() == Type.MESSAGE && isRepeated()
&& getMessageType().getOptions().getMapEntry();
}
@@ -923,9 +941,18 @@ public final class Descriptors {
return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REPEATED;
}
- /** Does this field have the {@code [packed = true]} option? */
+ /** Does this field have the {@code [packed = true]} option or is this field
+ * packable in proto3 and not explicitly setted to unpacked?
+ */
public boolean isPacked() {
- return getOptions().getPacked();
+ if (!isPackable()) {
+ return false;
+ }
+ if (getFile().getSyntax() == FileDescriptor.Syntax.PROTO2) {
+ return getOptions().getPacked();
+ } else {
+ return !getOptions().hasPacked() || getOptions().getPacked();
+ }
}
/** Can this field be packed? i.e. is it a repeated primitive field? */
@@ -1333,8 +1360,8 @@ public final class Descriptors {
"Message type had default value.");
}
} catch (NumberFormatException e) {
- throw new DescriptorValidationException(this,
- "Could not parse default value: \"" +
+ throw new DescriptorValidationException(this,
+ "Could not parse default value: \"" +
proto.getDefaultValue() + '\"', e);
}
} else {
@@ -1456,7 +1483,7 @@ public final class Descriptors {
return file.pool.enumValuesByNumber.get(
new DescriptorPool.DescriptorIntPair(this, number));
}
-
+
/**
* Get the enum value for a number. If no enum value has this number,
* construct an EnumValueDescriptor for it.
@@ -1494,7 +1521,7 @@ public final class Descriptors {
// then remove the whole entry. This way unknown descriptors will
// be freed automatically and we don't need to do anything to
// clean-up unused map entries.
-
+
// Note: We must use "new Integer(number)" here because we don't want
// these Integer objects to be cached.
Integer key = new Integer(number);
@@ -1509,7 +1536,7 @@ public final class Descriptors {
}
return result;
}
-
+
// Used in tests only.
int getUnknownEnumValueDescriptorCount() {
return unknownValues.size();
@@ -1585,7 +1612,7 @@ public final class Descriptors {
/** Get the value's number. */
public int getNumber() { return proto.getNumber(); }
-
+
@Override
public String toString() { return proto.getName(); }
@@ -1627,7 +1654,7 @@ public final class Descriptors {
file.pool.addSymbol(this);
file.pool.addEnumValueByNumber(this);
}
-
+
private Integer number;
// Create an unknown enum value.
private EnumValueDescriptor(
@@ -1643,7 +1670,7 @@ public final class Descriptors {
this.type = parent;
this.fullName = parent.getFullName() + '.' + proto.getName();
this.number = number;
-
+
// Don't add this descriptor into pool.
}
@@ -1930,13 +1957,13 @@ public final class Descriptors {
* descriptors defined in a particular file.
*/
private static final class DescriptorPool {
-
- /** Defines what subclass of descriptors to search in the descriptor pool.
+
+ /** Defines what subclass of descriptors to search in the descriptor pool.
*/
enum SearchFilter {
TYPES_ONLY, AGGREGATES_ONLY, ALL_SYMBOLS
}
-
+
DescriptorPool(final FileDescriptor[] dependencies,
boolean allowUnknownDependencies) {
this.dependencies = new HashSet<FileDescriptor>();
@@ -1982,9 +2009,9 @@ public final class Descriptors {
GenericDescriptor findSymbol(final String fullName) {
return findSymbol(fullName, SearchFilter.ALL_SYMBOLS);
}
-
- /** Find a descriptor by fully-qualified name and given option to only
- * search valid field type descriptors.
+
+ /** Find a descriptor by fully-qualified name and given option to only
+ * search valid field type descriptors.
*/
GenericDescriptor findSymbol(final String fullName,
final SearchFilter filter) {
@@ -2013,18 +2040,18 @@ public final class Descriptors {
/** Checks if the descriptor is a valid type for a message field. */
boolean isType(GenericDescriptor descriptor) {
- return (descriptor instanceof Descriptor) ||
+ return (descriptor instanceof Descriptor) ||
(descriptor instanceof EnumDescriptor);
}
-
+
/** Checks if the descriptor is a valid namespace type. */
boolean isAggregate(GenericDescriptor descriptor) {
- return (descriptor instanceof Descriptor) ||
- (descriptor instanceof EnumDescriptor) ||
- (descriptor instanceof PackageDescriptor) ||
+ return (descriptor instanceof Descriptor) ||
+ (descriptor instanceof EnumDescriptor) ||
+ (descriptor instanceof PackageDescriptor) ||
(descriptor instanceof ServiceDescriptor);
}
-
+
/**
* Look up a type descriptor by name, relative to some other descriptor.
* The name may be fully-qualified (with a leading '.'),
@@ -2082,7 +2109,7 @@ public final class Descriptors {
// Append firstPart and try to find
scopeToTry.append(firstPart);
- result = findSymbol(scopeToTry.toString(),
+ result = findSymbol(scopeToTry.toString(),
DescriptorPool.SearchFilter.AGGREGATES_ONLY);
if (result != null) {
@@ -2323,6 +2350,11 @@ public final class Descriptors {
public int getFieldCount() { return fieldCount; }
+ /** Get a list of this message type's fields. */
+ public List<FieldDescriptor> getFields() {
+ return Collections.unmodifiableList(Arrays.asList(fields));
+ }
+
public FieldDescriptor getField(int index) {
return fields[index];
}
diff --git a/java/src/main/java/com/google/protobuf/DoubleArrayList.java b/java/src/main/java/com/google/protobuf/DoubleArrayList.java
new file mode 100644
index 00000000..90ebe109
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/DoubleArrayList.java
@@ -0,0 +1,243 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import com.google.protobuf.Internal.DoubleList;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.RandomAccess;
+
+/**
+ * An implementation of {@link DoubleList} on top of a primitive array.
+ *
+ * @author dweis@google.com (Daniel Weis)
+ */
+final class DoubleArrayList
+ extends AbstractProtobufList<Double> implements DoubleList, RandomAccess {
+
+ private static final int DEFAULT_CAPACITY = 10;
+
+ private static final DoubleArrayList EMPTY_LIST = new DoubleArrayList();
+ static {
+ EMPTY_LIST.makeImmutable();
+ }
+
+ public static DoubleArrayList emptyList() {
+ return EMPTY_LIST;
+ }
+
+ /**
+ * The backing store for the list.
+ */
+ private double[] array;
+
+ /**
+ * The size of the list distinct from the length of the array. That is, it is the number of
+ * elements set in the list.
+ */
+ private int size;
+
+ /**
+ * Constructs a new mutable {@code DoubleArrayList}.
+ */
+ DoubleArrayList() {
+ array = new double[DEFAULT_CAPACITY];
+ size = 0;
+ }
+
+ /**
+ * Constructs a new mutable {@code DoubleArrayList} containing the same elements as {@code other}.
+ */
+ DoubleArrayList(List<Double> other) {
+ if (other instanceof DoubleArrayList) {
+ DoubleArrayList list = (DoubleArrayList) other;
+ array = list.array.clone();
+ size = list.size;
+ } else {
+ size = other.size();
+ array = new double[size];
+ for (int i = 0; i < size; i++) {
+ array[i] = other.get(i);
+ }
+ }
+ }
+
+ @Override
+ public Double get(int index) {
+ return getDouble(index);
+ }
+
+ @Override
+ public double getDouble(int index) {
+ ensureIndexInRange(index);
+ return array[index];
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+
+ @Override
+ public Double set(int index, Double element) {
+ return setDouble(index, element);
+ }
+
+ @Override
+ public double setDouble(int index, double element) {
+ ensureIsMutable();
+ ensureIndexInRange(index);
+ double previousValue = array[index];
+ array[index] = element;
+ return previousValue;
+ }
+
+ @Override
+ public void add(int index, Double element) {
+ addDouble(index, element);
+ }
+
+ /**
+ * Like {@link #add(Double)} but more efficient in that it doesn't box the element.
+ */
+ @Override
+ public void addDouble(double element) {
+ addDouble(size, element);
+ }
+
+ /**
+ * Like {@link #add(int, Double)} but more efficient in that it doesn't box the element.
+ */
+ private void addDouble(int index, double element) {
+ ensureIsMutable();
+ if (index < 0 || index > size) {
+ throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index));
+ }
+
+ if (size < array.length) {
+ // Shift everything over to make room
+ System.arraycopy(array, index, array, index + 1, size - index);
+ } else {
+ // Resize to 1.5x the size
+ int length = ((size * 3) / 2) + 1;
+ double[] newArray = new double[length];
+
+ // Copy the first part directly
+ System.arraycopy(array, 0, newArray, 0, index);
+
+ // Copy the rest shifted over by one to make room
+ System.arraycopy(array, index, newArray, index + 1, size - index);
+ array = newArray;
+ }
+
+ array[index] = element;
+ size++;
+ modCount++;
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends Double> collection) {
+ ensureIsMutable();
+
+ if (collection == null) {
+ throw new NullPointerException();
+ }
+
+ // We specialize when adding another DoubleArrayList to avoid boxing elements.
+ if (!(collection instanceof DoubleArrayList)) {
+ return super.addAll(collection);
+ }
+
+ DoubleArrayList list = (DoubleArrayList) collection;
+ if (list.size == 0) {
+ return false;
+ }
+
+ int overflow = Integer.MAX_VALUE - size;
+ if (overflow < list.size) {
+ // We can't actually represent a list this large.
+ throw new OutOfMemoryError();
+ }
+
+ int newSize = size + list.size;
+ if (newSize > array.length) {
+ array = Arrays.copyOf(array, newSize);
+ }
+
+ System.arraycopy(list.array, 0, array, size, list.size);
+ size = newSize;
+ modCount++;
+ return true;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ ensureIsMutable();
+ for (int i = 0; i < size; i++) {
+ if (o.equals(array[i])) {
+ System.arraycopy(array, i + 1, array, i, size - i);
+ size--;
+ modCount++;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Double remove(int index) {
+ ensureIsMutable();
+ ensureIndexInRange(index);
+ double value = array[index];
+ System.arraycopy(array, index + 1, array, index, size - index);
+ size--;
+ modCount++;
+ return value;
+ }
+
+ /**
+ * Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an
+ * {@link IndexOutOfBoundsException} if it is not.
+ *
+ * @param index the index to verify is in range
+ */
+ private void ensureIndexInRange(int index) {
+ if (index < 0 || index >= size) {
+ throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index));
+ }
+ }
+
+ private String makeOutOfBoundsExceptionMessage(int index) {
+ return "Index:" + index + ", Size:" + size;
+ }
+}
diff --git a/java/src/main/java/com/google/protobuf/DynamicMessage.java b/java/src/main/java/com/google/protobuf/DynamicMessage.java
index 9c5e6c61..3ea1b688 100644
--- a/java/src/main/java/com/google/protobuf/DynamicMessage.java
+++ b/java/src/main/java/com/google/protobuf/DynamicMessage.java
@@ -35,8 +35,8 @@ import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.OneofDescriptor;
-import java.io.InputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -600,9 +600,12 @@ public final class DynamicMessage extends AbstractMessage {
}
// TODO(xiaofeng): Re-enable this check after Orgstore is fixed to not
// set incorrect EnumValueDescriptors.
- // if (field.getEnumType() != ((EnumValueDescriptor) value).getType()) {
- // throw new IllegalArgumentException(
- // "EnumValueDescriptor doesn't match Enum Field.");
+ // EnumDescriptor fieldType = field.getEnumType();
+ // EnumDescriptor fieldValueType = ((EnumValueDescriptor) value).getType();
+ // if (fieldType != fieldValueType) {
+ // throw new IllegalArgumentException(String.format(
+ // "EnumDescriptor %s of field doesn't match EnumDescriptor %s of field value",
+ // fieldType.getFullName(), fieldValueType.getFullName()));
// }
}
diff --git a/java/src/main/java/com/google/protobuf/FieldSet.java b/java/src/main/java/com/google/protobuf/FieldSet.java
index ff9b5bc0..47924b65 100644
--- a/java/src/main/java/com/google/protobuf/FieldSet.java
+++ b/java/src/main/java/com/google/protobuf/FieldSet.java
@@ -553,42 +553,13 @@ final class FieldSet<FieldDescriptorType extends
CodedInputStream input,
final WireFormat.FieldType type,
boolean checkUtf8) 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 : if (checkUtf8) {
- return input.readStringRequireUtf8();
- } else {
- 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.");
+ if (checkUtf8) {
+ return WireFormat.readPrimitiveField(input, type,
+ WireFormat.Utf8Validation.STRICT);
+ } else {
+ return WireFormat.readPrimitiveField(input, type,
+ WireFormat.Utf8Validation.LOOSE);
}
-
- throw new RuntimeException(
- "There is no way to get here, but the compiler thinks otherwise.");
}
@@ -685,9 +656,15 @@ final class FieldSet<FieldDescriptorType extends
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 STRING:
+ if (value instanceof ByteString) {
+ output.writeBytesNoTag((ByteString) value);
+ } else {
+ output.writeStringNoTag((String) value);
+ }
+ break;
case BYTES:
if (value instanceof ByteString) {
output.writeBytesNoTag((ByteString) value);
@@ -843,7 +820,6 @@ final class FieldSet<FieldDescriptorType extends
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 :
if (value instanceof ByteString) {
@@ -851,6 +827,12 @@ final class FieldSet<FieldDescriptorType extends
} else {
return CodedOutputStream.computeByteArraySizeNoTag((byte[]) value);
}
+ case STRING :
+ if (value instanceof ByteString) {
+ return CodedOutputStream.computeBytesSizeNoTag((ByteString) value);
+ } else {
+ return CodedOutputStream.computeStringSizeNoTag((String) value);
+ }
case UINT32 : return CodedOutputStream.computeUInt32SizeNoTag ((Integer )value);
case SFIXED32: return CodedOutputStream.computeSFixed32SizeNoTag((Integer )value);
case SFIXED64: return CodedOutputStream.computeSFixed64SizeNoTag((Long )value);
diff --git a/java/src/main/java/com/google/protobuf/FloatArrayList.java b/java/src/main/java/com/google/protobuf/FloatArrayList.java
new file mode 100644
index 00000000..293eaff6
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/FloatArrayList.java
@@ -0,0 +1,242 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import com.google.protobuf.Internal.FloatList;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.RandomAccess;
+
+/**
+ * An implementation of {@link FloatList} on top of a primitive array.
+ *
+ * @author dweis@google.com (Daniel Weis)
+ */
+final class FloatArrayList extends AbstractProtobufList<Float> implements FloatList, RandomAccess {
+
+ private static final int DEFAULT_CAPACITY = 10;
+
+ private static final FloatArrayList EMPTY_LIST = new FloatArrayList();
+ static {
+ EMPTY_LIST.makeImmutable();
+ }
+
+ public static FloatArrayList emptyList() {
+ return EMPTY_LIST;
+ }
+
+ /**
+ * The backing store for the list.
+ */
+ private float[] array;
+
+ /**
+ * The size of the list distinct from the length of the array. That is, it is the number of
+ * elements set in the list.
+ */
+ private int size;
+
+ /**
+ * Constructs a new mutable {@code FloatArrayList}.
+ */
+ FloatArrayList() {
+ array = new float[DEFAULT_CAPACITY];
+ size = 0;
+ }
+
+ /**
+ * Constructs a new mutable {@code FloatArrayList} containing the same elements as {@code other}.
+ */
+ FloatArrayList(List<Float> other) {
+ if (other instanceof FloatArrayList) {
+ FloatArrayList list = (FloatArrayList) other;
+ array = list.array.clone();
+ size = list.size;
+ } else {
+ size = other.size();
+ array = new float[size];
+ for (int i = 0; i < size; i++) {
+ array[i] = other.get(i);
+ }
+ }
+ }
+
+ @Override
+ public Float get(int index) {
+ return getFloat(index);
+ }
+
+ @Override
+ public float getFloat(int index) {
+ ensureIndexInRange(index);
+ return array[index];
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+
+ @Override
+ public Float set(int index, Float element) {
+ return setFloat(index, element);
+ }
+
+ @Override
+ public float setFloat(int index, float element) {
+ ensureIsMutable();
+ ensureIndexInRange(index);
+ float previousValue = array[index];
+ array[index] = element;
+ return previousValue;
+ }
+
+ @Override
+ public void add(int index, Float element) {
+ addFloat(index, element);
+ }
+
+ /**
+ * Like {@link #add(Float)} but more efficient in that it doesn't box the element.
+ */
+ @Override
+ public void addFloat(float element) {
+ addFloat(size, element);
+ }
+
+ /**
+ * Like {@link #add(int, Float)} but more efficient in that it doesn't box the element.
+ */
+ private void addFloat(int index, float element) {
+ ensureIsMutable();
+ if (index < 0 || index > size) {
+ throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index));
+ }
+
+ if (size < array.length) {
+ // Shift everything over to make room
+ System.arraycopy(array, index, array, index + 1, size - index);
+ } else {
+ // Resize to 1.5x the size
+ int length = ((size * 3) / 2) + 1;
+ float[] newArray = new float[length];
+
+ // Copy the first part directly
+ System.arraycopy(array, 0, newArray, 0, index);
+
+ // Copy the rest shifted over by one to make room
+ System.arraycopy(array, index, newArray, index + 1, size - index);
+ array = newArray;
+ }
+
+ array[index] = element;
+ size++;
+ modCount++;
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends Float> collection) {
+ ensureIsMutable();
+
+ if (collection == null) {
+ throw new NullPointerException();
+ }
+
+ // We specialize when adding another FloatArrayList to avoid boxing elements.
+ if (!(collection instanceof FloatArrayList)) {
+ return super.addAll(collection);
+ }
+
+ FloatArrayList list = (FloatArrayList) collection;
+ if (list.size == 0) {
+ return false;
+ }
+
+ int overflow = Integer.MAX_VALUE - size;
+ if (overflow < list.size) {
+ // We can't actually represent a list this large.
+ throw new OutOfMemoryError();
+ }
+
+ int newSize = size + list.size;
+ if (newSize > array.length) {
+ array = Arrays.copyOf(array, newSize);
+ }
+
+ System.arraycopy(list.array, 0, array, size, list.size);
+ size = newSize;
+ modCount++;
+ return true;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ ensureIsMutable();
+ for (int i = 0; i < size; i++) {
+ if (o.equals(array[i])) {
+ System.arraycopy(array, i + 1, array, i, size - i);
+ size--;
+ modCount++;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Float remove(int index) {
+ ensureIsMutable();
+ ensureIndexInRange(index);
+ float value = array[index];
+ System.arraycopy(array, index + 1, array, index, size - index);
+ size--;
+ modCount++;
+ return value;
+ }
+
+ /**
+ * Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an
+ * {@link IndexOutOfBoundsException} if it is not.
+ *
+ * @param index the index to verify is in range
+ */
+ private void ensureIndexInRange(int index) {
+ if (index < 0 || index >= size) {
+ throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index));
+ }
+ }
+
+ private String makeOutOfBoundsExceptionMessage(int index) {
+ return "Index:" + index + ", Size:" + size;
+ }
+}
diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessage.java b/java/src/main/java/com/google/protobuf/GeneratedMessage.java
index 156d1633..9457d999 100644
--- a/java/src/main/java/com/google/protobuf/GeneratedMessage.java
+++ b/java/src/main/java/com/google/protobuf/GeneratedMessage.java
@@ -73,7 +73,7 @@ public abstract class GeneratedMessage extends AbstractMessage
/** For use by generated code only. */
protected UnknownFieldSet unknownFields;
-
+
protected GeneratedMessage() {
unknownFields = UnknownFieldSet.getDefaultInstance();
}
@@ -109,8 +109,15 @@ public abstract class GeneratedMessage extends AbstractMessage
return internalGetFieldAccessorTable().descriptor;
}
- /** Internal helper which returns a mutable map. */
- private Map<FieldDescriptor, Object> getAllFieldsMutable() {
+ /**
+ * Internal helper to return a modifiable map containing all the fields.
+ * The returned Map is modifialbe so that the caller can add additional
+ * extension fields to implement {@link #getAllFields()}.
+ *
+ * @param getBytesForString whether to generate ByteString for string fields
+ */
+ private Map<FieldDescriptor, Object> getAllFieldsMutable(
+ boolean getBytesForString) {
final TreeMap<FieldDescriptor, Object> result =
new TreeMap<FieldDescriptor, Object>();
final Descriptor descriptor = internalGetFieldAccessorTable().descriptor;
@@ -122,7 +129,12 @@ public abstract class GeneratedMessage extends AbstractMessage
}
} else {
if (hasField(field)) {
- result.put(field, getField(field));
+ if (getBytesForString
+ && field.getJavaType() == FieldDescriptor.JavaType.STRING) {
+ result.put(field, getFieldRaw(field));
+ } else {
+ result.put(field, getField(field));
+ }
}
}
}
@@ -161,7 +173,23 @@ public abstract class GeneratedMessage extends AbstractMessage
//@Override (Java 1.6 override semantics, but we must support 1.5)
public Map<FieldDescriptor, Object> getAllFields() {
- return Collections.unmodifiableMap(getAllFieldsMutable());
+ return Collections.unmodifiableMap(
+ getAllFieldsMutable(/* getBytesForString = */ false));
+ }
+
+ /**
+ * 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 getRepeatedFieldCount() is greater than zero. The
+ * values are exactly what would be returned by calling
+ * {@link #getFieldRaw(Descriptors.FieldDescriptor)} for each field. The map
+ * is guaranteed to be a sorted map, so iterating over it will return fields
+ * in order by field number.
+ */
+ Map<FieldDescriptor, Object> getAllFieldsRaw() {
+ return Collections.unmodifiableMap(
+ getAllFieldsMutable(/* getBytesForString = */ true));
}
//@Override (Java 1.6 override semantics, but we must support 1.5)
@@ -184,6 +212,18 @@ public abstract class GeneratedMessage extends AbstractMessage
return internalGetFieldAccessorTable().getField(field).get(this);
}
+ /**
+ * 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. For present string fields, a
+ * ByteString is returned representing the bytes that the field contains.
+ */
+ Object getFieldRaw(final FieldDescriptor field) {
+ return internalGetFieldAccessorTable().getField(field).getRaw(this);
+ }
+
//@Override (Java 1.6 override semantics, but we must support 1.5)
public int getRepeatedFieldCount(final FieldDescriptor field) {
return internalGetFieldAccessorTable().getField(field)
@@ -214,6 +254,24 @@ public abstract class GeneratedMessage extends AbstractMessage
return unknownFields.mergeFieldFrom(tag, input);
}
+ @Override
+ public void writeTo(final CodedOutputStream output) throws IOException {
+ MessageReflection.writeMessageTo(this, getAllFieldsRaw(), output, false);
+ }
+
+ @Override
+ public int getSerializedSize() {
+ int size = memoizedSize;
+ if (size != -1) {
+ return size;
+ }
+
+ memoizedSize = MessageReflection.getSerializedSize(
+ this, getAllFieldsRaw());
+ return memoizedSize;
+ }
+
+
/**
* Used by parsing constructors in generated classes.
@@ -549,12 +607,12 @@ public abstract class GeneratedMessage extends AbstractMessage
* Gets the map field with the given field number. This method should be
* overridden in the generated message class if the message contains map
* fields.
- *
+ *
* Unlike other field types, reflection support for map fields can't be
* implemented based on generated public API because we need to access a
* map field as a list in reflection API but the generated API only allows
* us to access it as a map. This method returns the underlying map field
- * directly and thus enables us to access the map field as a list.
+ * directly and thus enables us to access the map field as a list.
*/
@SuppressWarnings({"unused", "rawtypes"})
protected MapField internalGetMapField(int fieldNumber) {
@@ -563,6 +621,15 @@ public abstract class GeneratedMessage extends AbstractMessage
throw new RuntimeException(
"No map fields found in " + getClass().getName());
}
+
+ /** Like {@link internalGetMapField} but return a mutable version. */
+ @SuppressWarnings({"unused", "rawtypes"})
+ protected MapField internalGetMutableMapField(int fieldNumber) {
+ // Note that we can't use descriptor names here because this method will
+ // be called when descriptor is being initialized.
+ throw new RuntimeException(
+ "No map fields found in " + getClass().getName());
+ }
}
// =================================================================
@@ -683,7 +750,7 @@ public abstract class GeneratedMessage extends AbstractMessage
public final <Type> Type getExtension(
final ExtensionLite<MessageType, Type> extensionLite) {
Extension<MessageType, Type> extension = checkNotLite(extensionLite);
-
+
verifyExtensionContainingType(extension);
FieldDescriptor descriptor = extension.getDescriptor();
final Object value = extensions.getField(descriptor);
@@ -825,7 +892,16 @@ public abstract class GeneratedMessage extends AbstractMessage
@Override
public Map<FieldDescriptor, Object> getAllFields() {
- final Map<FieldDescriptor, Object> result = super.getAllFieldsMutable();
+ final Map<FieldDescriptor, Object> result =
+ super.getAllFieldsMutable(/* getBytesForString = */ false);
+ result.putAll(getExtensionFields());
+ return Collections.unmodifiableMap(result);
+ }
+
+ @Override
+ public Map<FieldDescriptor, Object> getAllFieldsRaw() {
+ final Map<FieldDescriptor, Object> result =
+ super.getAllFieldsMutable(/* getBytesForString = */ false);
result.putAll(getExtensionFields());
return Collections.unmodifiableMap(result);
}
@@ -1313,7 +1389,7 @@ public abstract class GeneratedMessage extends AbstractMessage
implements ExtensionDescriptorRetriever {
private volatile FieldDescriptor descriptor;
protected abstract FieldDescriptor loadDescriptor();
-
+
public FieldDescriptor getDescriptor() {
if (descriptor == null) {
synchronized (this) {
@@ -1651,17 +1727,17 @@ public abstract class GeneratedMessage extends AbstractMessage
}
}
}
-
+
/**
* Gets the map field with the given field number. This method should be
* overridden in the generated message class if the message contains map
* fields.
- *
+ *
* Unlike other field types, reflection support for map fields can't be
* implemented based on generated public API because we need to access a
* map field as a list in reflection API but the generated API only allows
* us to access it as a map. This method returns the underlying map field
- * directly and thus enables us to access the map field as a list.
+ * directly and thus enables us to access the map field as a list.
*/
@SuppressWarnings({"rawtypes", "unused"})
protected MapField internalGetMapField(int fieldNumber) {
@@ -1709,7 +1785,7 @@ public abstract class GeneratedMessage extends AbstractMessage
oneofs = new OneofAccessor[descriptor.getOneofs().size()];
initialized = false;
}
-
+
private boolean isMapFieldEnabled(FieldDescriptor field) {
boolean result = true;
return result;
@@ -1761,6 +1837,10 @@ public abstract class GeneratedMessage extends AbstractMessage
fields[i] = new SingularEnumFieldAccessor(
field, camelCaseNames[i], messageClass, builderClass,
containingOneofCamelCaseName);
+ } else if (field.getJavaType() == FieldDescriptor.JavaType.STRING) {
+ fields[i] = new SingularStringFieldAccessor(
+ field, camelCaseNames[i], messageClass, builderClass,
+ containingOneofCamelCaseName);
} else {
fields[i] = new SingularFieldAccessor(
field, camelCaseNames[i], messageClass, builderClass,
@@ -1817,9 +1897,13 @@ public abstract class GeneratedMessage extends AbstractMessage
private interface FieldAccessor {
Object get(GeneratedMessage message);
Object get(GeneratedMessage.Builder builder);
+ Object getRaw(GeneratedMessage message);
+ Object getRaw(GeneratedMessage.Builder builder);
void set(Builder builder, Object value);
Object getRepeated(GeneratedMessage message, int index);
Object getRepeated(GeneratedMessage.Builder builder, int index);
+ Object getRepeatedRaw(GeneratedMessage message, int index);
+ Object getRepeatedRaw(GeneratedMessage.Builder builder, int index);
void setRepeated(Builder builder,
int index, Object value);
void addRepeated(Builder builder, Object value);
@@ -1934,11 +2018,11 @@ public abstract class GeneratedMessage extends AbstractMessage
protected final FieldDescriptor field;
protected final boolean isOneofField;
protected final boolean hasHasMethod;
-
+
private int getOneofFieldNumber(final GeneratedMessage message) {
return ((Internal.EnumLite) invokeOrDie(caseMethod, message)).getNumber();
}
-
+
private int getOneofFieldNumber(final GeneratedMessage.Builder builder) {
return ((Internal.EnumLite) invokeOrDie(caseMethodBuilder, builder)).getNumber();
}
@@ -1949,6 +2033,12 @@ public abstract class GeneratedMessage extends AbstractMessage
public Object get(GeneratedMessage.Builder builder) {
return invokeOrDie(getMethodBuilder, builder);
}
+ public Object getRaw(final GeneratedMessage message) {
+ return get(message);
+ }
+ public Object getRaw(GeneratedMessage.Builder builder) {
+ return get(builder);
+ }
public void set(final Builder builder, final Object value) {
invokeOrDie(setMethod, builder, value);
}
@@ -1957,12 +2047,22 @@ public abstract class GeneratedMessage extends AbstractMessage
throw new UnsupportedOperationException(
"getRepeatedField() called on a singular field.");
}
+ public Object getRepeatedRaw(final GeneratedMessage message,
+ final int index) {
+ throw new UnsupportedOperationException(
+ "getRepeatedFieldRaw() called on a singular field.");
+ }
public Object getRepeated(GeneratedMessage.Builder builder, int index) {
throw new UnsupportedOperationException(
"getRepeatedField() called on a singular field.");
}
- public void setRepeated(final Builder builder,
- final int index, final Object value) {
+ public Object getRepeatedRaw(GeneratedMessage.Builder builder,
+ int index) {
+ throw new UnsupportedOperationException(
+ "getRepeatedFieldRaw() called on a singular field.");
+ }
+ public void setRepeated(final Builder builder, final int index,
+ final Object value) {
throw new UnsupportedOperationException(
"setRepeatedField() called on a singular field.");
}
@@ -2058,6 +2158,12 @@ public abstract class GeneratedMessage extends AbstractMessage
public Object get(GeneratedMessage.Builder builder) {
return invokeOrDie(getMethodBuilder, builder);
}
+ public Object getRaw(final GeneratedMessage message) {
+ return get(message);
+ }
+ public Object getRaw(GeneratedMessage.Builder builder) {
+ return get(builder);
+ }
public void set(final Builder builder, final Object value) {
// Add all the elements individually. This serves two purposes:
// 1) Verifies that each element has the correct type.
@@ -2075,6 +2181,13 @@ public abstract class GeneratedMessage extends AbstractMessage
public Object getRepeated(GeneratedMessage.Builder builder, int index) {
return invokeOrDie(getRepeatedMethodBuilder, builder, index);
}
+ public Object getRepeatedRaw(GeneratedMessage message, int index) {
+ return getRepeated(message, index);
+ }
+ public Object getRepeatedRaw(GeneratedMessage.Builder builder,
+ int index) {
+ return getRepeated(builder, index);
+ }
public void setRepeated(final Builder builder,
final int index, final Object value) {
invokeOrDie(setRepeatedMethod, builder, index, value);
@@ -2130,15 +2243,21 @@ public abstract class GeneratedMessage extends AbstractMessage
private final FieldDescriptor field;
private final Message mapEntryMessageDefaultInstance;
-
+
private MapField<?, ?> getMapField(GeneratedMessage message) {
return (MapField<?, ?>) message.internalGetMapField(field.getNumber());
}
-
+
private MapField<?, ?> getMapField(GeneratedMessage.Builder builder) {
return (MapField<?, ?>) builder.internalGetMapField(field.getNumber());
}
-
+
+ private MapField<?, ?> getMutableMapField(
+ GeneratedMessage.Builder builder) {
+ return (MapField<?, ?>) builder.internalGetMutableMapField(
+ field.getNumber());
+ }
+
public Object get(GeneratedMessage message) {
List result = new ArrayList();
for (int i = 0; i < getRepeatedCount(message); i++) {
@@ -2155,6 +2274,14 @@ public abstract class GeneratedMessage extends AbstractMessage
return Collections.unmodifiableList(result);
}
+ public Object getRaw(GeneratedMessage message) {
+ return get(message);
+ }
+
+ public Object getRaw(GeneratedMessage.Builder builder) {
+ return get(builder);
+ }
+
public void set(Builder builder, Object value) {
clear(builder);
for (Object entry : (List) value) {
@@ -2170,12 +2297,20 @@ public abstract class GeneratedMessage extends AbstractMessage
return getMapField(builder).getList().get(index);
}
+ public Object getRepeatedRaw(GeneratedMessage message, int index) {
+ return getRepeated(message, index);
+ }
+
+ public Object getRepeatedRaw(Builder builder, int index) {
+ return getRepeated(builder, index);
+ }
+
public void setRepeated(Builder builder, int index, Object value) {
- getMapField(builder).getMutableList().set(index, (Message) value);
+ getMutableMapField(builder).getMutableList().set(index, (Message) value);
}
public void addRepeated(Builder builder, Object value) {
- getMapField(builder).getMutableList().add((Message) value);
+ getMutableMapField(builder).getMutableList().add((Message) value);
}
public boolean has(GeneratedMessage message) {
@@ -2197,7 +2332,7 @@ public abstract class GeneratedMessage extends AbstractMessage
}
public void clear(Builder builder) {
- getMapField(builder).getMutableList().clear();
+ getMutableMapField(builder).getMutableList().clear();
}
public com.google.protobuf.Message.Builder newBuilder() {
@@ -2208,7 +2343,7 @@ public abstract class GeneratedMessage extends AbstractMessage
throw new UnsupportedOperationException(
"Nested builder not supported for map fields.");
}
-
+
public com.google.protobuf.Message.Builder getRepeatedBuilder(
Builder builder, int index) {
throw new UnsupportedOperationException(
@@ -2226,7 +2361,7 @@ public abstract class GeneratedMessage extends AbstractMessage
final Class<? extends Builder> builderClass,
final String containingOneofCamelCaseName) {
super(descriptor, camelCaseName, messageClass, builderClass, containingOneofCamelCaseName);
-
+
enumDescriptor = descriptor.getEnumType();
valueOfMethod = getMethodOrDie(type, "valueOf",
@@ -2244,12 +2379,12 @@ public abstract class GeneratedMessage extends AbstractMessage
getMethodOrDie(builderClass, "set" + camelCaseName + "Value", int.class);
}
}
-
+
private EnumDescriptor enumDescriptor;
private Method valueOfMethod;
private Method getValueDescriptorMethod;
-
+
private boolean supportUnknownEnumValue;
private Method getValueMethod;
private Method getValueMethodBuilder;
@@ -2291,7 +2426,7 @@ public abstract class GeneratedMessage extends AbstractMessage
final Class<? extends GeneratedMessage> messageClass,
final Class<? extends Builder> builderClass) {
super(descriptor, camelCaseName, messageClass, builderClass);
-
+
enumDescriptor = descriptor.getEnumType();
valueOfMethod = getMethodOrDie(type, "valueOf",
@@ -2315,7 +2450,7 @@ public abstract class GeneratedMessage extends AbstractMessage
private final Method valueOfMethod;
private final Method getValueDescriptorMethod;
-
+
private boolean supportUnknownEnumValue;
private Method getRepeatedValueMethod;
private Method getRepeatedValueMethodBuilder;
@@ -2388,6 +2523,60 @@ public abstract class GeneratedMessage extends AbstractMessage
// ---------------------------------------------------------------
+ /**
+ * Field accessor for string fields.
+ *
+ * <p>This class makes getFooBytes() and setFooBytes() available for
+ * reflection API so that reflection based serialize/parse functions can
+ * access the raw bytes of the field to preserve non-UTF8 bytes in the
+ * string.
+ *
+ * <p>This ensures the serialize/parse round-trip safety, which is important
+ * for servers which forward messages.
+ */
+ private static final class SingularStringFieldAccessor
+ extends SingularFieldAccessor {
+ SingularStringFieldAccessor(
+ final FieldDescriptor descriptor, final String camelCaseName,
+ final Class<? extends GeneratedMessage> messageClass,
+ final Class<? extends Builder> builderClass,
+ final String containingOneofCamelCaseName) {
+ super(descriptor, camelCaseName, messageClass, builderClass,
+ containingOneofCamelCaseName);
+ getBytesMethod = getMethodOrDie(messageClass,
+ "get" + camelCaseName + "Bytes");
+ getBytesMethodBuilder = getMethodOrDie(builderClass,
+ "get" + camelCaseName + "Bytes");
+ setBytesMethodBuilder = getMethodOrDie(builderClass,
+ "set" + camelCaseName + "Bytes", ByteString.class);
+ }
+
+ private final Method getBytesMethod;
+ private final Method getBytesMethodBuilder;
+ private final Method setBytesMethodBuilder;
+
+ @Override
+ public Object getRaw(final GeneratedMessage message) {
+ return invokeOrDie(getBytesMethod, message);
+ }
+
+ @Override
+ public Object getRaw(GeneratedMessage.Builder builder) {
+ return invokeOrDie(getBytesMethodBuilder, builder);
+ }
+
+ @Override
+ public void set(GeneratedMessage.Builder builder, Object value) {
+ if (value instanceof ByteString) {
+ invokeOrDie(setBytesMethodBuilder, builder, value);
+ } else {
+ super.set(builder, value);
+ }
+ }
+ }
+
+ // ---------------------------------------------------------------
+
private static final class SingularMessageFieldAccessor
extends SingularFieldAccessor {
SingularMessageFieldAccessor(
@@ -2395,7 +2584,8 @@ public abstract class GeneratedMessage extends AbstractMessage
final Class<? extends GeneratedMessage> messageClass,
final Class<? extends Builder> builderClass,
final String containingOneofCamelCaseName) {
- super(descriptor, camelCaseName, messageClass, builderClass, containingOneofCamelCaseName);
+ super(descriptor, camelCaseName, messageClass, builderClass,
+ containingOneofCamelCaseName);
newBuilderMethod = getMethodOrDie(type, "newBuilder");
getBuilderMethodBuilder =
@@ -2492,7 +2682,7 @@ public abstract class GeneratedMessage extends AbstractMessage
protected Object writeReplace() throws ObjectStreamException {
return new GeneratedMessageLite.SerializedForm(this);
}
-
+
/**
* Checks that the {@link Extension} is non-Lite and returns it as a
* {@link GeneratedExtension}.
@@ -2503,7 +2693,7 @@ public abstract class GeneratedMessage extends AbstractMessage
if (extension.isLite()) {
throw new IllegalArgumentException("Expected non-lite extension.");
}
-
+
return (Extension<MessageType, T>) extension;
}
}
diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java
index 4d25c077..bd6bc463 100644
--- a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java
+++ b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java
@@ -30,6 +30,12 @@
package com.google.protobuf;
+import com.google.protobuf.Internal.BooleanList;
+import com.google.protobuf.Internal.DoubleList;
+import com.google.protobuf.Internal.FloatList;
+import com.google.protobuf.Internal.IntList;
+import com.google.protobuf.Internal.LongList;
+import com.google.protobuf.Internal.ProtobufList;
import com.google.protobuf.WireFormat.FieldType;
import java.io.IOException;
@@ -42,22 +48,62 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
/**
* Lite version of {@link GeneratedMessage}.
*
* @author kenton@google.com Kenton Varda
*/
-public abstract class GeneratedMessageLite extends AbstractMessageLite
- implements Serializable {
+public abstract class GeneratedMessageLite<
+ MessageType extends GeneratedMessageLite<MessageType, BuilderType>,
+ BuilderType extends GeneratedMessageLite.Builder<MessageType, BuilderType>>
+ extends AbstractMessageLite
+ implements Serializable {
+
+ /**
+ * Holds all the {@link PrototypeHolder}s for loaded classes.
+ */
+ // TODO(dweis): Consider different concurrency values.
+ // TODO(dweis): This will prevent garbage collection of the class loader.
+ // Ideally we'd use something like ClassValue but that's Java 7 only.
+ private static final Map<Class<?>, PrototypeHolder<?, ?>> PROTOTYPE_MAP =
+ new ConcurrentHashMap<Class<?>, PrototypeHolder<?, ?>>();
+
+ // For use by generated code only.
+ protected static <
+ MessageType extends GeneratedMessageLite<MessageType, BuilderType>,
+ BuilderType extends GeneratedMessageLite.Builder<
+ MessageType, BuilderType>> void onLoad(Class<MessageType> clazz,
+ PrototypeHolder<MessageType, BuilderType> protoTypeHolder) {
+ PROTOTYPE_MAP.put(clazz, protoTypeHolder);
+ }
+
private static final long serialVersionUID = 1L;
/** For use by generated code only. */
- protected UnknownFieldSetLite unknownFields;
+ protected UnknownFieldSetLite unknownFields =
+ UnknownFieldSetLite.getDefaultInstance();
- public Parser<? extends MessageLite> getParserForType() {
- throw new UnsupportedOperationException(
- "This is supposed to be overridden by subclasses.");
+ /** For use by generated code only. */
+ protected int memoizedSerializedSize = -1;
+
+ @SuppressWarnings("unchecked") // Guaranteed by runtime.
+ public final Parser<MessageType> getParserForType() {
+ return (Parser<MessageType>) PROTOTYPE_MAP
+ .get(getClass()).getParserForType();
+ }
+
+ @SuppressWarnings("unchecked") // Guaranteed by runtime.
+ public final MessageType getDefaultInstanceForType() {
+ return (MessageType) PROTOTYPE_MAP
+ .get(getClass()).getDefaultInstanceForType();
+ }
+
+ @SuppressWarnings("unchecked") // Guaranteed by runtime.
+ public final BuilderType newBuilderForType() {
+ return (BuilderType) PROTOTYPE_MAP
+ .get(getClass()).newBuilderForType();
}
/**
@@ -73,24 +119,107 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
return unknownFields.mergeFieldFrom(tag, input);
}
+ public final boolean isInitialized() {
+ return dynamicMethod(MethodToInvoke.IS_INITIALIZED, Boolean.TRUE) != null;
+ }
+
+ public final BuilderType toBuilder() {
+ BuilderType builder = (BuilderType) dynamicMethod(MethodToInvoke.NEW_BUILDER);
+ builder.mergeFrom((MessageType) this);
+ return builder;
+ }
+
+ /**
+ * Defines which method path to invoke in {@link GeneratedMessageLite
+ * #dynamicMethod(MethodToInvoke, Object...)}.
+ * <p>
+ * For use by generated code only.
+ */
+ public static enum MethodToInvoke {
+ IS_INITIALIZED,
+ PARSE_PARTIAL_FROM,
+ MERGE_FROM,
+ MAKE_IMMUTABLE,
+ NEW_INSTANCE,
+ NEW_BUILDER;
+ }
+
+ /**
+ * A method that implements different types of operations described in {@link MethodToInvoke}.
+ * Theses different kinds of operations are required to implement message-level operations for
+ * builders in the runtime. This method bundles those operations to reduce the generated methods
+ * count.
+ * <ul>
+ * <li>{@code PARSE_PARTIAL_FROM} is parameterized with an {@link CodedInputStream} and
+ * {@link ExtensionRegistryLite}. It consumes the input stream, parsing the contents into the
+ * returned protocol buffer. If parsing throws an {@link InvalidProtocolBufferException}, the
+ * implementation wraps it in a RuntimeException
+ * <li>{@code NEW_INSTANCE} returns a new instance of the protocol buffer
+ * <li>{@code IS_INITIALIZED} is parameterized with a {@code Boolean} detailing whether to
+ * memoize. It returns {@code null} for false and the default instance for true. We optionally
+ * memoize to support the Builder case, where memoization is not desired.
+ * <li>{@code NEW_BUILDER} returns a {@code BuilderType} instance.
+ * <li>{@code MERGE_FROM} is parameterized with a {@code MessageType} and merges the fields from
+ * that instance into this instance.
+ * <li>{@code MAKE_IMMUTABLE} sets all internal fields to an immutable state.
+ * </ul>
+ * This method, plus the implementation of the Builder, enables the Builder class to be proguarded
+ * away entirely on Android.
+ * <p>
+ * For use by generated code only.
+ */
+ protected abstract Object dynamicMethod(
+ MethodToInvoke method,
+ Object... args);
+
+ /**
+ * Merge some unknown fields into the {@link UnknownFieldSetLite} for this
+ * message.
+ *
+ * <p>For use by generated code only.
+ */
+ protected final void mergeUnknownFields(UnknownFieldSetLite unknownFields) {
+ this.unknownFields = UnknownFieldSetLite.concat(this.unknownFields, unknownFields);
+ }
+
@SuppressWarnings("unchecked")
- public abstract static class Builder<MessageType extends GeneratedMessageLite,
- BuilderType extends Builder>
- extends AbstractMessageLite.Builder<BuilderType> {
+ public abstract static class Builder<
+ MessageType extends GeneratedMessageLite<MessageType, BuilderType>,
+ BuilderType extends Builder<MessageType, BuilderType>>
+ extends AbstractMessageLite.Builder<BuilderType> {
private final MessageType defaultInstance;
-
- /** For use by generated code only. */
- protected UnknownFieldSetLite unknownFields =
- UnknownFieldSetLite.getDefaultInstance();
+ protected MessageType instance;
+ protected boolean isBuilt;
protected Builder(MessageType defaultInstance) {
this.defaultInstance = defaultInstance;
+ this.instance = (MessageType) defaultInstance.dynamicMethod(MethodToInvoke.NEW_INSTANCE);
+ isBuilt = false;
+ }
+
+ /**
+ * Called before any method that would mutate the builder to ensure that it correctly copies
+ * any state before the write happens to preserve immutability guarantees.
+ */
+ protected void copyOnWrite() {
+ if (isBuilt) {
+ MessageType newInstance = (MessageType) instance.dynamicMethod(MethodToInvoke.NEW_INSTANCE);
+ newInstance.dynamicMethod(MethodToInvoke.MERGE_FROM, instance);
+ instance = newInstance;
+ isBuilt = false;
+ }
}
//@Override (Java 1.6 override semantics, but we must support 1.5)
- public BuilderType clear() {
- unknownFields = UnknownFieldSetLite.getDefaultInstance();
+ public final boolean isInitialized() {
+ return GeneratedMessageLite.isInitialized(instance, false /* shouldMemoize */);
+ }
+
+ //@Override (Java 1.6 override semantics, but we must support 1.5)
+ public final BuilderType clear() {
+ // No need to copy on write since we're dropping the instance anyways.
+ instance = (MessageType) instance.dynamicMethod(MethodToInvoke.NEW_INSTANCE);
return (BuilderType) this;
}
@@ -102,8 +231,12 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
return builder;
}
- /** All subclasses implement this. */
- public abstract MessageType buildPartial();
+ //@Override (Java 1.6 override semantics, but we must support 1.5)
+ public MessageType buildPartial() {
+ instance.dynamicMethod(MethodToInvoke.MAKE_IMMUTABLE);
+ isBuilt = true;
+ return instance;
+ }
//@Override (Java 1.6 override semantics, but we must support 1.5)
public final MessageType build() {
@@ -113,9 +246,13 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
}
return result;
}
-
+
/** All subclasses implement this. */
- public abstract BuilderType mergeFrom(MessageType message);
+ public BuilderType mergeFrom(MessageType message) {
+ copyOnWrite();
+ instance.dynamicMethod(MethodToInvoke.MERGE_FROM, message);
+ return (BuilderType) this;
+ }
public MessageType getDefaultInstanceForType() {
return defaultInstance;
@@ -132,18 +269,6 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
int tag) throws IOException {
return unknownFields.mergeFieldFrom(tag, input);
}
-
- /**
- * Merge some unknown fields into the {@link UnknownFieldSetLite} for this
- * message.
- *
- * <p>For use by generated code only.
- */
- protected final BuilderType mergeUnknownFields(
- final UnknownFieldSetLite unknownFields) {
- this.unknownFields = UnknownFieldSetLite.concat(this.unknownFields, unknownFields);
- return (BuilderType) this;
- }
public BuilderType mergeFrom(
com.google.protobuf.CodedInputStream input,
@@ -174,7 +299,9 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
* Lite equivalent of {@link com.google.protobuf.GeneratedMessage.ExtendableMessageOrBuilder}.
*/
public interface ExtendableMessageOrBuilder<
- MessageType extends ExtendableMessage> extends MessageLiteOrBuilder {
+ MessageType extends ExtendableMessage<MessageType, BuilderType>,
+ BuilderType extends ExtendableBuilder<MessageType, BuilderType>>
+ extends MessageLiteOrBuilder {
/** Check if a singular extension is present. */
<Type> boolean hasExtension(
@@ -197,15 +324,23 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
* Lite equivalent of {@link GeneratedMessage.ExtendableMessage}.
*/
public abstract static class ExtendableMessage<
- MessageType extends ExtendableMessage<MessageType>>
- extends GeneratedMessageLite
- implements ExtendableMessageOrBuilder<MessageType> {
+ MessageType extends ExtendableMessage<MessageType, BuilderType>,
+ BuilderType extends ExtendableBuilder<MessageType, BuilderType>>
+ extends GeneratedMessageLite<MessageType, BuilderType>
+ implements ExtendableMessageOrBuilder<MessageType, BuilderType> {
/**
* Represents the set of extensions on this message. For use by generated
* code only.
*/
protected FieldSet<ExtensionDescriptor> extensions = FieldSet.newFieldSet();
+
+ protected final void mergeExtensionFields(final MessageType other) {
+ if (extensions.isImmutable()) {
+ extensions = extensions.clone();
+ }
+ extensions.mergeFrom(((ExtendableMessage) other).extensions);
+ }
private void verifyExtensionContainingType(
final GeneratedExtension<MessageType, ?> extension) {
@@ -349,46 +484,44 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
*/
@SuppressWarnings("unchecked")
public abstract static class ExtendableBuilder<
- MessageType extends ExtendableMessage<MessageType>,
+ MessageType extends ExtendableMessage<MessageType, BuilderType>,
BuilderType extends ExtendableBuilder<MessageType, BuilderType>>
extends Builder<MessageType, BuilderType>
- implements ExtendableMessageOrBuilder<MessageType> {
+ implements ExtendableMessageOrBuilder<MessageType, BuilderType> {
protected ExtendableBuilder(MessageType defaultInstance) {
super(defaultInstance);
+
+ // TODO(dweis): This is kind of an unnecessary clone since we construct a
+ // new instance in the parent constructor which makes the extensions
+ // immutable. This extra allocation shouldn't matter in practice
+ // though.
+ instance.extensions = instance.extensions.clone();
}
- private FieldSet<ExtensionDescriptor> extensions = FieldSet.emptySet();
- private boolean extensionsIsMutable;
-
// For immutable message conversion.
void internalSetExtensionSet(FieldSet<ExtensionDescriptor> extensions) {
- this.extensions = extensions;
+ copyOnWrite();
+ instance.extensions = extensions;
}
- @Override
- public BuilderType clear() {
- extensions.clear();
- extensionsIsMutable = false;
- return super.clear();
+ // @Override (Java 1.6 override semantics, but we must support 1.5)
+ protected void copyOnWrite() {
+ if (!isBuilt) {
+ return;
+ }
+
+ super.copyOnWrite();
+ instance.extensions = instance.extensions.clone();
}
- private void ensureExtensionsIsMutable() {
- if (!extensionsIsMutable) {
- extensions = extensions.clone();
- extensionsIsMutable = true;
+ // @Override (Java 1.6 override semantics, but we must support 1.5)
+ public final MessageType buildPartial() {
+ if (isBuilt) {
+ return instance;
}
- }
- /**
- * Called by the build code path to create a copy of the extensions for
- * building the message.
- * <p>
- * For use by generated code only.
- */
- protected final FieldSet<ExtensionDescriptor> buildExtensions() {
- extensions.makeImmutable();
- extensionsIsMutable = false;
- return extensions;
+ instance.extensions.makeImmutable();
+ return super.buildPartial();
}
private void verifyExtensionContainingType(
@@ -406,22 +539,14 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
//@Override (Java 1.6 override semantics, but we must support 1.5)
public final <Type> boolean hasExtension(
final ExtensionLite<MessageType, Type> extension) {
- GeneratedExtension<MessageType, Type> extensionLite =
- checkIsLite(extension);
-
- verifyExtensionContainingType(extensionLite);
- return extensions.hasField(extensionLite.descriptor);
+ return instance.hasExtension(extension);
}
/** 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 ExtensionLite<MessageType, List<Type>> extension) {
- GeneratedExtension<MessageType, List<Type>> extensionLite =
- checkIsLite(extension);
-
- verifyExtensionContainingType(extensionLite);
- return extensions.getRepeatedFieldCount(extensionLite.descriptor);
+ return instance.getExtensionCount(extension);
}
/** Get the value of an extension. */
@@ -429,16 +554,7 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
@SuppressWarnings("unchecked")
public final <Type> Type getExtension(
final ExtensionLite<MessageType, Type> extension) {
- GeneratedExtension<MessageType, Type> extensionLite =
- checkIsLite(extension);
-
- verifyExtensionContainingType(extensionLite);
- final Object value = extensions.getField(extensionLite.descriptor);
- if (value == null) {
- return extensionLite.defaultValue;
- } else {
- return (Type) extensionLite.fromFieldSetType(value);
- }
+ return instance.getExtension(extension);
}
/** Get one element of a repeated extension. */
@@ -447,12 +563,7 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
public final <Type> Type getExtension(
final ExtensionLite<MessageType, List<Type>> extension,
final int index) {
- GeneratedExtension<MessageType, List<Type>> extensionLite =
- checkIsLite(extension);
-
- verifyExtensionContainingType(extensionLite);
- return (Type) extensionLite.singularFromFieldSetType(
- extensions.getRepeatedField(extensionLite.descriptor, index));
+ return instance.getExtension(extension, index);
}
// This is implemented here only to work around an apparent bug in the
@@ -471,9 +582,8 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
checkIsLite(extension);
verifyExtensionContainingType(extensionLite);
- ensureExtensionsIsMutable();
- extensions.setField(extensionLite.descriptor,
- extensionLite.toFieldSetType(value));
+ copyOnWrite();
+ instance.extensions.setField(extensionLite.descriptor, extensionLite.toFieldSetType(value));
return (BuilderType) this;
}
@@ -485,9 +595,9 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
checkIsLite(extension);
verifyExtensionContainingType(extensionLite);
- ensureExtensionsIsMutable();
- extensions.setRepeatedField(extensionLite.descriptor, index,
- extensionLite.singularToFieldSetType(value));
+ copyOnWrite();
+ instance.extensions.setRepeatedField(
+ extensionLite.descriptor, index, extensionLite.singularToFieldSetType(value));
return (BuilderType) this;
}
@@ -499,9 +609,9 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
checkIsLite(extension);
verifyExtensionContainingType(extensionLite);
- ensureExtensionsIsMutable();
- extensions.addRepeatedField(extensionLite.descriptor,
- extensionLite.singularToFieldSetType(value));
+ copyOnWrite();
+ instance.extensions.addRepeatedField(
+ extensionLite.descriptor, extensionLite.singularToFieldSetType(value));
return (BuilderType) this;
}
@@ -511,20 +621,10 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
GeneratedExtension<MessageType, ?> extensionLite = checkIsLite(extension);
verifyExtensionContainingType(extensionLite);
- ensureExtensionsIsMutable();
- extensions.clearField(extensionLite.descriptor);
+ copyOnWrite();
+ instance.extensions.clearField(extensionLite.descriptor);
return (BuilderType) this;
}
-
- /** Called by subclasses to check if all extensions are initialized. */
- protected boolean extensionsAreInitialized() {
- return extensions.isInitialized();
- }
-
- protected final void mergeExtensionFields(final MessageType other) {
- ensureExtensionsIsMutable();
- extensions.mergeFrom(((ExtendableMessage) other).extensions);
- }
}
//-----------------------------------------------------------------
@@ -991,7 +1091,10 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
* Checks that the {@link Extension} is Lite and returns it as a
* {@link GeneratedExtension}.
*/
- private static <MessageType extends ExtendableMessage<MessageType>, T>
+ private static <
+ MessageType extends ExtendableMessage<MessageType, BuilderType>,
+ BuilderType extends ExtendableBuilder<MessageType, BuilderType>,
+ T>
GeneratedExtension<MessageType, T> checkIsLite(
ExtensionLite<MessageType, T> extension) {
if (!extension.isLite()) {
@@ -1000,4 +1103,172 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite
return (GeneratedExtension<MessageType, T>) extension;
}
+
+ /**
+ * Represents the state needed to implement *ForType methods. Generated code
+ * must provide a static singleton instance by adding it with
+ * {@link GeneratedMessageLite#onLoad(Class, PrototypeHolder)} on class load.
+ * <ul>
+ * <li>{@link #getDefaultInstanceForType()}
+ * <li>{@link #getParserForType()}
+ * <li>{@link #newBuilderForType()}
+ * </ul>
+ * This allows us to trade three generated methods for a static Map.
+ */
+ protected static class PrototypeHolder<
+ MessageType extends GeneratedMessageLite<MessageType, BuilderType>,
+ BuilderType extends GeneratedMessageLite.Builder<
+ MessageType, BuilderType>> {
+
+ private final MessageType defaultInstance;
+ private final Parser<MessageType> parser;
+
+ public PrototypeHolder(
+ MessageType defaultInstance, Parser<MessageType> parser) {
+ this.defaultInstance = defaultInstance;
+ this.parser = parser;
+ }
+
+ public MessageType getDefaultInstanceForType() {
+ return defaultInstance;
+ }
+
+ public Parser<MessageType> getParserForType() {
+ return parser;
+ }
+
+ @SuppressWarnings("unchecked") // Guaranteed by runtime.
+ public BuilderType newBuilderForType() {
+ return (BuilderType) defaultInstance.toBuilder();
+ }
+ }
+
+ /**
+ * A static helper method for checking if a message is initialized, optionally memoizing.
+ * <p>
+ * For use by generated code only.
+ */
+ protected static final <T extends GeneratedMessageLite<T, ?>> boolean isInitialized(
+ T message, boolean shouldMemoize) {
+ return message.dynamicMethod(MethodToInvoke.IS_INITIALIZED, shouldMemoize) != null;
+ }
+
+ protected static final <T extends GeneratedMessageLite<T, ?>> void makeImmutable(T message) {
+ message.dynamicMethod(MethodToInvoke.MAKE_IMMUTABLE);
+ }
+
+ /**
+ * A static helper method for parsing a partial from input using the extension registry and the
+ * instance.
+ */
+ static <T extends GeneratedMessageLite<T, ?>> T parsePartialFrom(
+ T instance, CodedInputStream input, ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException {
+ try {
+ return (T) instance.dynamicMethod(
+ MethodToInvoke.PARSE_PARTIAL_FROM, input, extensionRegistry);
+ } catch (RuntimeException e) {
+ if (e.getCause() instanceof InvalidProtocolBufferException) {
+ throw (InvalidProtocolBufferException) e.getCause();
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * A {@link Parser} implementation that delegates to the default instance.
+ * <p>
+ * For use by generated code only.
+ */
+ protected static class DefaultInstanceBasedParser<T extends GeneratedMessageLite<T, ?>>
+ extends AbstractParser<T> {
+
+ private T defaultInstance;
+
+ public DefaultInstanceBasedParser(T defaultInstance) {
+ this.defaultInstance = defaultInstance;
+ }
+
+ @Override
+ public T parsePartialFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException {
+ return GeneratedMessageLite.parsePartialFrom(defaultInstance, input, extensionRegistry);
+ }
+ }
+
+ protected static IntList newIntList() {
+ return new IntArrayList();
+ }
+
+ protected static IntList newIntList(List<Integer> toCopy) {
+ return new IntArrayList(toCopy);
+ }
+
+ protected static IntList emptyIntList() {
+ return IntArrayList.emptyList();
+ }
+
+ protected static LongList newLongList() {
+ return new LongArrayList();
+ }
+
+ protected static LongList newLongList(List<Long> toCopy) {
+ return new LongArrayList(toCopy);
+ }
+
+ protected static LongList emptyLongList() {
+ return LongArrayList.emptyList();
+ }
+
+ protected static FloatList newFloatList() {
+ return new FloatArrayList();
+ }
+
+ protected static FloatList newFloatList(List<Float> toCopy) {
+ return new FloatArrayList(toCopy);
+ }
+
+ protected static FloatList emptyFloatList() {
+ return FloatArrayList.emptyList();
+ }
+
+ protected static DoubleList newDoubleList() {
+ return new DoubleArrayList();
+ }
+
+ protected static DoubleList newDoubleList(List<Double> toCopy) {
+ return new DoubleArrayList(toCopy);
+ }
+
+ protected static DoubleList emptyDoubleList() {
+ return DoubleArrayList.emptyList();
+ }
+
+ protected static BooleanList newBooleanList() {
+ return new BooleanArrayList();
+ }
+
+ protected static BooleanList newBooleanList(List<Boolean> toCopy) {
+ return new BooleanArrayList(toCopy);
+ }
+
+ protected static BooleanList emptyBooleanList() {
+ return BooleanArrayList.emptyList();
+ }
+
+ protected static <E> ProtobufList<E> newProtobufList() {
+ return new ProtobufArrayList<E>();
+ }
+
+ protected static <E> ProtobufList<E> newProtobufList(List<E> toCopy) {
+ return new ProtobufArrayList<E>(toCopy);
+ }
+
+ protected static <E> ProtobufList<E> emptyProtobufList() {
+ return ProtobufArrayList.emptyList();
+ }
+
+ protected static LazyStringArrayList emptyLazyStringArrayList() {
+ return LazyStringArrayList.emptyList();
+ }
}
diff --git a/java/src/main/java/com/google/protobuf/IntArrayList.java b/java/src/main/java/com/google/protobuf/IntArrayList.java
new file mode 100644
index 00000000..f7609cc9
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/IntArrayList.java
@@ -0,0 +1,242 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import com.google.protobuf.Internal.IntList;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.RandomAccess;
+
+/**
+ * An implementation of {@link IntList} on top of a primitive array.
+ *
+ * @author dweis@google.com (Daniel Weis)
+ */
+final class IntArrayList extends AbstractProtobufList<Integer> implements IntList, RandomAccess {
+
+ private static final int DEFAULT_CAPACITY = 10;
+
+ private static final IntArrayList EMPTY_LIST = new IntArrayList();
+ static {
+ EMPTY_LIST.makeImmutable();
+ }
+
+ public static IntArrayList emptyList() {
+ return EMPTY_LIST;
+ }
+
+ /**
+ * The backing store for the list.
+ */
+ private int[] array;
+
+ /**
+ * The size of the list distinct from the length of the array. That is, it is the number of
+ * elements set in the list.
+ */
+ private int size;
+
+ /**
+ * Constructs a new mutable {@code IntArrayList}.
+ */
+ IntArrayList() {
+ array = new int[DEFAULT_CAPACITY];
+ size = 0;
+ }
+
+ /**
+ * Constructs a new mutable {@code IntArrayList} containing the same elements as {@code other}.
+ */
+ IntArrayList(List<Integer> other) {
+ if (other instanceof IntArrayList) {
+ IntArrayList list = (IntArrayList) other;
+ array = list.array.clone();
+ size = list.size;
+ } else {
+ size = other.size();
+ array = new int[size];
+ for (int i = 0; i < size; i++) {
+ array[i] = other.get(i);
+ }
+ }
+ }
+
+ @Override
+ public Integer get(int index) {
+ return getInt(index);
+ }
+
+ @Override
+ public int getInt(int index) {
+ ensureIndexInRange(index);
+ return array[index];
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+
+ @Override
+ public Integer set(int index, Integer element) {
+ return setInt(index, element);
+ }
+
+ @Override
+ public int setInt(int index, int element) {
+ ensureIsMutable();
+ ensureIndexInRange(index);
+ int previousValue = array[index];
+ array[index] = element;
+ return previousValue;
+ }
+
+ @Override
+ public void add(int index, Integer element) {
+ addInt(index, element);
+ }
+
+ /**
+ * Like {@link #add(Integer)} but more efficient in that it doesn't box the element.
+ */
+ @Override
+ public void addInt(int element) {
+ addInt(size, element);
+ }
+
+ /**
+ * Like {@link #add(int, Integer)} but more efficient in that it doesn't box the element.
+ */
+ private void addInt(int index, int element) {
+ ensureIsMutable();
+ if (index < 0 || index > size) {
+ throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index));
+ }
+
+ if (size < array.length) {
+ // Shift everything over to make room
+ System.arraycopy(array, index, array, index + 1, size - index);
+ } else {
+ // Resize to 1.5x the size
+ int length = ((size * 3) / 2) + 1;
+ int[] newArray = new int[length];
+
+ // Copy the first part directly
+ System.arraycopy(array, 0, newArray, 0, index);
+
+ // Copy the rest shifted over by one to make room
+ System.arraycopy(array, index, newArray, index + 1, size - index);
+ array = newArray;
+ }
+
+ array[index] = element;
+ size++;
+ modCount++;
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends Integer> collection) {
+ ensureIsMutable();
+
+ if (collection == null) {
+ throw new NullPointerException();
+ }
+
+ // We specialize when adding another IntArrayList to avoid boxing elements.
+ if (!(collection instanceof IntArrayList)) {
+ return super.addAll(collection);
+ }
+
+ IntArrayList list = (IntArrayList) collection;
+ if (list.size == 0) {
+ return false;
+ }
+
+ int overflow = Integer.MAX_VALUE - size;
+ if (overflow < list.size) {
+ // We can't actually represent a list this large.
+ throw new OutOfMemoryError();
+ }
+
+ int newSize = size + list.size;
+ if (newSize > array.length) {
+ array = Arrays.copyOf(array, newSize);
+ }
+
+ System.arraycopy(list.array, 0, array, size, list.size);
+ size = newSize;
+ modCount++;
+ return true;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ ensureIsMutable();
+ for (int i = 0; i < size; i++) {
+ if (o.equals(array[i])) {
+ System.arraycopy(array, i + 1, array, i, size - i);
+ size--;
+ modCount++;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Integer remove(int index) {
+ ensureIsMutable();
+ ensureIndexInRange(index);
+ int value = array[index];
+ System.arraycopy(array, index + 1, array, index, size - index);
+ size--;
+ modCount++;
+ return value;
+ }
+
+ /**
+ * Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an
+ * {@link IndexOutOfBoundsException} if it is not.
+ *
+ * @param index the index to verify is in range
+ */
+ private void ensureIndexInRange(int index) {
+ if (index < 0 || index >= size) {
+ throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index));
+ }
+ }
+
+ private String makeOutOfBoundsExceptionMessage(int index) {
+ return "Index:" + index + ", Size:" + size;
+ }
+}
diff --git a/java/src/main/java/com/google/protobuf/Internal.java b/java/src/main/java/com/google/protobuf/Internal.java
index 5a0de6d1..20054b79 100644
--- a/java/src/main/java/com/google/protobuf/Internal.java
+++ b/java/src/main/java/com/google/protobuf/Internal.java
@@ -31,8 +31,8 @@
package com.google.protobuf;
import java.io.IOException;
-import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
import java.util.AbstractList;
import java.util.AbstractMap;
import java.util.AbstractSet;
@@ -51,6 +51,10 @@ import java.util.Set;
* @author kenton@google.com (Kenton Varda)
*/
public class Internal {
+
+ protected static final Charset UTF_8 = Charset.forName("UTF-8");
+ protected static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
+
/**
* Helper called by generated code to construct default values for string
* fields.
@@ -80,14 +84,7 @@ public class Internal {
* 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);
- }
+ return new String(bytes.getBytes(ISO_8859_1), UTF_8);
}
/**
@@ -99,37 +96,23 @@ public class Internal {
* 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);
- }
+ return ByteString.copyFrom(bytes.getBytes(ISO_8859_1));
}
/**
* Helper called by generated code to construct default values for bytes
* fields.
* <p>
- * This is like {@link #bytesDefaultValue}, but returns a byte array.
+ * This is like {@link #bytesDefaultValue}, but returns a byte array.
*/
public static byte[] byteArrayDefaultValue(String bytes) {
- try {
- return 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);
- }
+ return bytes.getBytes(ISO_8859_1);
}
/**
* Helper called by generated code to construct default values for bytes
* fields.
* <p>
- * This is like {@link #bytesDefaultValue}, but returns a ByteBuffer.
+ * This is like {@link #bytesDefaultValue}, but returns a ByteBuffer.
*/
public static ByteBuffer byteBufferDefaultValue(String bytes) {
return ByteBuffer.wrap(byteArrayDefaultValue(bytes));
@@ -185,7 +168,7 @@ public class Internal {
public static boolean isValidUtf8(ByteString byteString) {
return byteString.isValidUtf8();
}
-
+
/**
* Like {@link #isValidUtf8(ByteString)} but for byte arrays.
*/
@@ -197,22 +180,14 @@ public class Internal {
* Helper method to get the UTF-8 bytes of a string.
*/
public static byte[] toByteArray(String value) {
- try {
- return value.getBytes("UTF-8");
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException("UTF-8 not supported?", e);
- }
+ return value.getBytes(UTF_8);
}
-
+
/**
* Helper method to convert a byte array to a string using UTF-8 encoding.
*/
public static String toStringUtf8(byte[] bytes) {
- try {
- return new String(bytes, "UTF-8");
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException("UTF-8 not supported?", e);
- }
+ return new String(bytes, UTF_8);
}
/**
@@ -274,7 +249,7 @@ public class Internal {
}
return hash;
}
-
+
/**
* Helper method for implementing {@link Message#equals(Object)} for bytes field.
*/
@@ -298,7 +273,7 @@ public class Internal {
}
return hash;
}
-
+
/**
* Helper method for implementing {@link Message#hashCode()} for bytes field.
*/
@@ -309,7 +284,7 @@ public class Internal {
// based hashCode() method.
return LiteralByteString.hashCode(bytes);
}
-
+
/**
* Helper method for implementing {@link Message#equals(Object)} for bytes
* field.
@@ -322,7 +297,7 @@ public class Internal {
// compare all the content.
return a.duplicate().clear().equals(b.duplicate().clear());
}
-
+
/**
* Helper method for implementing {@link Message#equals(Object)} for bytes
* field.
@@ -351,9 +326,9 @@ public class Internal {
}
return hash;
}
-
+
private static final int DEFAULT_BUFFER_SIZE = 4096;
-
+
/**
* Helper method for implementing {@link Message#hashCode()} for bytes
* field.
@@ -382,18 +357,18 @@ public class Internal {
return h == 0 ? 1 : h;
}
}
-
+
/**
* An empty byte array constant used in generated code.
*/
public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
-
+
/**
* An empty byte array constant used in generated code.
*/
public static final ByteBuffer EMPTY_BYTE_BUFFER =
ByteBuffer.wrap(EMPTY_BYTE_ARRAY);
-
+
/** An empty coded input stream constant used in generated code. */
public static final CodedInputStream EMPTY_CODED_INPUT_STREAM =
CodedInputStream.newInstance(EMPTY_BYTE_ARRAY);
@@ -458,13 +433,13 @@ public class Internal {
private final Map<K, RealValue> realMap;
private final Converter<RealValue, V> valueConverter;
-
+
public MapAdapter(Map<K, RealValue> realMap,
Converter<RealValue, V> valueConverter) {
this.realMap = realMap;
this.valueConverter = valueConverter;
}
-
+
@SuppressWarnings("unchecked")
@Override
public V get(Object key) {
@@ -474,7 +449,7 @@ public class Internal {
}
return valueConverter.doForward(result);
}
-
+
@Override
public V put(K key, V value) {
RealValue oldValue = realMap.put(key, valueConverter.doBackward(value));
@@ -488,13 +463,13 @@ public class Internal {
public Set<java.util.Map.Entry<K, V>> entrySet() {
return new SetAdapter(realMap.entrySet());
}
-
+
private class SetAdapter extends AbstractSet<Map.Entry<K, V>> {
private final Set<Map.Entry<K, RealValue>> realSet;
public SetAdapter(Set<Map.Entry<K, RealValue>> realSet) {
this.realSet = realSet;
}
-
+
@Override
public Iterator<java.util.Map.Entry<K, V>> iterator() {
return new IteratorAdapter(realSet.iterator());
@@ -503,17 +478,17 @@ public class Internal {
@Override
public int size() {
return realSet.size();
- }
+ }
}
-
+
private class IteratorAdapter implements Iterator<Map.Entry<K, V>> {
private final Iterator<Map.Entry<K, RealValue>> realIterator;
-
+
public IteratorAdapter(
Iterator<Map.Entry<K, RealValue>> realIterator) {
this.realIterator = realIterator;
}
-
+
@Override
public boolean hasNext() {
return realIterator.hasNext();
@@ -529,14 +504,14 @@ public class Internal {
realIterator.remove();
}
}
-
+
private class EntryAdapter implements Map.Entry<K, V> {
private final Map.Entry<K, RealValue> realEntry;
-
+
public EntryAdapter(Map.Entry<K, RealValue> realEntry) {
this.realEntry = realEntry;
}
-
+
@Override
public K getKey() {
return realEntry.getKey();
@@ -558,4 +533,132 @@ public class Internal {
}
}
}
+
+ /**
+ * Extends {@link List} to add the capability to make the list immutable and inspect if it is
+ * modifiable.
+ */
+ public static interface ProtobufList<E> extends List<E> {
+
+ /**
+ * Makes this list immutable. All subsequent modifications will throw an
+ * {@link UnsupportedOperationException}.
+ */
+ void makeImmutable();
+
+ /**
+ * Returns whether this list can be modified via the publicly accessible {@link List} methods.
+ */
+ boolean isModifiable();
+ }
+
+ /**
+ * A {@link java.util.List} implementation that avoids boxing the elements into Integers if
+ * possible. Does not support null elements.
+ */
+ public static interface IntList extends ProtobufList<Integer> {
+
+ /**
+ * Like {@link #get(int)} but more efficient in that it doesn't box the returned value.
+ */
+ int getInt(int index);
+
+ /**
+ * Like {@link #add(Integer)} but more efficient in that it doesn't box the element.
+ */
+ void addInt(int element);
+
+ /**
+ * Like {@link #set(int, Integer)} but more efficient in that it doesn't box the element.
+ */
+ int setInt(int index, int element);
+ }
+
+ /**
+ * A {@link java.util.List} implementation that avoids boxing the elements into Booleans if
+ * possible. Does not support null elements.
+ */
+ public static interface BooleanList extends ProtobufList<Boolean> {
+
+ /**
+ * Like {@link #get(int)} but more efficient in that it doesn't box the returned value.
+ */
+ boolean getBoolean(int index);
+
+ /**
+ * Like {@link #add(Boolean)} but more efficient in that it doesn't box the element.
+ */
+ void addBoolean(boolean element);
+
+ /**
+ * Like {@link #set(int, Boolean)} but more efficient in that it doesn't box the element.
+ */
+ boolean setBoolean(int index, boolean element);
+ }
+
+ /**
+ * A {@link java.util.List} implementation that avoids boxing the elements into Longs if
+ * possible. Does not support null elements.
+ */
+ public static interface LongList extends ProtobufList<Long> {
+
+ /**
+ * Like {@link #get(int)} but more efficient in that it doesn't box the returned value.
+ */
+ long getLong(int index);
+
+ /**
+ * Like {@link #add(Long)} but more efficient in that it doesn't box the element.
+ */
+ void addLong(long element);
+
+ /**
+ * Like {@link #set(int, Long)} but more efficient in that it doesn't box the element.
+ */
+ long setLong(int index, long element);
+ }
+
+ /**
+ * A {@link java.util.List} implementation that avoids boxing the elements into Doubles if
+ * possible. Does not support null elements.
+ */
+ public static interface DoubleList extends ProtobufList<Double> {
+
+ /**
+ * Like {@link #get(int)} but more efficient in that it doesn't box the returned value.
+ */
+ double getDouble(int index);
+
+ /**
+ * Like {@link #add(Double)} but more efficient in that it doesn't box the element.
+ */
+ void addDouble(double element);
+
+ /**
+ * Like {@link #set(int, Double)} but more efficient in that it doesn't box the element.
+ */
+ double setDouble(int index, double element);
+ }
+
+ /**
+ * A {@link java.util.List} implementation that avoids boxing the elements into Floats if
+ * possible. Does not support null elements.
+ */
+ public static interface FloatList extends ProtobufList<Float> {
+
+ /**
+ * Like {@link #get(int)} but more efficient in that it doesn't box the returned value.
+ */
+ float getFloat(int index);
+
+ /**
+ * Like {@link #add(Float)} but more efficient in that it doesn't box the element.
+ */
+ void addFloat(float element);
+
+ /**
+ * Like {@link #set(int, Float)} but more efficient in that it doesn't box the element.
+ */
+ float setFloat(int index, float element);
+ }
}
diff --git a/java/src/main/java/com/google/protobuf/LazyFieldLite.java b/java/src/main/java/com/google/protobuf/LazyFieldLite.java
index 1fc80e87..eea1fe3c 100644
--- a/java/src/main/java/com/google/protobuf/LazyFieldLite.java
+++ b/java/src/main/java/com/google/protobuf/LazyFieldLite.java
@@ -30,8 +30,6 @@
package com.google.protobuf;
-import java.io.IOException;
-
/**
* LazyFieldLite encapsulates the logic of lazily parsing message fields. It stores
* the message in a ByteString initially and then parse it on-demand.
@@ -44,40 +42,102 @@ import java.io.IOException;
* @author xiangl@google.com (Xiang Li)
*/
public class LazyFieldLite {
- private ByteString bytes;
+ private static final ExtensionRegistryLite EMPTY_REGISTRY =
+ ExtensionRegistryLite.getEmptyRegistry();
+
+ /**
+ * A delayed-parsed version of the bytes. When this is non-null then {@code extensionRegistry } is
+ * also non-null and {@code value} and {@code memoizedBytes} are null.
+ */
+ private ByteString delayedBytes;
+
+ /**
+ * An {@code ExtensionRegistryLite} for parsing bytes. It is non-null on a best-effort basis. It
+ * is only guaranteed to be non-null if this message was initialized using bytes and an
+ * {@code ExtensionRegistry}. If it directly had a value set then it will be null, unless it has
+ * been merged with another {@code LazyFieldLite} that had an {@code ExtensionRegistry}.
+ */
private ExtensionRegistryLite extensionRegistry;
- private volatile boolean isDirty = false;
+ /**
+ * The parsed value. When this is non-null then {@code delayedBytes} will be null.
+ */
protected volatile MessageLite value;
+ /**
+ * The memoized bytes for {@code value}. Will be null when {@code value} is null.
+ */
+ private volatile ByteString memoizedBytes;
+
+ /**
+ * Constructs a LazyFieldLite with bytes that will be parsed lazily.
+ */
public LazyFieldLite(ExtensionRegistryLite extensionRegistry, ByteString bytes) {
+ checkArguments(extensionRegistry, bytes);
this.extensionRegistry = extensionRegistry;
- this.bytes = bytes;
+ this.delayedBytes = bytes;
}
+ /**
+ * Constructs a LazyFieldLite with no contents, and no ability to parse extensions.
+ */
public LazyFieldLite() {
}
+ /**
+ * Constructs a LazyFieldLite instance with a value. The LazyFieldLite may not be able to parse
+ * the extensions in the value as it has no ExtensionRegistry.
+ */
public static LazyFieldLite fromValue(MessageLite value) {
LazyFieldLite lf = new LazyFieldLite();
lf.setValue(value);
return lf;
}
+ /**
+ * Determines whether this LazyFieldLite instance represents the default instance of this type.
+ */
public boolean containsDefaultInstance() {
- return value == null && bytes == null;
+ return memoizedBytes == ByteString.EMPTY
+ || value == null && (delayedBytes == null || delayedBytes == ByteString.EMPTY);
}
+ /**
+ * Clears the value state of this instance.
+ *
+ * <p>LazyField is not thread-safe for write access. Synchronizations are needed
+ * under read/write situations.
+ */
public void clear() {
- bytes = null;
+ // Don't clear the ExtensionRegistry. It might prove useful later on when merging in another
+ // value, but there is no guarantee that it will contain all extensions that were directly set
+ // on the values that need to be merged.
+ delayedBytes = null;
value = null;
- extensionRegistry = null;
- isDirty = true;
+ memoizedBytes = null;
+ }
+
+ /**
+ * Overrides the contents of this LazyField.
+ *
+ * <p>LazyField is not thread-safe for write access. Synchronizations are needed
+ * under read/write situations.
+ */
+ public void set(LazyFieldLite other) {
+ this.delayedBytes = other.delayedBytes;
+ this.value = other.value;
+ this.memoizedBytes = other.memoizedBytes;
+ // If the other LazyFieldLite was created by directly setting the value rather than first by
+ // parsing, then it will not have an extensionRegistry. In this case we hold on to the existing
+ // extensionRegistry, which has no guarantees that it has all the extensions that will be
+ // directly set on the value.
+ if (other.extensionRegistry != null) {
+ this.extensionRegistry = other.extensionRegistry;
+ }
}
/**
- * Returns message instance. At first time, serialized data is parsed by
- * {@code defaultInstance.getParserForType()}.
+ * Returns message instance. It may do some thread-safe delayed parsing of bytes.
*
* @param defaultInstance its message's default instance. It's also used to get parser for the
* message type.
@@ -88,38 +148,109 @@ public class LazyFieldLite {
}
/**
- * LazyField is not thread-safe for write access. Synchronizations are needed
+ * Sets the value of the instance and returns the old value without delay parsing anything.
+ *
+ * <p>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.delayedBytes = null;
+ this.memoizedBytes = null;
this.value = value;
- bytes = null;
- isDirty = true;
return originalValue;
}
- public void merge(LazyFieldLite value) {
- if (value.containsDefaultInstance()) {
+ /**
+ * Merges another instance's contents. In some cases may drop some extensions if both fields
+ * contain data. If the other field has an {@code ExtensionRegistry} but this does not, then this
+ * field will copy over that {@code ExtensionRegistry}.
+ *
+ * <p>LazyField is not thread-safe for write access. Synchronizations are needed
+ * under read/write situations.
+ */
+ public void merge(LazyFieldLite other) {
+ if (other.containsDefaultInstance()) {
return;
}
- if (bytes == null) {
- this.bytes = value.bytes;
+ if (this.containsDefaultInstance()) {
+ set(other);
+ return;
+ }
+
+ // If the other field has an extension registry but this does not, copy over the other extension
+ // registry.
+ if (this.extensionRegistry == null) {
+ this.extensionRegistry = other.extensionRegistry;
+ }
+
+ // In the case that both of them are not parsed we simply concatenate the bytes to save time. In
+ // the (probably rare) case that they have different extension registries there is a chance that
+ // some of the extensions may be dropped, but the tradeoff of making this operation fast seems
+ // to outway the benefits of combining the extension registries, which is not normally done for
+ // lite protos anyways.
+ if (this.delayedBytes != null && other.delayedBytes != null) {
+ this.delayedBytes = this.delayedBytes.concat(other.delayedBytes);
+ return;
+ }
+
+ // At least one is parsed and both contain data. We won't drop any extensions here directly, but
+ // in the case that the extension registries are not the same then we might in the future if we
+ // need to serialze and parse a message again.
+ if (this.value == null && other.value != null) {
+ setValue(mergeValueAndBytes(other.value, this.delayedBytes, this.extensionRegistry));
+ return;
+ } else if (this.value != null && other.value == null) {
+ setValue(mergeValueAndBytes(this.value, other.delayedBytes, other.extensionRegistry));
+ return;
+ }
+
+ // At this point we have two fully parsed messages. We can't merge directly from one to the
+ // other because only generated builder code contains methods to mergeFrom another parsed
+ // message. We have to serialize one instance and then merge the bytes into the other. This may
+ // drop extensions from one of the messages if one of the values had an extension set on it
+ // directly.
+ //
+ // To mitigate this we prefer serializing a message that has an extension registry, and
+ // therefore a chance that all extensions set on it are in that registry.
+ //
+ // NOTE: The check for other.extensionRegistry not being null must come first because at this
+ // point in time if other.extensionRegistry is not null then this.extensionRegistry will not be
+ // null either.
+ if (other.extensionRegistry != null) {
+ setValue(mergeValueAndBytes(this.value, other.toByteString(), other.extensionRegistry));
+ return;
+ } else if (this.extensionRegistry != null) {
+ setValue(mergeValueAndBytes(other.value, this.toByteString(), this.extensionRegistry));
+ return;
} else {
- this.bytes.concat(value.toByteString());
+ // All extensions from the other message will be dropped because we have no registry.
+ setValue(mergeValueAndBytes(this.value, other.toByteString(), EMPTY_REGISTRY));
+ return;
}
- isDirty = false;
}
- public void setByteString(ByteString bytes, ExtensionRegistryLite extensionRegistry) {
- this.bytes = bytes;
- this.extensionRegistry = extensionRegistry;
- isDirty = false;
+ private static MessageLite mergeValueAndBytes(
+ MessageLite value, ByteString otherBytes, ExtensionRegistryLite extensionRegistry) {
+ try {
+ return value.toBuilder().mergeFrom(otherBytes, extensionRegistry).build();
+ } catch (InvalidProtocolBufferException e) {
+ // Nothing is logged and no exceptions are thrown. Clients will be unaware that a proto
+ // was invalid.
+ return value;
+ }
}
- public ExtensionRegistryLite getExtensionRegistry() {
- return extensionRegistry;
+ /**
+ * Sets this field with bytes to delay-parse.
+ */
+ public void setByteString(ByteString bytes, ExtensionRegistryLite extensionRegistry) {
+ checkArguments(extensionRegistry, bytes);
+ this.delayedBytes = bytes;
+ this.extensionRegistry = extensionRegistry;
+ this.value = null;
+ this.memoizedBytes = null;
}
/**
@@ -128,30 +259,43 @@ public class LazyFieldLite {
* parsed. Be careful when using this method.
*/
public int getSerializedSize() {
- if (isDirty) {
+ if (delayedBytes != null) {
+ return delayedBytes.size();
+ } else if (memoizedBytes != null) {
+ return memoizedBytes.size();
+ } else if (value != null) {
return value.getSerializedSize();
+ } else {
+ return 0;
}
- return bytes.size();
}
+ /**
+ * Returns a BytesString for this field in a thread-safe way.
+ */
public ByteString toByteString() {
- if (!isDirty) {
- return bytes;
+ if (delayedBytes != null) {
+ return delayedBytes;
+ }
+ if (memoizedBytes != null) {
+ return memoizedBytes;
}
synchronized (this) {
- if (!isDirty) {
- return bytes;
+ if (memoizedBytes != null) {
+ return memoizedBytes;
}
if (value == null) {
- bytes = ByteString.EMPTY;
+ memoizedBytes = ByteString.EMPTY;
} else {
- bytes = value.toByteString();
+ memoizedBytes = value.toByteString();
}
- isDirty = false;
- return bytes;
+ return memoizedBytes;
}
}
+ /**
+ * Might lazily parse the bytes that were previously passed in. Is thread-safe.
+ */
protected void ensureInitialized(MessageLite defaultInstance) {
if (value != null) {
return;
@@ -161,16 +305,35 @@ public class LazyFieldLite {
return;
}
try {
- if (bytes != null) {
- value = defaultInstance.getParserForType()
- .parseFrom(bytes, extensionRegistry);
+ if (delayedBytes != null) {
+ // The extensionRegistry shouldn't be null here since we have delayedBytes.
+ MessageLite parsedValue = defaultInstance.getParserForType()
+ .parseFrom(delayedBytes, extensionRegistry);
+ this.value = parsedValue;
+ this.memoizedBytes = delayedBytes;
+ this.delayedBytes = null;
} else {
- value = defaultInstance;
+ this.value = defaultInstance;
+ this.memoizedBytes = ByteString.EMPTY;
+ this.delayedBytes = null;
}
- } catch (IOException e) {
- // TODO(xiangl): Refactory the API to support the exception thrown from
- // lazily load messages.
+ } catch (InvalidProtocolBufferException e) {
+ // Nothing is logged and no exceptions are thrown. Clients will be unaware that this proto
+ // was invalid.
+ this.value = defaultInstance;
+ this.memoizedBytes = ByteString.EMPTY;
+ this.delayedBytes = null;
}
}
}
+
+
+ private static void checkArguments(ExtensionRegistryLite extensionRegistry, ByteString bytes) {
+ if (extensionRegistry == null) {
+ throw new NullPointerException("found null ExtensionRegistry");
+ }
+ if (bytes == null) {
+ throw new NullPointerException("found null ByteString");
+ }
+ }
}
diff --git a/java/src/main/java/com/google/protobuf/LazyStringArrayList.java b/java/src/main/java/com/google/protobuf/LazyStringArrayList.java
index 2d40a51f..a2997e1c 100644
--- a/java/src/main/java/com/google/protobuf/LazyStringArrayList.java
+++ b/java/src/main/java/com/google/protobuf/LazyStringArrayList.java
@@ -62,11 +62,20 @@ import java.util.RandomAccess;
*
* @author jonp@google.com (Jon Perlow)
*/
-public class LazyStringArrayList extends AbstractList<String>
+public class LazyStringArrayList extends AbstractProtobufList<String>
implements LazyStringList, RandomAccess {
+
+ private static final LazyStringArrayList EMPTY_LIST = new LazyStringArrayList();
+ static {
+ EMPTY_LIST.makeImmutable();
+ }
+
+ static LazyStringArrayList emptyList() {
+ return EMPTY_LIST;
+ }
- public static final LazyStringList EMPTY =
- new LazyStringArrayList().getUnmodifiableView();
+ // For compatibility with older runtimes.
+ public static final LazyStringList EMPTY = EMPTY_LIST;
private final List<Object> list;
@@ -116,12 +125,26 @@ public class LazyStringArrayList extends AbstractList<String>
@Override
public String set(int index, String s) {
+ ensureIsMutable();
Object o = list.set(index, s);
return asString(o);
}
@Override
public void add(int index, String element) {
+ ensureIsMutable();
+ list.add(index, element);
+ modCount++;
+ }
+
+ private void add(int index, ByteString element) {
+ ensureIsMutable();
+ list.add(index, element);
+ modCount++;
+ }
+
+ private void add(int index, byte[] element) {
+ ensureIsMutable();
list.add(index, element);
modCount++;
}
@@ -137,6 +160,7 @@ public class LazyStringArrayList extends AbstractList<String>
@Override
public boolean addAll(int index, Collection<? extends String> c) {
+ ensureIsMutable();
// 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
@@ -148,6 +172,7 @@ public class LazyStringArrayList extends AbstractList<String>
// @Override
public boolean addAllByteString(Collection<? extends ByteString> values) {
+ ensureIsMutable();
boolean ret = list.addAll(values);
modCount++;
return ret;
@@ -155,6 +180,7 @@ public class LazyStringArrayList extends AbstractList<String>
// @Override
public boolean addAllByteArray(Collection<byte[]> c) {
+ ensureIsMutable();
boolean ret = list.addAll(c);
modCount++;
return ret;
@@ -162,6 +188,7 @@ public class LazyStringArrayList extends AbstractList<String>
@Override
public String remove(int index) {
+ ensureIsMutable();
Object o = list.remove(index);
modCount++;
return asString(o);
@@ -169,18 +196,21 @@ public class LazyStringArrayList extends AbstractList<String>
@Override
public void clear() {
+ ensureIsMutable();
list.clear();
modCount++;
}
// @Override
public void add(ByteString element) {
+ ensureIsMutable();
list.add(element);
modCount++;
}
// @Override
public void add(byte[] element) {
+ ensureIsMutable();
list.add(element);
modCount++;
}
@@ -207,14 +237,23 @@ public class LazyStringArrayList extends AbstractList<String>
// @Override
public void set(int index, ByteString s) {
- list.set(index, s);
+ setAndReturn(index, s);
+ }
+
+ private Object setAndReturn(int index, ByteString s) {
+ ensureIsMutable();
+ return list.set(index, s);
}
// @Override
public void set(int index, byte[] s) {
- list.set(index, s);
+ setAndReturn(index, s);
+ }
+
+ private Object setAndReturn(int index, byte[] s) {
+ ensureIsMutable();
+ return list.set(index, s);
}
-
private static String asString(Object o) {
if (o instanceof String) {
@@ -253,6 +292,7 @@ public class LazyStringArrayList extends AbstractList<String>
// @Override
public void mergeFrom(LazyStringList other) {
+ ensureIsMutable();
for (Object o : other.getUnderlyingElements()) {
if (o instanceof byte[]) {
byte[] b = (byte[]) o;
@@ -267,20 +307,15 @@ public class LazyStringArrayList extends AbstractList<String>
private static class ByteArrayListView extends AbstractList<byte[]>
implements RandomAccess {
- private final List<Object> list;
+ private final LazyStringArrayList list;
- ByteArrayListView(List<Object> list) {
+ ByteArrayListView(LazyStringArrayList list) {
this.list = list;
}
@Override
public byte[] get(int index) {
- Object o = list.get(index);
- byte[] b = asByteArray(o);
- if (b != o) {
- list.set(index, b);
- }
- return b;
+ return list.getByteArray(index);
}
@Override
@@ -290,7 +325,7 @@ public class LazyStringArrayList extends AbstractList<String>
@Override
public byte[] set(int index, byte[] s) {
- Object o = list.set(index, s);
+ Object o = list.setAndReturn(index, s);
modCount++;
return asByteArray(o);
}
@@ -311,25 +346,20 @@ public class LazyStringArrayList extends AbstractList<String>
// @Override
public List<byte[]> asByteArrayList() {
- return new ByteArrayListView(list);
+ return new ByteArrayListView(this);
}
private static class ByteStringListView extends AbstractList<ByteString>
implements RandomAccess {
- private final List<Object> list;
+ private final LazyStringArrayList list;
- ByteStringListView(List<Object> list) {
+ ByteStringListView(LazyStringArrayList list) {
this.list = list;
}
@Override
public ByteString get(int index) {
- Object o = list.get(index);
- ByteString b = asByteString(o);
- if (b != o) {
- list.set(index, b);
- }
- return b;
+ return list.getByteString(index);
}
@Override
@@ -339,7 +369,7 @@ public class LazyStringArrayList extends AbstractList<String>
@Override
public ByteString set(int index, ByteString s) {
- Object o = list.set(index, s);
+ Object o = list.setAndReturn(index, s);
modCount++;
return asByteString(o);
}
@@ -360,12 +390,15 @@ public class LazyStringArrayList extends AbstractList<String>
// @Override
public List<ByteString> asByteStringList() {
- return new ByteStringListView(list);
+ return new ByteStringListView(this);
}
// @Override
public LazyStringList getUnmodifiableView() {
- return new UnmodifiableLazyStringList(this);
+ if (isModifiable()) {
+ return new UnmodifiableLazyStringList(this);
+ }
+ return this;
}
}
diff --git a/java/src/main/java/com/google/protobuf/LiteralByteString.java b/java/src/main/java/com/google/protobuf/LiteralByteString.java
index 767b9f35..c5a8512a 100644
--- a/java/src/main/java/com/google/protobuf/LiteralByteString.java
+++ b/java/src/main/java/com/google/protobuf/LiteralByteString.java
@@ -34,8 +34,8 @@ 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.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
@@ -112,7 +112,7 @@ class LiteralByteString extends ByteString {
// ByteString -> byte[]
@Override
- protected void copyToInternal(byte[] target, int sourceOffset,
+ 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.
@@ -152,9 +152,8 @@ class LiteralByteString extends ByteString {
}
@Override
- public String toString(String charsetName)
- throws UnsupportedEncodingException {
- return new String(bytes, getOffsetIntoBytes(), size(), charsetName);
+ protected String toStringInternal(Charset charset) {
+ return new String(bytes, getOffsetIntoBytes(), size(), charset);
}
// =================================================================
@@ -195,12 +194,12 @@ class LiteralByteString extends ByteString {
LiteralByteString otherAsLiteral = (LiteralByteString) other;
// If we know the hash codes and they are not equal, we know the byte
// strings are not equal.
- if (hash != 0
- && otherAsLiteral.hash != 0
+ if (hash != 0
+ && otherAsLiteral.hash != 0
&& hash != otherAsLiteral.hash) {
return false;
}
-
+
return equalsRange((LiteralByteString) other, 0, size());
} else if (other instanceof RopeByteString) {
return other.equals(this);
@@ -281,14 +280,14 @@ class LiteralByteString extends ByteString {
protected int partialHash(int h, int offset, int length) {
return hashCode(h, bytes, getOffsetIntoBytes() + offset, length);
}
-
+
static int hashCode(int h, byte[] bytes, int offset, int length) {
for (int i = offset; i < offset + length; i++) {
h = h * 31 + bytes[i];
}
return h;
}
-
+
static int hashCode(byte[] bytes) {
int h = hashCode(bytes.length, bytes, 0, bytes.length);
return h == 0 ? 1 : h;
diff --git a/java/src/main/java/com/google/protobuf/LongArrayList.java b/java/src/main/java/com/google/protobuf/LongArrayList.java
new file mode 100644
index 00000000..298617ff
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/LongArrayList.java
@@ -0,0 +1,242 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import com.google.protobuf.Internal.LongList;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.RandomAccess;
+
+/**
+ * An implementation of {@link LongList} on top of a primitive array.
+ *
+ * @author dweis@google.com (Daniel Weis)
+ */
+final class LongArrayList extends AbstractProtobufList<Long> implements LongList, RandomAccess {
+
+ private static final int DEFAULT_CAPACITY = 10;
+
+ private static final LongArrayList EMPTY_LIST = new LongArrayList();
+ static {
+ EMPTY_LIST.makeImmutable();
+ }
+
+ public static LongArrayList emptyList() {
+ return EMPTY_LIST;
+ }
+
+ /**
+ * The backing store for the list.
+ */
+ private long[] array;
+
+ /**
+ * The size of the list distinct from the length of the array. That is, it is the number of
+ * elements set in the list.
+ */
+ private int size;
+
+ /**
+ * Constructs a new mutable {@code LongArrayList}.
+ */
+ LongArrayList() {
+ array = new long[DEFAULT_CAPACITY];
+ size = 0;
+ }
+
+ /**
+ * Constructs a new mutable {@code LongArrayList} containing the same elements as {@code other}.
+ */
+ LongArrayList(List<Long> other) {
+ if (other instanceof LongArrayList) {
+ LongArrayList list = (LongArrayList) other;
+ array = list.array.clone();
+ size = list.size;
+ } else {
+ size = other.size();
+ array = new long[size];
+ for (int i = 0; i < size; i++) {
+ array[i] = other.get(i);
+ }
+ }
+ }
+
+ @Override
+ public Long get(int index) {
+ return getLong(index);
+ }
+
+ @Override
+ public long getLong(int index) {
+ ensureIndexInRange(index);
+ return array[index];
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+
+ @Override
+ public Long set(int index, Long element) {
+ return setLong(index, element);
+ }
+
+ @Override
+ public long setLong(int index, long element) {
+ ensureIsMutable();
+ ensureIndexInRange(index);
+ long previousValue = array[index];
+ array[index] = element;
+ return previousValue;
+ }
+
+ @Override
+ public void add(int index, Long element) {
+ addLong(index, element);
+ }
+
+ /**
+ * Like {@link #add(Long)} but more efficient in that it doesn't box the element.
+ */
+ @Override
+ public void addLong(long element) {
+ addLong(size, element);
+ }
+
+ /**
+ * Like {@link #add(int, Long)} but more efficient in that it doesn't box the element.
+ */
+ private void addLong(int index, long element) {
+ ensureIsMutable();
+ if (index < 0 || index > size) {
+ throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index));
+ }
+
+ if (size < array.length) {
+ // Shift everything over to make room
+ System.arraycopy(array, index, array, index + 1, size - index);
+ } else {
+ // Resize to 1.5x the size
+ int length = ((size * 3) / 2) + 1;
+ long[] newArray = new long[length];
+
+ // Copy the first part directly
+ System.arraycopy(array, 0, newArray, 0, index);
+
+ // Copy the rest shifted over by one to make room
+ System.arraycopy(array, index, newArray, index + 1, size - index);
+ array = newArray;
+ }
+
+ array[index] = element;
+ size++;
+ modCount++;
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends Long> collection) {
+ ensureIsMutable();
+
+ if (collection == null) {
+ throw new NullPointerException();
+ }
+
+ // We specialize when adding another LongArrayList to avoid boxing elements.
+ if (!(collection instanceof LongArrayList)) {
+ return super.addAll(collection);
+ }
+
+ LongArrayList list = (LongArrayList) collection;
+ if (list.size == 0) {
+ return false;
+ }
+
+ int overflow = Integer.MAX_VALUE - size;
+ if (overflow < list.size) {
+ // We can't actually represent a list this large.
+ throw new OutOfMemoryError();
+ }
+
+ int newSize = size + list.size;
+ if (newSize > array.length) {
+ array = Arrays.copyOf(array, newSize);
+ }
+
+ System.arraycopy(list.array, 0, array, size, list.size);
+ size = newSize;
+ modCount++;
+ return true;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ ensureIsMutable();
+ for (int i = 0; i < size; i++) {
+ if (o.equals(array[i])) {
+ System.arraycopy(array, i + 1, array, i, size - i);
+ size--;
+ modCount++;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Long remove(int index) {
+ ensureIsMutable();
+ ensureIndexInRange(index);
+ long value = array[index];
+ System.arraycopy(array, index + 1, array, index, size - index);
+ size--;
+ modCount++;
+ return value;
+ }
+
+ /**
+ * Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an
+ * {@link IndexOutOfBoundsException} if it is not.
+ *
+ * @param index the index to verify is in range
+ */
+ private void ensureIndexInRange(int index) {
+ if (index < 0 || index >= size) {
+ throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index));
+ }
+ }
+
+ private String makeOutOfBoundsExceptionMessage(int index) {
+ return "Index:" + index + ", Size:" + size;
+ }
+}
diff --git a/java/src/main/java/com/google/protobuf/MapField.java b/java/src/main/java/com/google/protobuf/MapField.java
index 82906d37..b290993c 100644
--- a/java/src/main/java/com/google/protobuf/MapField.java
+++ b/java/src/main/java/com/google/protobuf/MapField.java
@@ -30,9 +30,11 @@
package com.google.protobuf;
+import com.google.protobuf.MapFieldLite.MutatabilityAwareMap;
+
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -51,7 +53,7 @@ import java.util.Map;
* and getList() concurrently in multiple threads. If write-access is needed,
* all access must be synchronized.
*/
-public class MapField<K, V> {
+public class MapField<K, V> implements MutabilityOracle {
/**
* Indicates where the data of this map field is currently stored.
*
@@ -72,8 +74,9 @@ public class MapField<K, V> {
*/
private enum StorageMode {MAP, LIST, BOTH}
+ private volatile boolean isMutable;
private volatile StorageMode mode;
- private Map<K, V> mapData;
+ private MutatabilityAwareMap<K, V> mapData;
private List<Message> listData;
// Convert between a map entry Message and a key-value pair.
@@ -110,20 +113,19 @@ public class MapField<K, V> {
private MapField(
Converter<K, V> converter,
StorageMode mode,
- Map<K, V> mapData,
- List<Message> listData) {
+ Map<K, V> mapData) {
this.converter = converter;
+ this.isMutable = true;
this.mode = mode;
- this.mapData = mapData;
- this.listData = listData;
+ this.mapData = new MutatabilityAwareMap<K, V>(this, mapData);
+ this.listData = null;
}
private MapField(
MapEntry<K, V> defaultEntry,
StorageMode mode,
- Map<K, V> mapData,
- List<Message> listData) {
- this(new ImmutableMessageConverter<K, V>(defaultEntry), mode, mapData, listData);
+ Map<K, V> mapData) {
+ this(new ImmutableMessageConverter<K, V>(defaultEntry), mode, mapData);
}
@@ -131,14 +133,14 @@ public class MapField<K, V> {
public static <K, V> MapField<K, V> emptyMapField(
MapEntry<K, V> defaultEntry) {
return new MapField<K, V>(
- defaultEntry, StorageMode.MAP, Collections.<K, V>emptyMap(), null);
+ defaultEntry, StorageMode.MAP, Collections.<K, V>emptyMap());
}
/** Creates a new mutable empty MapField. */
public static <K, V> MapField<K, V> newMapField(MapEntry<K, V> defaultEntry) {
return new MapField<K, V>(
- defaultEntry, StorageMode.MAP, new HashMap<K, V>(), null);
+ defaultEntry, StorageMode.MAP, new LinkedHashMap<K, V>());
}
@@ -151,7 +153,7 @@ public class MapField<K, V> {
converter.convertMessageToKeyAndValue(message, map);
}
- private List<Message> convertMapToList(Map<K, V> mapData) {
+ private List<Message> convertMapToList(MutatabilityAwareMap<K, V> mapData) {
List<Message> listData = new ArrayList<Message>();
for (Map.Entry<K, V> entry : mapData.entrySet()) {
listData.add(
@@ -161,12 +163,12 @@ public class MapField<K, V> {
return listData;
}
- private Map<K, V> convertListToMap(List<Message> listData) {
- Map<K, V> mapData = new HashMap<K, V>();
+ private MutatabilityAwareMap<K, V> convertListToMap(List<Message> listData) {
+ Map<K, V> mapData = new LinkedHashMap<K, V>();
for (Message item : listData) {
convertMessageToKeyAndValue(item, mapData);
}
- return mapData;
+ return new MutatabilityAwareMap<K, V>(this, mapData);
}
/** Returns the content of this MapField as a read-only Map. */
@@ -199,7 +201,7 @@ public class MapField<K, V> {
}
public void clear() {
- mapData = new HashMap<K, V>();
+ mapData = new MutatabilityAwareMap<K, V>(this, new LinkedHashMap<K, V>());
mode = StorageMode.MAP;
}
@@ -221,7 +223,7 @@ public class MapField<K, V> {
/** Returns a deep copy of this MapField. */
public MapField<K, V> copy() {
return new MapField<K, V>(
- converter, StorageMode.MAP, MapFieldLite.copy(getMap()), null);
+ converter, StorageMode.MAP, MapFieldLite.copy(getMap()));
}
/** Gets the content of this MapField as a read-only List. */
@@ -256,4 +258,29 @@ public class MapField<K, V> {
Message getMapEntryMessageDefaultInstance() {
return converter.getMessageDefaultInstance();
}
+
+ /**
+ * Makes this list immutable. All subsequent modifications will throw an
+ * {@link UnsupportedOperationException}.
+ */
+ public void makeImmutable() {
+ isMutable = false;
+ }
+
+ /**
+ * Returns whether this field can be modified.
+ */
+ public boolean isMutable() {
+ return isMutable;
+ }
+
+ /* (non-Javadoc)
+ * @see com.google.protobuf.MutabilityOracle#ensureMutable()
+ */
+ @Override
+ public void ensureMutable() {
+ if (!isMutable()) {
+ throw new UnsupportedOperationException();
+ }
+ }
}
diff --git a/java/src/main/java/com/google/protobuf/MapFieldLite.java b/java/src/main/java/com/google/protobuf/MapFieldLite.java
index 7f94c690..c17fa7b1 100644
--- a/java/src/main/java/com/google/protobuf/MapFieldLite.java
+++ b/java/src/main/java/com/google/protobuf/MapFieldLite.java
@@ -31,9 +31,12 @@
package com.google.protobuf;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.Set;
/**
* Internal representation of map fields in generated lite-runtime messages.
@@ -41,16 +44,21 @@ import java.util.Map;
* This class is a protobuf implementation detail. Users shouldn't use this
* class directly.
*/
-public class MapFieldLite<K, V> {
- private Map<K, V> mapData;
+public class MapFieldLite<K, V> implements MutabilityOracle {
+ private MutatabilityAwareMap<K, V> mapData;
+ private boolean isMutable;
private MapFieldLite(Map<K, V> mapData) {
- this.mapData = mapData;
+ this.mapData = new MutatabilityAwareMap<K, V>(this, mapData);
+ this.isMutable = true;
}
@SuppressWarnings({"rawtypes", "unchecked"})
private static final MapFieldLite EMPTY_MAP_FIELD =
new MapFieldLite(Collections.emptyMap());
+ static {
+ EMPTY_MAP_FIELD.makeImmutable();
+ }
/** Returns an singleton immutable empty MapFieldLite instance. */
@SuppressWarnings({"unchecked", "cast"})
@@ -60,7 +68,7 @@ public class MapFieldLite<K, V> {
/** Creates a new MapFieldLite instance. */
public static <K, V> MapFieldLite<K, V> newMapField() {
- return new MapFieldLite<K, V>(new HashMap<K, V>());
+ return new MapFieldLite<K, V>(new LinkedHashMap<K, V>());
}
/** Gets the content of this MapField as a read-only Map. */
@@ -168,7 +176,7 @@ public class MapFieldLite<K, V> {
*/
@SuppressWarnings("unchecked")
static <K, V> Map<K, V> copy(Map<K, V> map) {
- Map<K, V> result = new HashMap<K, V>();
+ Map<K, V> result = new LinkedHashMap<K, V>();
for (Map.Entry<K, V> entry : map.entrySet()) {
result.put(entry.getKey(), (V) copy(entry.getValue()));
}
@@ -179,4 +187,360 @@ public class MapFieldLite<K, V> {
public MapFieldLite<K, V> copy() {
return new MapFieldLite<K, V>(copy(mapData));
}
+
+ /**
+ * Makes this field immutable. All subsequent modifications will throw an
+ * {@link UnsupportedOperationException}.
+ */
+ public void makeImmutable() {
+ isMutable = false;
+ }
+
+ /**
+ * Returns whether this field can be modified.
+ */
+ public boolean isMutable() {
+ return isMutable;
+ }
+
+ @Override
+ public void ensureMutable() {
+ if (!isMutable()) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * An internal map that checks for mutability before delegating.
+ */
+ static class MutatabilityAwareMap<K, V> implements Map<K, V> {
+ private final MutabilityOracle mutabilityOracle;
+ private final Map<K, V> delegate;
+
+ MutatabilityAwareMap(MutabilityOracle mutabilityOracle, Map<K, V> delegate) {
+ this.mutabilityOracle = mutabilityOracle;
+ this.delegate = delegate;
+ }
+
+ @Override
+ public int size() {
+ return delegate.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return delegate.isEmpty();
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return delegate.containsKey(key);
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ return delegate.containsValue(value);
+ }
+
+ @Override
+ public V get(Object key) {
+ return delegate.get(key);
+ }
+
+ @Override
+ public V put(K key, V value) {
+ mutabilityOracle.ensureMutable();
+ return delegate.put(key, value);
+ }
+
+ @Override
+ public V remove(Object key) {
+ mutabilityOracle.ensureMutable();
+ return delegate.remove(key);
+ }
+
+ @Override
+ public void putAll(Map<? extends K, ? extends V> m) {
+ mutabilityOracle.ensureMutable();
+ delegate.putAll(m);
+ }
+
+ @Override
+ public void clear() {
+ mutabilityOracle.ensureMutable();
+ delegate.clear();
+ }
+
+ @Override
+ public Set<K> keySet() {
+ return new MutatabilityAwareSet<K>(mutabilityOracle, delegate.keySet());
+ }
+
+ @Override
+ public Collection<V> values() {
+ return new MutatabilityAwareCollection<V>(mutabilityOracle, delegate.values());
+ }
+
+ @Override
+ public Set<java.util.Map.Entry<K, V>> entrySet() {
+ return new MutatabilityAwareSet<Entry<K, V>>(mutabilityOracle, delegate.entrySet());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return delegate.equals(o);
+ }
+
+ @Override
+ public int hashCode() {
+ return delegate.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return delegate.toString();
+ }
+ }
+
+ /**
+ * An internal collection that checks for mutability before delegating.
+ */
+ private static class MutatabilityAwareCollection<E> implements Collection<E> {
+ private final MutabilityOracle mutabilityOracle;
+ private final Collection<E> delegate;
+
+ MutatabilityAwareCollection(MutabilityOracle mutabilityOracle, Collection<E> delegate) {
+ this.mutabilityOracle = mutabilityOracle;
+ this.delegate = delegate;
+ }
+
+ @Override
+ public int size() {
+ return delegate.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return delegate.isEmpty();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return delegate.contains(o);
+ }
+
+ @Override
+ public Iterator<E> iterator() {
+ return new MutatabilityAwareIterator<E>(mutabilityOracle, delegate.iterator());
+ }
+
+ @Override
+ public Object[] toArray() {
+ return delegate.toArray();
+ }
+
+ @Override
+ public <T> T[] toArray(T[] a) {
+ return delegate.toArray(a);
+ }
+
+ @Override
+ public boolean add(E e) {
+ // Unsupported operation in the delegate.
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ mutabilityOracle.ensureMutable();
+ return delegate.remove(o);
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> c) {
+ return delegate.containsAll(c);
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends E> c) {
+ // Unsupported operation in the delegate.
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c) {
+ mutabilityOracle.ensureMutable();
+ return delegate.removeAll(c);
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c) {
+ mutabilityOracle.ensureMutable();
+ return delegate.retainAll(c);
+ }
+
+ @Override
+ public void clear() {
+ mutabilityOracle.ensureMutable();
+ delegate.clear();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return delegate.equals(o);
+ }
+
+ @Override
+ public int hashCode() {
+ return delegate.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return delegate.toString();
+ }
+ }
+
+ /**
+ * An internal set that checks for mutability before delegating.
+ */
+ private static class MutatabilityAwareSet<E> implements Set<E> {
+ private final MutabilityOracle mutabilityOracle;
+ private final Set<E> delegate;
+
+ MutatabilityAwareSet(MutabilityOracle mutabilityOracle, Set<E> delegate) {
+ this.mutabilityOracle = mutabilityOracle;
+ this.delegate = delegate;
+ }
+
+ @Override
+ public int size() {
+ return delegate.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return delegate.isEmpty();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return delegate.contains(o);
+ }
+
+ @Override
+ public Iterator<E> iterator() {
+ return new MutatabilityAwareIterator<E>(mutabilityOracle, delegate.iterator());
+ }
+
+ @Override
+ public Object[] toArray() {
+ return delegate.toArray();
+ }
+
+ @Override
+ public <T> T[] toArray(T[] a) {
+ return delegate.toArray(a);
+ }
+
+ @Override
+ public boolean add(E e) {
+ mutabilityOracle.ensureMutable();
+ return delegate.add(e);
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ mutabilityOracle.ensureMutable();
+ return delegate.remove(o);
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> c) {
+ return delegate.containsAll(c);
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends E> c) {
+ mutabilityOracle.ensureMutable();
+ return delegate.addAll(c);
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c) {
+ mutabilityOracle.ensureMutable();
+ return delegate.retainAll(c);
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c) {
+ mutabilityOracle.ensureMutable();
+ return delegate.removeAll(c);
+ }
+
+ @Override
+ public void clear() {
+ mutabilityOracle.ensureMutable();
+ delegate.clear();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return delegate.equals(o);
+ }
+
+ @Override
+ public int hashCode() {
+ return delegate.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return delegate.toString();
+ }
+ }
+
+ /**
+ * An internal iterator that checks for mutability before delegating.
+ */
+ private static class MutatabilityAwareIterator<E> implements Iterator<E> {
+ private final MutabilityOracle mutabilityOracle;
+ private final Iterator<E> delegate;
+
+ MutatabilityAwareIterator(MutabilityOracle mutabilityOracle, Iterator<E> delegate) {
+ this.mutabilityOracle = mutabilityOracle;
+ this.delegate = delegate;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return delegate.hasNext();
+ }
+
+ @Override
+ public E next() {
+ return delegate.next();
+ }
+
+ @Override
+ public void remove() {
+ mutabilityOracle.ensureMutable();
+ delegate.remove();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return delegate.equals(obj);
+ }
+
+ @Override
+ public int hashCode() {
+ return delegate.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return delegate.toString();
+ }
+ }
}
diff --git a/java/src/main/java/com/google/protobuf/Message.java b/java/src/main/java/com/google/protobuf/Message.java
index 5c75b58b..fa0265e2 100644
--- a/java/src/main/java/com/google/protobuf/Message.java
+++ b/java/src/main/java/com/google/protobuf/Message.java
@@ -163,7 +163,7 @@ public interface Message extends MessageLite, MessageOrBuilder {
* 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>.
+ * <code>UnsupportedOperationException</code>.
*/
Builder getFieldBuilder(Descriptors.FieldDescriptor field);
@@ -181,7 +181,7 @@ public interface Message extends MessageLite, MessageOrBuilder {
* 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>.
+ * <code>UnsupportedOperationException</code>.
*/
Builder getRepeatedFieldBuilder(Descriptors.FieldDescriptor field,
int index);
diff --git a/java/src/main/java/com/google/protobuf/MessageReflection.java b/java/src/main/java/com/google/protobuf/MessageReflection.java
index 06e3c99b..de4bfd3e 100644
--- a/java/src/main/java/com/google/protobuf/MessageReflection.java
+++ b/java/src/main/java/com/google/protobuf/MessageReflection.java
@@ -45,13 +45,14 @@ import java.util.TreeMap;
*/
class MessageReflection {
- static void writeMessageTo(Message message, CodedOutputStream output,
+ static void writeMessageTo(
+ Message message,
+ Map<FieldDescriptor, Object> fields,
+ CodedOutputStream output,
boolean alwaysWriteRequiredFields)
throws IOException {
final boolean isMessageSet =
message.getDescriptorForType().getOptions().getMessageSetWireFormat();
-
- Map<FieldDescriptor, Object> fields = message.getAllFields();
if (alwaysWriteRequiredFields) {
fields = new TreeMap<FieldDescriptor, Object>(fields);
for (final FieldDescriptor field :
@@ -82,13 +83,15 @@ class MessageReflection {
}
}
- static int getSerializedSize(Message message) {
+ static int getSerializedSize(
+ Message message,
+ Map<FieldDescriptor, Object> fields) {
int size = 0;
final boolean isMessageSet =
message.getDescriptorForType().getOptions().getMessageSetWireFormat();
for (final Map.Entry<Descriptors.FieldDescriptor, Object> entry :
- message.getAllFields().entrySet()) {
+ fields.entrySet()) {
final Descriptors.FieldDescriptor field = entry.getKey();
final Object value = entry.getValue();
if (isMessageSet && field.isExtension() &&
@@ -340,14 +343,12 @@ class MessageReflection {
ByteString bytes, ExtensionRegistryLite registry,
Descriptors.FieldDescriptor descriptor, Message defaultInstance)
throws IOException;
-
+
/**
- * Read a primitive field from input. Note that builders and mutable
- * messages may use different Java types to represent a primtive field.
+ * Returns the UTF8 validation level for the field.
*/
- Object readPrimitiveField(
- CodedInputStream input, WireFormat.FieldType type,
- boolean checkUtf8) throws IOException;
+ WireFormat.Utf8Validation getUtf8Validation(Descriptors.FieldDescriptor
+ descriptor);
/**
* Returns a new merge target for a sub-field. When defaultInstance is
@@ -513,11 +514,18 @@ class MessageReflection {
return new BuilderAdapter(builder.newBuilderForField(field));
}
}
-
- public Object readPrimitiveField(
- CodedInputStream input, WireFormat.FieldType type,
- boolean checkUtf8) throws IOException {
- return FieldSet.readPrimitiveField(input, type, checkUtf8);
+
+ public WireFormat.Utf8Validation
+ getUtf8Validation(Descriptors.FieldDescriptor descriptor) {
+ if (descriptor.needsUtf8Check()) {
+ return WireFormat.Utf8Validation.STRICT;
+ }
+ // TODO(liujisi): support lazy strings for repeated fields.
+ if (!descriptor.isRepeated()
+ && builder instanceof GeneratedMessage.Builder) {
+ return WireFormat.Utf8Validation.LAZY;
+ }
+ return WireFormat.Utf8Validation.LOOSE;
}
public Object finish() {
@@ -651,11 +659,14 @@ class MessageReflection {
throw new UnsupportedOperationException(
"newMergeTargetForField() called on FieldSet object");
}
-
- public Object readPrimitiveField(
- CodedInputStream input, WireFormat.FieldType type,
- boolean checkUtf8) throws IOException {
- return FieldSet.readPrimitiveField(input, type, checkUtf8);
+
+ public WireFormat.Utf8Validation
+ getUtf8Validation(Descriptors.FieldDescriptor descriptor) {
+ if (descriptor.needsUtf8Check()) {
+ return WireFormat.Utf8Validation.STRICT;
+ }
+ // TODO(liujisi): support lazy strings for ExtesnsionSet.
+ return WireFormat.Utf8Validation.LOOSE;
}
public Object finish() {
@@ -767,8 +778,8 @@ class MessageReflection {
}
} else {
while (input.getBytesUntilLimit() > 0) {
- final Object value =
- target.readPrimitiveField(input, field.getLiteType(), field.needsUtf8Check());
+ final Object value = WireFormat.readPrimitiveField(
+ input, field.getLiteType(), target.getUtf8Validation(field));
target.addRepeatedField(field, value);
}
}
@@ -801,7 +812,8 @@ class MessageReflection {
}
break;
default:
- value = target.readPrimitiveField(input, field.getLiteType(), field.needsUtf8Check());
+ value = WireFormat.readPrimitiveField(
+ input, field.getLiteType(), target.getUtf8Validation(field));
break;
}
diff --git a/java/src/main/java/com/google/protobuf/MutabilityOracle.java b/java/src/main/java/com/google/protobuf/MutabilityOracle.java
new file mode 100644
index 00000000..82b723c9
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/MutabilityOracle.java
@@ -0,0 +1,48 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+/**
+ * Verifies that an object is mutable, throwing if not.
+ */
+interface MutabilityOracle {
+ static final MutabilityOracle IMMUTABLE = new MutabilityOracle() {
+ @Override
+ public void ensureMutable() {
+ throw new UnsupportedOperationException();
+ }
+ };
+
+ /**
+ * Throws an {@link UnsupportedOperationException} if not mutable.
+ */
+ void ensureMutable();
+}
diff --git a/java/src/main/java/com/google/protobuf/Parser.java b/java/src/main/java/com/google/protobuf/Parser.java
index f636014b..227c02b7 100644
--- a/java/src/main/java/com/google/protobuf/Parser.java
+++ b/java/src/main/java/com/google/protobuf/Parser.java
@@ -227,8 +227,8 @@ public interface Parser<MessageType> {
* {@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
+ * @return Parsed message if successful, or null 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)
diff --git a/java/src/main/java/com/google/protobuf/ProtobufArrayList.java b/java/src/main/java/com/google/protobuf/ProtobufArrayList.java
new file mode 100644
index 00000000..759368c9
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/ProtobufArrayList.java
@@ -0,0 +1,95 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import com.google.protobuf.Internal.ProtobufList;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implements {@link ProtobufList} for non-primitive and {@link String} types.
+ */
+class ProtobufArrayList<E> extends AbstractProtobufList<E> {
+
+ private static final ProtobufArrayList<Object> EMPTY_LIST = new ProtobufArrayList<Object>();
+ static {
+ EMPTY_LIST.makeImmutable();
+ }
+
+ @SuppressWarnings("unchecked") // Guaranteed safe by runtime.
+ public static <E> ProtobufArrayList<E> emptyList() {
+ return (ProtobufArrayList<E>) EMPTY_LIST;
+ }
+
+ private final List<E> list;
+
+ ProtobufArrayList() {
+ list = new ArrayList<E>();
+ }
+
+ ProtobufArrayList(List<E> toCopy) {
+ list = new ArrayList<E>(toCopy);
+ }
+
+ @Override
+ public void add(int index, E element) {
+ ensureIsMutable();
+ list.add(index, element);
+ modCount++;
+ }
+
+ @Override
+ public E get(int index) {
+ return list.get(index);
+ }
+
+ @Override
+ public E remove(int index) {
+ ensureIsMutable();
+ E toReturn = list.remove(index);
+ modCount++;
+ return toReturn;
+ }
+
+ @Override
+ public E set(int index, E element) {
+ ensureIsMutable();
+ E toReturn = list.set(index, element);
+ modCount++;
+ return toReturn;
+ }
+
+ @Override
+ public int size() {
+ return list.size();
+ }
+}
diff --git a/java/src/main/java/com/google/protobuf/RopeByteString.java b/java/src/main/java/com/google/protobuf/RopeByteString.java
index fa23c9dd..4f3eb127 100644
--- a/java/src/main/java/com/google/protobuf/RopeByteString.java
+++ b/java/src/main/java/com/google/protobuf/RopeByteString.java
@@ -30,14 +30,14 @@
package com.google.protobuf;
-import java.io.InvalidObjectException;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-import java.io.ByteArrayInputStream;
import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
@@ -418,9 +418,8 @@ class RopeByteString extends ByteString {
}
@Override
- public String toString(String charsetName)
- throws UnsupportedEncodingException {
- return new String(toByteArray(), charsetName);
+ protected String toStringInternal(Charset charset) {
+ return new String(toByteArray(), charset);
}
// =================================================================
diff --git a/java/src/main/java/com/google/protobuf/TextFormat.java b/java/src/main/java/com/google/protobuf/TextFormat.java
index 4f6756ed..a79ce559 100644
--- a/java/src/main/java/com/google/protobuf/TextFormat.java
+++ b/java/src/main/java/com/google/protobuf/TextFormat.java
@@ -409,9 +409,9 @@ public final class TextFormat {
case STRING:
generator.print("\"");
- generator.print(escapeNonAscii ?
- escapeText((String) value) :
- escapeDoubleQuotesAndBackslashes((String) value)
+ generator.print(escapeNonAscii
+ ? escapeText((String) value)
+ : escapeDoubleQuotesAndBackslashes((String) value)
.replace("\n", "\\n"));
generator.print("\"");
break;
@@ -730,8 +730,8 @@ public final class TextFormat {
}
final char c = currentToken.charAt(0);
- return ('0' <= c && c <= '9') ||
- c == '-' || c == '+';
+ return ('0' <= c && c <= '9')
+ || c == '-' || c == '+';
}
/**
@@ -749,10 +749,10 @@ public final class TextFormat {
public String consumeIdentifier() throws ParseException {
for (int i = 0; i < currentToken.length(); i++) {
final char c = currentToken.charAt(i);
- if (('a' <= c && c <= 'z') ||
- ('A' <= c && c <= 'Z') ||
- ('0' <= c && c <= '9') ||
- (c == '_') || (c == '.')) {
+ if (('a' <= c && c <= 'z')
+ || ('A' <= c && c <= 'Z')
+ || ('0' <= c && c <= '9')
+ || (c == '_') || (c == '.')) {
// OK
} else {
throw parseException(
@@ -941,14 +941,14 @@ public final class TextFormat {
* Otherwise, throw a {@link ParseException}.
*/
public boolean consumeBoolean() throws ParseException {
- if (currentToken.equals("true") ||
- currentToken.equals("t") ||
- currentToken.equals("1")) {
+ if (currentToken.equals("true")
+ || currentToken.equals("t")
+ || currentToken.equals("1")) {
nextToken();
return true;
- } else if (currentToken.equals("false") ||
- currentToken.equals("f") ||
- currentToken.equals("0")) {
+ } else if (currentToken.equals("false")
+ || currentToken.equals("f")
+ || currentToken.equals("0")) {
nextToken();
return false;
} else {
@@ -999,14 +999,15 @@ public final class TextFormat {
*/
private void consumeByteString(List<ByteString> list)
throws ParseException {
- final char quote = currentToken.length() > 0 ? currentToken.charAt(0)
- : '\0';
+ final char quote = currentToken.length() > 0
+ ? currentToken.charAt(0)
+ : '\0';
if (quote != '\"' && quote != '\'') {
throw parseException("Expected string.");
}
- if (currentToken.length() < 2 ||
- currentToken.charAt(currentToken.length() - 1) != quote) {
+ if (currentToken.length() < 2
+ || currentToken.charAt(currentToken.length() - 1) != quote) {
throw parseException("String missing ending quote.");
}
@@ -1340,8 +1341,8 @@ public final class TextFormat {
} else {
if (extension.descriptor.getContainingType() != type) {
throw tokenizer.parseExceptionPreviousToken(
- "Extension \"" + name + "\" does not extend message type \"" +
- type.getFullName() + "\".");
+ "Extension \"" + name + "\" does not extend message type \""
+ + type.getFullName() + "\".");
}
field = extension.descriptor;
}
@@ -1365,20 +1366,20 @@ public final class TextFormat {
}
}
// Again, special-case group names as described above.
- if (field != null && field.getType() == FieldDescriptor.Type.GROUP &&
- !field.getMessageType().getName().equals(name)) {
+ if (field != null && field.getType() == FieldDescriptor.Type.GROUP
+ && !field.getMessageType().getName().equals(name)) {
field = null;
}
if (field == null) {
if (!allowUnknownFields) {
throw tokenizer.parseExceptionPreviousToken(
- "Message type \"" + type.getFullName() +
- "\" has no field named \"" + name + "\".");
+ "Message type \"" + type.getFullName()
+ + "\" has no field named \"" + name + "\".");
} else {
logger.warning(
- "Message type \"" + type.getFullName() +
- "\" has no field named \"" + name + "\".");
+ "Message type \"" + type.getFullName()
+ + "\" has no field named \"" + name + "\".");
}
}
}
@@ -1391,8 +1392,9 @@ public final class TextFormat {
// start with "{" or "<" which indicates the beginning of a message body.
// If there is no ":" or there is a "{" or "<" after ":", this field has
// to be a message or the input is ill-formed.
- if (tokenizer.tryConsume(":") && !tokenizer.lookingAt("{") &&
- !tokenizer.lookingAt("<")) {
+ if (tokenizer.tryConsume(":")
+ && !tokenizer.lookingAt("{")
+ && !tokenizer.lookingAt("<")) {
skipFieldValue(tokenizer);
} else {
skipFieldMessage(tokenizer);
@@ -1516,16 +1518,16 @@ public final class TextFormat {
value = enumType.findValueByNumber(number);
if (value == null) {
throw tokenizer.parseExceptionPreviousToken(
- "Enum type \"" + enumType.getFullName() +
- "\" has no value with number " + number + '.');
+ "Enum type \"" + enumType.getFullName()
+ + "\" has no value with number " + number + '.');
}
} else {
final String id = tokenizer.consumeIdentifier();
value = enumType.findValueByName(id);
if (value == null) {
throw tokenizer.parseExceptionPreviousToken(
- "Enum type \"" + enumType.getFullName() +
- "\" has no value named \"" + id + "\".");
+ "Enum type \"" + enumType.getFullName()
+ + "\" has no value named \"" + id + "\".");
}
}
@@ -1578,8 +1580,9 @@ public final class TextFormat {
// start with "{" or "<" which indicates the beginning of a message body.
// If there is no ":" or there is a "{" or "<" after ":", this field has
// to be a message or the input is ill-formed.
- if (tokenizer.tryConsume(":") && !tokenizer.lookingAt("<") &&
- !tokenizer.lookingAt("{")) {
+ if (tokenizer.tryConsume(":")
+ && !tokenizer.lookingAt("<")
+ && !tokenizer.lookingAt("{")) {
skipFieldValue(tokenizer);
} else {
skipFieldMessage(tokenizer);
@@ -1617,11 +1620,11 @@ public final class TextFormat {
while (tokenizer.tryConsumeString()) {}
return;
}
- if (!tokenizer.tryConsumeIdentifier() && // includes enum & boolean
- !tokenizer.tryConsumeInt64() && // includes int32
- !tokenizer.tryConsumeUInt64() && // includes uint32
- !tokenizer.tryConsumeDouble() &&
- !tokenizer.tryConsumeFloat()) {
+ if (!tokenizer.tryConsumeIdentifier() // includes enum & boolean
+ && !tokenizer.tryConsumeInt64() // includes int32
+ && !tokenizer.tryConsumeUInt64() // includes uint32
+ && !tokenizer.tryConsumeDouble()
+ && !tokenizer.tryConsumeFloat()) {
throw tokenizer.parseException(
"Invalid field value: " + tokenizer.currentToken);
}
@@ -1647,19 +1650,19 @@ public final class TextFormat {
* which no defined short-hand escape sequence is defined will be escaped
* using 3-digit octal sequences.
*/
- private static String escapeBytes(final ByteSequence input) {
+ public static String escapeBytes(final ByteSequence input) {
final StringBuilder builder = new StringBuilder(input.size());
for (int i = 0; i < input.size(); i++) {
final byte b = input.byteAt(i);
switch (b) {
// Java does not recognize \a or \v, apparently.
- case 0x07: builder.append("\\a" ); break;
- case '\b': builder.append("\\b" ); break;
- case '\f': builder.append("\\f" ); break;
- case '\n': builder.append("\\n" ); break;
- case '\r': builder.append("\\r" ); break;
- case '\t': builder.append("\\t" ); break;
- case 0x0b: builder.append("\\v" ); break;
+ case 0x07: builder.append("\\a"); break;
+ case '\b': builder.append("\\b"); break;
+ case '\f': builder.append("\\f"); break;
+ case '\n': builder.append("\\n"); break;
+ case '\r': builder.append("\\r"); break;
+ case '\t': builder.append("\\t"); break;
+ case 0x0b: builder.append("\\v"); break;
case '\\': builder.append("\\\\"); break;
case '\'': builder.append("\\\'"); break;
case '"' : builder.append("\\\""); break;
@@ -1688,11 +1691,13 @@ public final class TextFormat {
* which no defined short-hand escape sequence is defined will be escaped
* using 3-digit octal sequences.
*/
- static String escapeBytes(final ByteString input) {
+ public static String escapeBytes(final ByteString input) {
return escapeBytes(new ByteSequence() {
+ @Override
public int size() {
return input.size();
}
+ @Override
public byte byteAt(int offset) {
return input.byteAt(offset);
}
@@ -1702,11 +1707,13 @@ public final class TextFormat {
/**
* Like {@link #escapeBytes(ByteString)}, but used for byte array.
*/
- static String escapeBytes(final byte[] input) {
+ public static String escapeBytes(final byte[] input) {
return escapeBytes(new ByteSequence() {
+ @Override
public int size() {
return input.length;
}
+ @Override
public byte byteAt(int offset) {
return input[offset];
}
@@ -1718,7 +1725,7 @@ public final class TextFormat {
* {@link #escapeBytes(ByteString)}. Two-digit hex escapes (starting with
* "\x") are also recognized.
*/
- static ByteString unescapeBytes(final CharSequence charString)
+ public static ByteString unescapeBytes(final CharSequence charString)
throws InvalidEscapeSequenceException {
// First convert the Java character sequence to UTF-8 bytes.
ByteString input = ByteString.copyFromUtf8(charString.toString());
@@ -1749,7 +1756,7 @@ public final class TextFormat {
code = code * 8 + digitValue(input.byteAt(i));
}
// TODO: Check that 0 <= code && code <= 0xFF.
- result[pos++] = (byte)code;
+ result[pos++] = (byte) code;
} else {
switch (c) {
case 'a' : result[pos++] = 0x07; break;
@@ -1777,12 +1784,12 @@ public final class TextFormat {
++i;
code = code * 16 + digitValue(input.byteAt(i));
}
- result[pos++] = (byte)code;
+ result[pos++] = (byte) code;
break;
default:
throw new InvalidEscapeSequenceException(
- "Invalid escape sequence: '\\" + (char)c + '\'');
+ "Invalid escape sequence: '\\" + (char) c + '\'');
}
}
} else {
@@ -1801,7 +1808,7 @@ public final class TextFormat {
* Thrown by {@link TextFormat#unescapeBytes} and
* {@link TextFormat#unescapeText} when an invalid escape sequence is seen.
*/
- static class InvalidEscapeSequenceException extends IOException {
+ public static class InvalidEscapeSequenceException extends IOException {
private static final long serialVersionUID = -8164033650142593304L;
InvalidEscapeSequenceException(final String description) {
@@ -1841,9 +1848,9 @@ public final class TextFormat {
/** Is this a hex digit? */
private static boolean isHex(final byte c) {
- return ('0' <= c && c <= '9') ||
- ('a' <= c && c <= 'f') ||
- ('A' <= c && c <= 'F');
+ return ('0' <= c && c <= '9')
+ || ('a' <= c && c <= 'f')
+ || ('A' <= c && c <= 'F');
}
/**
diff --git a/java/src/main/java/com/google/protobuf/Utf8.java b/java/src/main/java/com/google/protobuf/Utf8.java
index 4d0ef53b..4271b41b 100644
--- a/java/src/main/java/com/google/protobuf/Utf8.java
+++ b/java/src/main/java/com/google/protobuf/Utf8.java
@@ -46,7 +46,7 @@ package com.google.protobuf;
* <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"))
+ * Arrays.equals(bytes, new String(bytes, Internal.UTF_8).getBytes(Internal.UTF_8))
* }</pre>
*
* <p>See the Unicode Standard,</br>
diff --git a/java/src/main/java/com/google/protobuf/WireFormat.java b/java/src/main/java/com/google/protobuf/WireFormat.java
index eba2528e..ba83b666 100644
--- a/java/src/main/java/com/google/protobuf/WireFormat.java
+++ b/java/src/main/java/com/google/protobuf/WireFormat.java
@@ -30,6 +30,8 @@
package com.google.protobuf;
+import java.io.IOException;
+
/**
* This class is used internally by the Protocol Buffer library and generated
* message implementations. It is public only because those generated messages
@@ -160,4 +162,84 @@ public final class WireFormat {
makeTag(MESSAGE_SET_TYPE_ID, WIRETYPE_VARINT);
static final int MESSAGE_SET_MESSAGE_TAG =
makeTag(MESSAGE_SET_MESSAGE, WIRETYPE_LENGTH_DELIMITED);
+
+ /**
+ * Validation level for handling incoming string field data which potentially
+ * contain non-UTF8 bytes.
+ */
+ enum Utf8Validation {
+ /** Eagerly parses to String; silently accepts invalid UTF8 bytes. */
+ LOOSE {
+ Object readString(CodedInputStream input) throws IOException {
+ return input.readString();
+ }
+ },
+ /** Eagerly parses to String; throws an IOException on invalid bytes. */
+ STRICT {
+ Object readString(CodedInputStream input) throws IOException {
+ return input.readStringRequireUtf8();
+ }
+ },
+ /** Keep data as ByteString; validation/conversion to String is lazy. */
+ LAZY {
+ Object readString(CodedInputStream input) throws IOException {
+ return input.readBytes();
+ }
+ };
+
+ /** Read a string field from the input with the proper UTF8 validation. */
+ abstract Object readString(CodedInputStream input) throws IOException;
+ }
+
+ /**
+ * Read a field of any primitive type for immutable messages 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.
+ * @param utf8Validation Different string UTF8 validation level for handling
+ * string fields.
+ * @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.
+ */
+ static Object readPrimitiveField(
+ CodedInputStream input,
+ FieldType type,
+ Utf8Validation utf8Validation) 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 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 STRING : return utf8Validation.readString(input);
+ 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.");
+ }
}
diff --git a/java/src/test/java/com/google/protobuf/BooleanArrayListTest.java b/java/src/test/java/com/google/protobuf/BooleanArrayListTest.java
new file mode 100644
index 00000000..df89c263
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/BooleanArrayListTest.java
@@ -0,0 +1,473 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import static java.util.Arrays.asList;
+
+import junit.framework.TestCase;
+
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+
+/**
+ * Tests for {@link BooleanArrayList}.
+ *
+ * @author dweis@google.com (Daniel Weis)
+ */
+public class BooleanArrayListTest extends TestCase {
+
+ private static final BooleanArrayList UNARY_LIST = newImmutableBooleanArrayList(true);
+ private static final BooleanArrayList TERTIARY_LIST =
+ newImmutableBooleanArrayList(true, true, false);
+
+ private BooleanArrayList list;
+
+ @Override
+ protected void setUp() throws Exception {
+ list = new BooleanArrayList();
+ }
+
+ public void testEmptyListReturnsSameInstance() {
+ assertSame(BooleanArrayList.emptyList(), BooleanArrayList.emptyList());
+ }
+
+ public void testEmptyListIsImmutable() {
+ assertImmutable(BooleanArrayList.emptyList());
+ }
+
+ public void testMakeImmutable() {
+ list.addBoolean(true);
+ list.addBoolean(false);
+ list.addBoolean(true);
+ list.addBoolean(true);
+ list.makeImmutable();
+ assertImmutable(list);
+ }
+
+ public void testCopyConstructor() {
+ BooleanArrayList copy = new BooleanArrayList(TERTIARY_LIST);
+ assertEquals(TERTIARY_LIST, copy);
+
+ copy = new BooleanArrayList(BooleanArrayList.emptyList());
+ assertEquals(BooleanArrayList.emptyList(), copy);
+
+ copy = new BooleanArrayList(asList(false, false, true));
+ assertEquals(asList(false, false, true), copy);
+
+ copy = new BooleanArrayList(Collections.<Boolean>emptyList());
+ assertEquals(BooleanArrayList.emptyList(), copy);
+ }
+
+ public void testModificationWithIteration() {
+ list.addAll(asList(true, false, false, true));
+ Iterator<Boolean> iterator = list.iterator();
+ assertEquals(4, list.size());
+ assertEquals(true, (boolean) list.get(0));
+ assertEquals(true, (boolean) iterator.next());
+ list.set(0, true);
+ assertEquals(false, (boolean) iterator.next());
+
+ list.remove(0);
+ try {
+ iterator.next();
+ fail();
+ } catch (ConcurrentModificationException e) {
+ // expected
+ }
+
+ iterator = list.iterator();
+ list.add(0, false);
+ try {
+ iterator.next();
+ fail();
+ } catch (ConcurrentModificationException e) {
+ // expected
+ }
+ }
+
+ public void testGet() {
+ assertEquals(true, (boolean) TERTIARY_LIST.get(0));
+ assertEquals(true, (boolean) TERTIARY_LIST.get(1));
+ assertEquals(false, (boolean) TERTIARY_LIST.get(2));
+
+ try {
+ TERTIARY_LIST.get(-1);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ TERTIARY_LIST.get(3);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testGetInt() {
+ assertEquals(true, TERTIARY_LIST.getBoolean(0));
+ assertEquals(true, TERTIARY_LIST.getBoolean(1));
+ assertEquals(false, TERTIARY_LIST.getBoolean(2));
+
+ try {
+ TERTIARY_LIST.get(-1);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ TERTIARY_LIST.get(3);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testSize() {
+ assertEquals(0, BooleanArrayList.emptyList().size());
+ assertEquals(1, UNARY_LIST.size());
+ assertEquals(3, TERTIARY_LIST.size());
+
+ list.addBoolean(true);
+ list.addBoolean(false);
+ list.addBoolean(false);
+ list.addBoolean(false);
+ assertEquals(4, list.size());
+
+ list.remove(0);
+ assertEquals(3, list.size());
+
+ list.add(true);
+ assertEquals(4, list.size());
+ }
+
+ public void testSet() {
+ list.addBoolean(false);
+ list.addBoolean(false);
+
+ assertEquals(false, (boolean) list.set(0, true));
+ assertEquals(true, list.getBoolean(0));
+
+ assertEquals(false, (boolean) list.set(1, false));
+ assertEquals(false, list.getBoolean(1));
+
+ try {
+ list.set(-1, true);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ list.set(2, false);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testSetInt() {
+ list.addBoolean(true);
+ list.addBoolean(true);
+
+ assertEquals(true, list.setBoolean(0, false));
+ assertEquals(false, list.getBoolean(0));
+
+ assertEquals(true, list.setBoolean(1, false));
+ assertEquals(false, list.getBoolean(1));
+
+ try {
+ list.setBoolean(-1, false);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ list.setBoolean(2, true);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testAdd() {
+ assertEquals(0, list.size());
+
+ assertTrue(list.add(true));
+ assertEquals(asList(true), list);
+
+ assertTrue(list.add(false));
+ list.add(0, false);
+ assertEquals(asList(false, true, false), list);
+
+ list.add(0, false);
+ list.add(0, true);
+ // Force a resize by getting up to 11 elements.
+ for (int i = 0; i < 6; i++) {
+ list.add(true);
+ }
+ assertEquals(asList(true, false, false, true, false, true, true, true, true, true, true), list);
+
+ try {
+ list.add(-1, false);
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ list.add(4, true);
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testAddInt() {
+ assertEquals(0, list.size());
+
+ list.addBoolean(true);
+ assertEquals(asList(true), list);
+
+ list.addBoolean(false);
+ assertEquals(asList(true, false), list);
+ }
+
+ public void testAddAll() {
+ assertEquals(0, list.size());
+
+ assertTrue(list.addAll(Collections.singleton(false)));
+ assertEquals(1, list.size());
+ assertEquals(false, (boolean) list.get(0));
+ assertEquals(false, list.getBoolean(0));
+
+ assertTrue(list.addAll(asList(true, false, false, false, true)));
+ assertEquals(asList(false, true, false, false, false, true), list);
+
+ assertTrue(list.addAll(TERTIARY_LIST));
+ assertEquals(asList(false, true, false, false, false, true, true, true, false), list);
+
+ assertFalse(list.addAll(Collections.<Boolean>emptyList()));
+ assertFalse(list.addAll(BooleanArrayList.emptyList()));
+ }
+
+ public void testRemove() {
+ list.addAll(TERTIARY_LIST);
+ assertEquals(true, (boolean) list.remove(0));
+ assertEquals(asList(true, false), list);
+
+ assertTrue(list.remove(Boolean.TRUE));
+ assertEquals(asList(false), list);
+
+ assertFalse(list.remove(Boolean.TRUE));
+ assertEquals(asList(false), list);
+
+ assertEquals(false, (boolean) list.remove(0));
+ assertEquals(asList(), list);
+
+ try {
+ list.remove(-1);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ list.remove(0);
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ private void assertImmutable(BooleanArrayList list) {
+ if (list.contains(1)) {
+ throw new RuntimeException("Cannot test the immutability of lists that contain 1.");
+ }
+
+ try {
+ list.add(false);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.add(0, true);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(Collections.<Boolean>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(Collections.singletonList(false));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(new BooleanArrayList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(0, Collections.singleton(true));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(0, UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(0, Collections.<Boolean>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addBoolean(true);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.clear();
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.remove(1);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.remove(new Object());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.removeAll(Collections.<Boolean>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.removeAll(Collections.singleton(1));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.removeAll(UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.retainAll(Collections.<Boolean>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.retainAll(Collections.singleton(1));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.retainAll(UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.set(0, true);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.setBoolean(0, false);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ }
+
+ private static BooleanArrayList newImmutableBooleanArrayList(boolean... elements) {
+ BooleanArrayList list = new BooleanArrayList();
+ for (boolean element : elements) {
+ list.addBoolean(element);
+ }
+ list.makeImmutable();
+ return list;
+ }
+}
diff --git a/java/src/test/java/com/google/protobuf/BoundedByteStringTest.java b/java/src/test/java/com/google/protobuf/BoundedByteStringTest.java
index 6c9596ca..1562a1a6 100644
--- a/java/src/test/java/com/google/protobuf/BoundedByteStringTest.java
+++ b/java/src/test/java/com/google/protobuf/BoundedByteStringTest.java
@@ -62,7 +62,7 @@ public class BoundedByteStringTest extends LiteralByteStringTest {
@Override
public void testToString() throws UnsupportedEncodingException {
String testString = "I love unicode \u1234\u5678 characters";
- LiteralByteString unicode = new LiteralByteString(testString.getBytes(UTF_8));
+ LiteralByteString unicode = new LiteralByteString(testString.getBytes(Internal.UTF_8));
ByteString chopped = unicode.substring(2, unicode.size() - 6);
assertEquals(classUnderTest + ".substring() must have the expected type",
classUnderTest, getActualClassName(chopped));
@@ -72,6 +72,19 @@ public class BoundedByteStringTest extends LiteralByteStringTest {
testString.substring(2, testString.length() - 6), roundTripString);
}
+ @Override
+ public void testCharsetToString() throws UnsupportedEncodingException {
+ String testString = "I love unicode \u1234\u5678 characters";
+ LiteralByteString unicode = new LiteralByteString(testString.getBytes(Internal.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(Internal.UTF_8);
+ assertEquals(classUnderTest + " unicode bytes must match",
+ testString.substring(2, testString.length() - 6), roundTripString);
+ }
+
public void testJavaSerialization() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(out);
diff --git a/java/src/test/java/com/google/protobuf/ByteStringTest.java b/java/src/test/java/com/google/protobuf/ByteStringTest.java
index 15de17cd..8af5dcde 100644
--- a/java/src/test/java/com/google/protobuf/ByteStringTest.java
+++ b/java/src/test/java/com/google/protobuf/ByteStringTest.java
@@ -41,6 +41,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
@@ -56,7 +57,7 @@ import java.util.Random;
*/
public class ByteStringTest extends TestCase {
- private static final String UTF_16 = "UTF-16";
+ private static final Charset UTF_16 = Charset.forName("UTF-16");
static byte[] getTestBytes(int size, long seed) {
Random random = new Random(seed);
@@ -139,7 +140,7 @@ public class ByteStringTest extends TestCase {
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");
+ byte[] testBytes = testString.getBytes(Internal.UTF_8);
assertTrue("copyFromUtf8 string must respect the charset",
isArrayRange(byteString.toByteArray(), testBytes, 0, testBytes.length));
}
@@ -380,7 +381,7 @@ public class ByteStringTest extends TestCase {
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;
@@ -400,7 +401,7 @@ public class ByteStringTest extends TestCase {
public void testToStringUtf8() throws UnsupportedEncodingException {
String testString = "I love unicode \u1234\u5678 characters";
- byte[] testBytes = testString.getBytes("UTF-8");
+ byte[] testBytes = testString.getBytes(Internal.UTF_8);
ByteString byteString = ByteString.copyFrom(testBytes);
assertEquals("copyToStringUtf8 must respect the charset",
testString, byteString.toStringUtf8());
@@ -492,13 +493,13 @@ public class ByteStringTest extends TestCase {
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});
@@ -705,14 +706,14 @@ public class ByteStringTest extends TestCase {
}
return pieces;
}
-
+
private byte[] substringUsingWriteTo(
ByteString data, int offset, int length) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
data.writeTo(output, offset, length);
return output.toByteArray();
}
-
+
public void testWriteToOutputStream() throws Exception {
// Choose a size large enough so when two ByteStrings are concatenated they
// won't be merged into one byte array due to some optimizations.
@@ -727,7 +728,7 @@ public class ByteStringTest extends TestCase {
byte[] result = substringUsingWriteTo(left, 1, 1);
assertEquals(1, result.length);
assertEquals((byte) 11, result[0]);
-
+
byte[] data2 = new byte[dataSize];
for (int i = 0; i < data1.length; i++) {
data2[i] = (byte) 2;
diff --git a/java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java b/java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java
index 0785cc91..365789c0 100644
--- a/java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java
+++ b/java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java
@@ -321,7 +321,7 @@ public class CodedOutputStreamTest extends TestCase {
final int BUFFER_SIZE = 4 * 1024;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(BUFFER_SIZE);
CodedOutputStream codedStream = CodedOutputStream.newInstance(outputStream);
- byte[] value = "abcde".getBytes("UTF-8");
+ byte[] value = "abcde".getBytes(Internal.UTF_8);
for (int i = 0; i < 1024; ++i) {
codedStream.writeRawBytes(value, 0, value.length);
}
@@ -330,7 +330,7 @@ public class CodedOutputStreamTest extends TestCase {
assertTrue(codedStream.getTotalBytesWritten() > BUFFER_SIZE);
assertEquals(value.length * 1024, codedStream.getTotalBytesWritten());
}
-
+
public void testWriteToByteBuffer() throws Exception {
final int bufferSize = 16 * 1024;
ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
@@ -351,7 +351,7 @@ public class CodedOutputStreamTest extends TestCase {
codedStream.writeRawByte((byte) 3);
}
codedStream.flush();
-
+
// Check that data is correctly written to the ByteBuffer.
assertEquals(0, buffer.remaining());
buffer.flip();
@@ -365,9 +365,9 @@ public class CodedOutputStreamTest extends TestCase {
assertEquals((byte) 3, buffer.get());
}
}
-
+
public void testWriteByteBuffer() throws Exception {
- byte[] value = "abcde".getBytes("UTF-8");
+ byte[] value = "abcde".getBytes(Internal.UTF_8);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CodedOutputStream codedStream = CodedOutputStream.newInstance(outputStream);
ByteBuffer byteBuffer = ByteBuffer.wrap(value, 0, 1);
@@ -377,10 +377,10 @@ public class CodedOutputStreamTest extends TestCase {
// The above call shouldn't affect the ByteBuffer's state.
assertEquals(0, byteBuffer.position());
assertEquals(1, byteBuffer.limit());
-
+
// The correct way to write part of an array using ByteBuffer.
codedStream.writeRawBytes(ByteBuffer.wrap(value, 2, 1).slice());
-
+
codedStream.flush();
byte[] result = outputStream.toByteArray();
assertEquals(6, result.length);
diff --git a/java/src/test/java/com/google/protobuf/DescriptorsTest.java b/java/src/test/java/com/google/protobuf/DescriptorsTest.java
index 05832a12..edd7fc46 100644
--- a/java/src/test/java/com/google/protobuf/DescriptorsTest.java
+++ b/java/src/test/java/com/google/protobuf/DescriptorsTest.java
@@ -35,32 +35,29 @@ 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.OneofDescriptor;
+import com.google.protobuf.Descriptors.DescriptorValidationException;
import com.google.protobuf.Descriptors.EnumDescriptor;
import com.google.protobuf.Descriptors.EnumValueDescriptor;
-import com.google.protobuf.Descriptors.ServiceDescriptor;
+import com.google.protobuf.Descriptors.FieldDescriptor;
+import com.google.protobuf.Descriptors.FileDescriptor;
import com.google.protobuf.Descriptors.MethodDescriptor;
-
+import com.google.protobuf.Descriptors.OneofDescriptor;
+import com.google.protobuf.Descriptors.ServiceDescriptor;
import com.google.protobuf.test.UnittestImport;
import com.google.protobuf.test.UnittestImport.ImportEnum;
-import com.google.protobuf.test.UnittestImport.ImportMessage;
+import protobuf_unittest.TestCustomOptions;
+import protobuf_unittest.UnittestCustomOptions;
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.TestAllTypes;
import protobuf_unittest.UnittestProto.TestExtremeDefaultValues;
import protobuf_unittest.UnittestProto.TestMultipleExtensionRanges;
import protobuf_unittest.UnittestProto.TestRequired;
+import protobuf_unittest.UnittestProto.TestReservedFields;
import protobuf_unittest.UnittestProto.TestService;
-import protobuf_unittest.UnittestCustomOptions;
-
-import protobuf_unittest.TestCustomOptions;
-
import junit.framework.TestCase;
@@ -286,7 +283,7 @@ public class DescriptorsTest extends TestCase {
d = TestExtremeDefaultValues.getDescriptor();
assertEquals(
ByteString.copyFrom(
- "\0\001\007\b\f\n\r\t\013\\\'\"\u00fe".getBytes("ISO-8859-1")),
+ "\0\001\007\b\f\n\r\t\013\\\'\"\u00fe".getBytes(Internal.ISO_8859_1)),
d.findFieldByName("escaped_bytes").getDefaultValue());
assertEquals(-1, d.findFieldByName("large_uint32").getDefaultValue());
assertEquals(-1L, d.findFieldByName("large_uint64").getDefaultValue());
@@ -497,7 +494,7 @@ public class DescriptorsTest extends TestCase {
.build();
// translate and crosslink
FileDescriptor file =
- Descriptors.FileDescriptor.buildFrom(fileDescriptorProto,
+ Descriptors.FileDescriptor.buildFrom(fileDescriptorProto,
new FileDescriptor[0]);
// verify resulting descriptors
assertNotNull(file);
@@ -518,7 +515,7 @@ public class DescriptorsTest extends TestCase {
}
assertTrue(barFound);
}
-
+
public void testDependencyOrder() throws Exception {
FileDescriptorProto fooProto = FileDescriptorProto.newBuilder()
.setName("foo.proto").build();
@@ -537,14 +534,14 @@ public class DescriptorsTest extends TestCase {
new FileDescriptor[0]);
FileDescriptor barFile = Descriptors.FileDescriptor.buildFrom(barProto,
new FileDescriptor[] {fooFile});
-
- // Items in the FileDescriptor array can be in any order.
+
+ // Items in the FileDescriptor array can be in any order.
Descriptors.FileDescriptor.buildFrom(bazProto,
new FileDescriptor[] {fooFile, barFile});
Descriptors.FileDescriptor.buildFrom(bazProto,
new FileDescriptor[] {barFile, fooFile});
}
-
+
public void testInvalidPublicDependency() throws Exception {
FileDescriptorProto fooProto = FileDescriptorProto.newBuilder()
.setName("foo.proto").build();
@@ -628,7 +625,7 @@ public class DescriptorsTest extends TestCase {
Descriptors.FileDescriptor.buildFrom(
fooProto, new FileDescriptor[] {forwardFile});
}
-
+
/**
* Tests the translate/crosslink for an example with a more complex namespace
* referencing.
@@ -677,7 +674,7 @@ public class DescriptorsTest extends TestCase {
assertTrue(field.getEnumType().getFile().getName().equals("bar.proto"));
assertTrue(field.getEnumType().getFile().getPackage().equals(
"a.b.c.d.bar.shared"));
- }
+ }
}
public void testOneofDescriptor() throws Exception {
@@ -691,6 +688,9 @@ public class DescriptorsTest extends TestCase {
assertEquals(4, oneofDescriptor.getFieldCount());
assertSame(oneofDescriptor.getField(1), field);
+
+ assertEquals(4, oneofDescriptor.getFields().size());
+ assertEquals(oneofDescriptor.getFields().get(1), field);
}
public void testMessageDescriptorExtensions() throws Exception {
@@ -706,6 +706,19 @@ public class DescriptorsTest extends TestCase {
assertTrue(TestMultipleExtensionRanges.getDescriptor().isExtensionNumber(4143));
}
+ public void testReservedFields() {
+ Descriptor d = TestReservedFields.getDescriptor();
+ assertTrue(d.isReservedNumber(2));
+ assertFalse(d.isReservedNumber(8));
+ assertTrue(d.isReservedNumber(9));
+ assertTrue(d.isReservedNumber(10));
+ assertTrue(d.isReservedNumber(11));
+ assertFalse(d.isReservedNumber(12));
+ assertFalse(d.isReservedName("foo"));
+ assertTrue(d.isReservedName("bar"));
+ assertTrue(d.isReservedName("baz"));
+ }
+
public void testToString() {
assertEquals("protobuf_unittest.TestAllTypes.optional_uint64",
UnittestProto.TestAllTypes.getDescriptor().findFieldByNumber(
diff --git a/java/src/test/java/com/google/protobuf/DoubleArrayListTest.java b/java/src/test/java/com/google/protobuf/DoubleArrayListTest.java
new file mode 100644
index 00000000..e7a73d70
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/DoubleArrayListTest.java
@@ -0,0 +1,473 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import static java.util.Arrays.asList;
+
+import junit.framework.TestCase;
+
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+
+/**
+ * Tests for {@link DoubleArrayList}.
+ *
+ * @author dweis@google.com (Daniel Weis)
+ */
+public class DoubleArrayListTest extends TestCase {
+
+ private static final DoubleArrayList UNARY_LIST = newImmutableDoubleArrayList(1);
+ private static final DoubleArrayList TERTIARY_LIST =
+ newImmutableDoubleArrayList(1, 2, 3);
+
+ private DoubleArrayList list;
+
+ @Override
+ protected void setUp() throws Exception {
+ list = new DoubleArrayList();
+ }
+
+ public void testEmptyListReturnsSameInstance() {
+ assertSame(DoubleArrayList.emptyList(), DoubleArrayList.emptyList());
+ }
+
+ public void testEmptyListIsImmutable() {
+ assertImmutable(DoubleArrayList.emptyList());
+ }
+
+ public void testMakeImmutable() {
+ list.addDouble(2);
+ list.addDouble(4);
+ list.addDouble(6);
+ list.addDouble(8);
+ list.makeImmutable();
+ assertImmutable(list);
+ }
+
+ public void testCopyConstructor() {
+ DoubleArrayList copy = new DoubleArrayList(TERTIARY_LIST);
+ assertEquals(TERTIARY_LIST, copy);
+
+ copy = new DoubleArrayList(DoubleArrayList.emptyList());
+ assertEquals(DoubleArrayList.emptyList(), copy);
+
+ copy = new DoubleArrayList(asList(1D, 2D, 3D));
+ assertEquals(asList(1D, 2D, 3D), copy);
+
+ copy = new DoubleArrayList(Collections.<Double>emptyList());
+ assertEquals(DoubleArrayList.emptyList(), copy);
+ }
+
+ public void testModificationWithIteration() {
+ list.addAll(asList(1D, 2D, 3D, 4D));
+ Iterator<Double> iterator = list.iterator();
+ assertEquals(4, list.size());
+ assertEquals(1D, (double) list.get(0));
+ assertEquals(1D, (double) iterator.next());
+ list.set(0, 1D);
+ assertEquals(2D, (double) iterator.next());
+
+ list.remove(0);
+ try {
+ iterator.next();
+ fail();
+ } catch (ConcurrentModificationException e) {
+ // expected
+ }
+
+ iterator = list.iterator();
+ list.add(0, 0D);
+ try {
+ iterator.next();
+ fail();
+ } catch (ConcurrentModificationException e) {
+ // expected
+ }
+ }
+
+ public void testGet() {
+ assertEquals(1D, (double) TERTIARY_LIST.get(0));
+ assertEquals(2D, (double) TERTIARY_LIST.get(1));
+ assertEquals(3D, (double) TERTIARY_LIST.get(2));
+
+ try {
+ TERTIARY_LIST.get(-1);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ TERTIARY_LIST.get(3);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testGetInt() {
+ assertEquals(1D, TERTIARY_LIST.getDouble(0));
+ assertEquals(2D, TERTIARY_LIST.getDouble(1));
+ assertEquals(3D, TERTIARY_LIST.getDouble(2));
+
+ try {
+ TERTIARY_LIST.get(-1);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ TERTIARY_LIST.get(3);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testSize() {
+ assertEquals(0, DoubleArrayList.emptyList().size());
+ assertEquals(1, UNARY_LIST.size());
+ assertEquals(3, TERTIARY_LIST.size());
+
+ list.addDouble(2);
+ list.addDouble(4);
+ list.addDouble(6);
+ list.addDouble(8);
+ assertEquals(4, list.size());
+
+ list.remove(0);
+ assertEquals(3, list.size());
+
+ list.add(16D);
+ assertEquals(4, list.size());
+ }
+
+ public void testSet() {
+ list.addDouble(2);
+ list.addDouble(4);
+
+ assertEquals(2D, (double) list.set(0, 0D));
+ assertEquals(0D, list.getDouble(0));
+
+ assertEquals(4D, (double) list.set(1, 0D));
+ assertEquals(0D, list.getDouble(1));
+
+ try {
+ list.set(-1, 0D);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ list.set(2, 0D);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testSetInt() {
+ list.addDouble(2);
+ list.addDouble(4);
+
+ assertEquals(2D, list.setDouble(0, 0));
+ assertEquals(0D, list.getDouble(0));
+
+ assertEquals(4D, list.setDouble(1, 0));
+ assertEquals(0D, list.getDouble(1));
+
+ try {
+ list.setDouble(-1, 0);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ list.setDouble(2, 0);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testAdd() {
+ assertEquals(0, list.size());
+
+ assertTrue(list.add(2D));
+ assertEquals(asList(2D), list);
+
+ assertTrue(list.add(3D));
+ list.add(0, 4D);
+ assertEquals(asList(4D, 2D, 3D), list);
+
+ list.add(0, 1D);
+ list.add(0, 0D);
+ // Force a resize by getting up to 11 elements.
+ for (int i = 0; i < 6; i++) {
+ list.add(Double.valueOf(5 + i));
+ }
+ assertEquals(asList(0D, 1D, 4D, 2D, 3D, 5D, 6D, 7D, 8D, 9D, 10D), list);
+
+ try {
+ list.add(-1, 5D);
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ list.add(4, 5D);
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testAddInt() {
+ assertEquals(0, list.size());
+
+ list.addDouble(2);
+ assertEquals(asList(2D), list);
+
+ list.addDouble(3);
+ assertEquals(asList(2D, 3D), list);
+ }
+
+ public void testAddAll() {
+ assertEquals(0, list.size());
+
+ assertTrue(list.addAll(Collections.singleton(1D)));
+ assertEquals(1, list.size());
+ assertEquals(1D, (double) list.get(0));
+ assertEquals(1D, list.getDouble(0));
+
+ assertTrue(list.addAll(asList(2D, 3D, 4D, 5D, 6D)));
+ assertEquals(asList(1D, 2D, 3D, 4D, 5D, 6D), list);
+
+ assertTrue(list.addAll(TERTIARY_LIST));
+ assertEquals(asList(1D, 2D, 3D, 4D, 5D, 6D, 1D, 2D, 3D), list);
+
+ assertFalse(list.addAll(Collections.<Double>emptyList()));
+ assertFalse(list.addAll(DoubleArrayList.emptyList()));
+ }
+
+ public void testRemove() {
+ list.addAll(TERTIARY_LIST);
+ assertEquals(1D, (double) list.remove(0));
+ assertEquals(asList(2D, 3D), list);
+
+ assertTrue(list.remove(Double.valueOf(3)));
+ assertEquals(asList(2D), list);
+
+ assertFalse(list.remove(Double.valueOf(3)));
+ assertEquals(asList(2D), list);
+
+ assertEquals(2D, (double) list.remove(0));
+ assertEquals(asList(), list);
+
+ try {
+ list.remove(-1);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ list.remove(0);
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ private void assertImmutable(DoubleArrayList list) {
+ if (list.contains(1)) {
+ throw new RuntimeException("Cannot test the immutability of lists that contain 1.");
+ }
+
+ try {
+ list.add(1D);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.add(0, 1D);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(Collections.<Double>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(Collections.singletonList(1D));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(new DoubleArrayList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(0, Collections.singleton(1D));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(0, UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(0, Collections.<Double>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addDouble(0);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.clear();
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.remove(1);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.remove(new Object());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.removeAll(Collections.<Double>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.removeAll(Collections.singleton(1));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.removeAll(UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.retainAll(Collections.<Double>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.retainAll(Collections.singleton(1));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.retainAll(UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.set(0, 0D);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.setDouble(0, 0);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ }
+
+ private static DoubleArrayList newImmutableDoubleArrayList(double... elements) {
+ DoubleArrayList list = new DoubleArrayList();
+ for (double element : elements) {
+ list.addDouble(element);
+ }
+ list.makeImmutable();
+ return list;
+ }
+}
diff --git a/java/src/test/java/com/google/protobuf/FloatArrayListTest.java b/java/src/test/java/com/google/protobuf/FloatArrayListTest.java
new file mode 100644
index 00000000..8f3e93dc
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/FloatArrayListTest.java
@@ -0,0 +1,473 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import static java.util.Arrays.asList;
+
+import junit.framework.TestCase;
+
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+
+/**
+ * Tests for {@link FloatArrayList}.
+ *
+ * @author dweis@google.com (Daniel Weis)
+ */
+public class FloatArrayListTest extends TestCase {
+
+ private static final FloatArrayList UNARY_LIST = newImmutableFloatArrayList(1);
+ private static final FloatArrayList TERTIARY_LIST =
+ newImmutableFloatArrayList(1, 2, 3);
+
+ private FloatArrayList list;
+
+ @Override
+ protected void setUp() throws Exception {
+ list = new FloatArrayList();
+ }
+
+ public void testEmptyListReturnsSameInstance() {
+ assertSame(FloatArrayList.emptyList(), FloatArrayList.emptyList());
+ }
+
+ public void testEmptyListIsImmutable() {
+ assertImmutable(FloatArrayList.emptyList());
+ }
+
+ public void testMakeImmutable() {
+ list.addFloat(2);
+ list.addFloat(4);
+ list.addFloat(6);
+ list.addFloat(8);
+ list.makeImmutable();
+ assertImmutable(list);
+ }
+
+ public void testCopyConstructor() {
+ FloatArrayList copy = new FloatArrayList(TERTIARY_LIST);
+ assertEquals(TERTIARY_LIST, copy);
+
+ copy = new FloatArrayList(FloatArrayList.emptyList());
+ assertEquals(FloatArrayList.emptyList(), copy);
+
+ copy = new FloatArrayList(asList(1F, 2F, 3F));
+ assertEquals(asList(1F, 2F, 3F), copy);
+
+ copy = new FloatArrayList(Collections.<Float>emptyList());
+ assertEquals(FloatArrayList.emptyList(), copy);
+ }
+
+ public void testModificationWithIteration() {
+ list.addAll(asList(1F, 2F, 3F, 4F));
+ Iterator<Float> iterator = list.iterator();
+ assertEquals(4, list.size());
+ assertEquals(1F, (float) list.get(0));
+ assertEquals(1F, (float) iterator.next());
+ list.set(0, 1F);
+ assertEquals(2F, (float) iterator.next());
+
+ list.remove(0);
+ try {
+ iterator.next();
+ fail();
+ } catch (ConcurrentModificationException e) {
+ // expected
+ }
+
+ iterator = list.iterator();
+ list.add(0, 0F);
+ try {
+ iterator.next();
+ fail();
+ } catch (ConcurrentModificationException e) {
+ // expected
+ }
+ }
+
+ public void testGet() {
+ assertEquals(1F, (float) TERTIARY_LIST.get(0));
+ assertEquals(2F, (float) TERTIARY_LIST.get(1));
+ assertEquals(3F, (float) TERTIARY_LIST.get(2));
+
+ try {
+ TERTIARY_LIST.get(-1);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ TERTIARY_LIST.get(3);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testGetFloat() {
+ assertEquals(1F, TERTIARY_LIST.getFloat(0));
+ assertEquals(2F, TERTIARY_LIST.getFloat(1));
+ assertEquals(3F, TERTIARY_LIST.getFloat(2));
+
+ try {
+ TERTIARY_LIST.get(-1);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ TERTIARY_LIST.get(3);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testSize() {
+ assertEquals(0, FloatArrayList.emptyList().size());
+ assertEquals(1, UNARY_LIST.size());
+ assertEquals(3, TERTIARY_LIST.size());
+
+ list.addFloat(2);
+ list.addFloat(4);
+ list.addFloat(6);
+ list.addFloat(8);
+ assertEquals(4, list.size());
+
+ list.remove(0);
+ assertEquals(3, list.size());
+
+ list.add(16F);
+ assertEquals(4, list.size());
+ }
+
+ public void testSet() {
+ list.addFloat(2);
+ list.addFloat(4);
+
+ assertEquals(2F, (float) list.set(0, 0F));
+ assertEquals(0F, list.getFloat(0));
+
+ assertEquals(4F, (float) list.set(1, 0F));
+ assertEquals(0F, list.getFloat(1));
+
+ try {
+ list.set(-1, 0F);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ list.set(2, 0F);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testSetFloat() {
+ list.addFloat(2);
+ list.addFloat(4);
+
+ assertEquals(2F, list.setFloat(0, 0));
+ assertEquals(0F, list.getFloat(0));
+
+ assertEquals(4F, list.setFloat(1, 0));
+ assertEquals(0F, list.getFloat(1));
+
+ try {
+ list.setFloat(-1, 0);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ list.setFloat(2, 0);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testAdd() {
+ assertEquals(0, list.size());
+
+ assertTrue(list.add(2F));
+ assertEquals(asList(2F), list);
+
+ assertTrue(list.add(3F));
+ list.add(0, 4F);
+ assertEquals(asList(4F, 2F, 3F), list);
+
+ list.add(0, 1F);
+ list.add(0, 0F);
+ // Force a resize by getting up to 11 elements.
+ for (int i = 0; i < 6; i++) {
+ list.add(Float.valueOf(5 + i));
+ }
+ assertEquals(asList(0F, 1F, 4F, 2F, 3F, 5F, 6F, 7F, 8F, 9F, 10F), list);
+
+ try {
+ list.add(-1, 5F);
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ list.add(4, 5F);
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testAddFloat() {
+ assertEquals(0, list.size());
+
+ list.addFloat(2);
+ assertEquals(asList(2F), list);
+
+ list.addFloat(3);
+ assertEquals(asList(2F, 3F), list);
+ }
+
+ public void testAddAll() {
+ assertEquals(0, list.size());
+
+ assertTrue(list.addAll(Collections.singleton(1F)));
+ assertEquals(1, list.size());
+ assertEquals(1F, (float) list.get(0));
+ assertEquals(1F, list.getFloat(0));
+
+ assertTrue(list.addAll(asList(2F, 3F, 4F, 5F, 6F)));
+ assertEquals(asList(1F, 2F, 3F, 4F, 5F, 6F), list);
+
+ assertTrue(list.addAll(TERTIARY_LIST));
+ assertEquals(asList(1F, 2F, 3F, 4F, 5F, 6F, 1F, 2F, 3F), list);
+
+ assertFalse(list.addAll(Collections.<Float>emptyList()));
+ assertFalse(list.addAll(FloatArrayList.emptyList()));
+ }
+
+ public void testRemove() {
+ list.addAll(TERTIARY_LIST);
+ assertEquals(1F, (float) list.remove(0));
+ assertEquals(asList(2F, 3F), list);
+
+ assertTrue(list.remove(Float.valueOf(3)));
+ assertEquals(asList(2F), list);
+
+ assertFalse(list.remove(Float.valueOf(3)));
+ assertEquals(asList(2F), list);
+
+ assertEquals(2F, (float) list.remove(0));
+ assertEquals(asList(), list);
+
+ try {
+ list.remove(-1);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ list.remove(0);
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ private void assertImmutable(FloatArrayList list) {
+ if (list.contains(1)) {
+ throw new RuntimeException("Cannot test the immutability of lists that contain 1.");
+ }
+
+ try {
+ list.add(1F);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.add(0, 1F);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(Collections.<Float>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(Collections.singletonList(1F));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(new FloatArrayList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(0, Collections.singleton(1F));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(0, UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(0, Collections.<Float>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addFloat(0);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.clear();
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.remove(1);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.remove(new Object());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.removeAll(Collections.<Float>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.removeAll(Collections.singleton(1));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.removeAll(UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.retainAll(Collections.<Float>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.retainAll(Collections.singleton(1));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.retainAll(UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.set(0, 0F);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.setFloat(0, 0);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ }
+
+ private static FloatArrayList newImmutableFloatArrayList(int... elements) {
+ FloatArrayList list = new FloatArrayList();
+ for (int element : elements) {
+ list.addFloat(element);
+ }
+ list.makeImmutable();
+ return list;
+ }
+}
diff --git a/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java b/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java
index 41ed7bd0..2bd8d1a9 100644
--- a/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java
+++ b/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java
@@ -56,6 +56,7 @@ import protobuf_unittest.UnittestProto;
import protobuf_unittest.UnittestProto.ForeignEnum;
import protobuf_unittest.UnittestProto.ForeignMessage;
import protobuf_unittest.UnittestProto.ForeignMessageOrBuilder;
+import protobuf_unittest.UnittestProto.NestedTestAllTypes;
import protobuf_unittest.UnittestProto.TestAllExtensions;
import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage;
@@ -1510,6 +1511,17 @@ public class GeneratedMessageTest extends TestCase {
}
}
+ public void testOneofNestedBuilderOnChangePropagation() {
+ NestedTestAllTypes.Builder parentBuilder = NestedTestAllTypes.newBuilder();
+ TestAllTypes.Builder builder = parentBuilder.getPayloadBuilder();
+ builder.getOneofNestedMessageBuilder();
+ assertTrue(builder.hasOneofNestedMessage());
+ assertTrue(parentBuilder.hasPayload());
+ NestedTestAllTypes message = parentBuilder.build();
+ assertTrue(message.hasPayload());
+ assertTrue(message.getPayload().hasOneofNestedMessage());
+ }
+
public void testGetRepeatedFieldBuilder() {
Descriptor descriptor = TestAllTypes.getDescriptor();
diff --git a/java/src/test/java/com/google/protobuf/IntArrayListTest.java b/java/src/test/java/com/google/protobuf/IntArrayListTest.java
new file mode 100644
index 00000000..3733eb30
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/IntArrayListTest.java
@@ -0,0 +1,473 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import static java.util.Arrays.asList;
+
+import junit.framework.TestCase;
+
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+
+/**
+ * Tests for {@link IntArrayList}.
+ *
+ * @author dweis@google.com (Daniel Weis)
+ */
+public class IntArrayListTest extends TestCase {
+
+ private static final IntArrayList UNARY_LIST = newImmutableIntArrayList(1);
+ private static final IntArrayList TERTIARY_LIST =
+ newImmutableIntArrayList(1, 2, 3);
+
+ private IntArrayList list;
+
+ @Override
+ protected void setUp() throws Exception {
+ list = new IntArrayList();
+ }
+
+ public void testEmptyListReturnsSameInstance() {
+ assertSame(IntArrayList.emptyList(), IntArrayList.emptyList());
+ }
+
+ public void testEmptyListIsImmutable() {
+ assertImmutable(IntArrayList.emptyList());
+ }
+
+ public void testMakeImmutable() {
+ list.addInt(2);
+ list.addInt(4);
+ list.addInt(6);
+ list.addInt(8);
+ list.makeImmutable();
+ assertImmutable(list);
+ }
+
+ public void testCopyConstructor() {
+ IntArrayList copy = new IntArrayList(TERTIARY_LIST);
+ assertEquals(TERTIARY_LIST, copy);
+
+ copy = new IntArrayList(IntArrayList.emptyList());
+ assertEquals(IntArrayList.emptyList(), copy);
+
+ copy = new IntArrayList(asList(1, 2, 3));
+ assertEquals(asList(1, 2, 3), copy);
+
+ copy = new IntArrayList(Collections.<Integer>emptyList());
+ assertEquals(IntArrayList.emptyList(), copy);
+ }
+
+ public void testModificationWithIteration() {
+ list.addAll(asList(1, 2, 3, 4));
+ Iterator<Integer> iterator = list.iterator();
+ assertEquals(4, list.size());
+ assertEquals(1, (int) list.get(0));
+ assertEquals(1, (int) iterator.next());
+ list.set(0, 1);
+ assertEquals(2, (int) iterator.next());
+
+ list.remove(0);
+ try {
+ iterator.next();
+ fail();
+ } catch (ConcurrentModificationException e) {
+ // expected
+ }
+
+ iterator = list.iterator();
+ list.add(0, 0);
+ try {
+ iterator.next();
+ fail();
+ } catch (ConcurrentModificationException e) {
+ // expected
+ }
+ }
+
+ public void testGet() {
+ assertEquals(1, (int) TERTIARY_LIST.get(0));
+ assertEquals(2, (int) TERTIARY_LIST.get(1));
+ assertEquals(3, (int) TERTIARY_LIST.get(2));
+
+ try {
+ TERTIARY_LIST.get(-1);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ TERTIARY_LIST.get(3);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testGetInt() {
+ assertEquals(1, TERTIARY_LIST.getInt(0));
+ assertEquals(2, TERTIARY_LIST.getInt(1));
+ assertEquals(3, TERTIARY_LIST.getInt(2));
+
+ try {
+ TERTIARY_LIST.get(-1);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ TERTIARY_LIST.get(3);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testSize() {
+ assertEquals(0, IntArrayList.emptyList().size());
+ assertEquals(1, UNARY_LIST.size());
+ assertEquals(3, TERTIARY_LIST.size());
+
+ list.addInt(2);
+ list.addInt(4);
+ list.addInt(6);
+ list.addInt(8);
+ assertEquals(4, list.size());
+
+ list.remove(0);
+ assertEquals(3, list.size());
+
+ list.add(16);
+ assertEquals(4, list.size());
+ }
+
+ public void testSet() {
+ list.addInt(2);
+ list.addInt(4);
+
+ assertEquals(2, (int) list.set(0, 0));
+ assertEquals(0, list.getInt(0));
+
+ assertEquals(4, (int) list.set(1, 0));
+ assertEquals(0, list.getInt(1));
+
+ try {
+ list.set(-1, 0);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ list.set(2, 0);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testSetInt() {
+ list.addInt(2);
+ list.addInt(4);
+
+ assertEquals(2, list.setInt(0, 0));
+ assertEquals(0, list.getInt(0));
+
+ assertEquals(4, list.setInt(1, 0));
+ assertEquals(0, list.getInt(1));
+
+ try {
+ list.setInt(-1, 0);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ list.setInt(2, 0);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testAdd() {
+ assertEquals(0, list.size());
+
+ assertTrue(list.add(2));
+ assertEquals(asList(2), list);
+
+ assertTrue(list.add(3));
+ list.add(0, 4);
+ assertEquals(asList(4, 2, 3), list);
+
+ list.add(0, 1);
+ list.add(0, 0);
+ // Force a resize by getting up to 11 elements.
+ for (int i = 0; i < 6; i++) {
+ list.add(5 + i);
+ }
+ assertEquals(asList(0, 1, 4, 2, 3, 5, 6, 7, 8, 9, 10), list);
+
+ try {
+ list.add(-1, 5);
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ list.add(4, 5);
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testAddInt() {
+ assertEquals(0, list.size());
+
+ list.addInt(2);
+ assertEquals(asList(2), list);
+
+ list.addInt(3);
+ assertEquals(asList(2, 3), list);
+ }
+
+ public void testAddAll() {
+ assertEquals(0, list.size());
+
+ assertTrue(list.addAll(Collections.singleton(1)));
+ assertEquals(1, list.size());
+ assertEquals(1, (int) list.get(0));
+ assertEquals(1, list.getInt(0));
+
+ assertTrue(list.addAll(asList(2, 3, 4, 5, 6)));
+ assertEquals(asList(1, 2, 3, 4, 5, 6), list);
+
+ assertTrue(list.addAll(TERTIARY_LIST));
+ assertEquals(asList(1, 2, 3, 4, 5, 6, 1, 2, 3), list);
+
+ assertFalse(list.addAll(Collections.<Integer>emptyList()));
+ assertFalse(list.addAll(IntArrayList.emptyList()));
+ }
+
+ public void testRemove() {
+ list.addAll(TERTIARY_LIST);
+ assertEquals(1, (int) list.remove(0));
+ assertEquals(asList(2, 3), list);
+
+ assertTrue(list.remove(Integer.valueOf(3)));
+ assertEquals(asList(2), list);
+
+ assertFalse(list.remove(Integer.valueOf(3)));
+ assertEquals(asList(2), list);
+
+ assertEquals(2, (int) list.remove(0));
+ assertEquals(asList(), list);
+
+ try {
+ list.remove(-1);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ list.remove(0);
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ private void assertImmutable(IntArrayList list) {
+ if (list.contains(1)) {
+ throw new RuntimeException("Cannot test the immutability of lists that contain 1.");
+ }
+
+ try {
+ list.add(1);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.add(0, 1);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(Collections.<Integer>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(Collections.singletonList(1));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(new IntArrayList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(0, Collections.singleton(1));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(0, UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(0, Collections.<Integer>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addInt(0);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.clear();
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.remove(1);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.remove(new Object());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.removeAll(Collections.<Integer>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.removeAll(Collections.singleton(1));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.removeAll(UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.retainAll(Collections.<Integer>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.retainAll(Collections.singleton(1));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.retainAll(UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.set(0, 0);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.setInt(0, 0);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ }
+
+ private static IntArrayList newImmutableIntArrayList(int... elements) {
+ IntArrayList list = new IntArrayList();
+ for (int element : elements) {
+ list.addInt(element);
+ }
+ list.makeImmutable();
+ return list;
+ }
+}
diff --git a/java/src/test/java/com/google/protobuf/IsValidUtf8Test.java b/java/src/test/java/com/google/protobuf/IsValidUtf8Test.java
index 89b64519..8751baae 100644
--- a/java/src/test/java/com/google/protobuf/IsValidUtf8Test.java
+++ b/java/src/test/java/com/google/protobuf/IsValidUtf8Test.java
@@ -72,8 +72,11 @@ public class IsValidUtf8Test extends TestCase {
* 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);
+ // Travis' OOM killer doesn't like this test
+ if (System.getenv("TRAVIS") == null) {
+ IsValidUtf8TestUtil.testBytes(3,
+ IsValidUtf8TestUtil.EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT);
+ }
}
/**
diff --git a/java/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java b/java/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java
index f41595ec..321669f3 100644
--- a/java/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java
+++ b/java/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java
@@ -33,18 +33,17 @@ package com.google.protobuf;
import static junit.framework.Assert.*;
import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
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
@@ -220,8 +219,8 @@ class IsValidUtf8TestUtil {
}
ByteString bs = ByteString.copyFrom(bytes);
boolean isRoundTrippable = bs.isValidUtf8();
- String s = new String(bytes, "UTF-8");
- byte[] bytesReencoded = s.getBytes("UTF-8");
+ String s = new String(bytes, Internal.UTF_8);
+ byte[] bytesReencoded = s.getBytes(Internal.UTF_8);
boolean bytesEqual = Arrays.equals(bytes, bytesReencoded);
if (bytesEqual != isRoundTrippable) {
@@ -313,10 +312,10 @@ class IsValidUtf8TestUtil {
void testBytesUsingByteBuffers(
int numBytes, long expectedCount, long start, long lim)
throws UnsupportedEncodingException {
- CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder()
+ CharsetDecoder decoder = Internal.UTF_8.newDecoder()
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE);
- CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder()
+ CharsetEncoder encoder = Internal.UTF_8.newEncoder()
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE);
byte[] bytes = new byte[numBytes];
diff --git a/java/src/test/java/com/google/protobuf/LazyFieldLiteTest.java b/java/src/test/java/com/google/protobuf/LazyFieldLiteTest.java
index e67c6d27..211b5697 100644
--- a/java/src/test/java/com/google/protobuf/LazyFieldLiteTest.java
+++ b/java/src/test/java/com/google/protobuf/LazyFieldLiteTest.java
@@ -30,6 +30,9 @@
package com.google.protobuf;
+import static protobuf_unittest.UnittestProto.optionalInt32Extension;
+import static protobuf_unittest.UnittestProto.optionalInt64Extension;
+
import protobuf_unittest.UnittestProto.TestAllExtensions;
import protobuf_unittest.UnittestProto.TestAllTypes;
@@ -111,12 +114,146 @@ public class LazyFieldLiteTest extends TestCase {
assertNotEqual(message.toByteString(), lazyField.toByteString());
}
+ public void testMergeExtensions() throws Exception {
+ TestAllExtensions message = TestUtil.getAllExtensionsSet();
+ LazyFieldLite original = createLazyFieldLiteFromMessage(message);
+ LazyFieldLite merged = new LazyFieldLite();
+ merged.merge(original);
+ TestAllExtensions value = (TestAllExtensions) merged.getValue(
+ TestAllExtensions.getDefaultInstance());
+ assertEquals(message, value);
+ }
+
+ public void testEmptyLazyField() throws Exception {
+ LazyFieldLite field = new LazyFieldLite();
+ assertEquals(0, field.getSerializedSize());
+ assertEquals(ByteString.EMPTY, field.toByteString());
+ }
+
+ public void testInvalidProto() throws Exception {
+ // Silently fails and uses the default instance.
+ LazyFieldLite field = new LazyFieldLite(
+ TestUtil.getExtensionRegistry(), ByteString.copyFromUtf8("invalid"));
+ assertEquals(
+ TestAllTypes.getDefaultInstance(), field.getValue(TestAllTypes.getDefaultInstance()));
+ assertEquals(0, field.getSerializedSize());
+ assertEquals(ByteString.EMPTY, field.toByteString());
+ }
+
+ public void testMergeBeforeParsing() throws Exception {
+ TestAllTypes message1 = TestAllTypes.newBuilder().setOptionalInt32(1).build();
+ LazyFieldLite field1 = createLazyFieldLiteFromMessage(message1);
+ TestAllTypes message2 = TestAllTypes.newBuilder().setOptionalInt64(2).build();
+ LazyFieldLite field2 = createLazyFieldLiteFromMessage(message2);
+
+ field1.merge(field2);
+ TestAllTypes expected =
+ TestAllTypes.newBuilder().setOptionalInt32(1).setOptionalInt64(2).build();
+ assertEquals(expected, field1.getValue(TestAllTypes.getDefaultInstance()));
+ }
+
+ public void testMergeOneNotParsed() throws Exception {
+ // Test a few different paths that involve one message that was not parsed.
+ TestAllTypes message1 = TestAllTypes.newBuilder().setOptionalInt32(1).build();
+ TestAllTypes message2 = TestAllTypes.newBuilder().setOptionalInt64(2).build();
+ TestAllTypes expected =
+ TestAllTypes.newBuilder().setOptionalInt32(1).setOptionalInt64(2).build();
+
+ LazyFieldLite field1 = LazyFieldLite.fromValue(message1);
+ field1.getValue(TestAllTypes.getDefaultInstance()); // Force parsing.
+ LazyFieldLite field2 = createLazyFieldLiteFromMessage(message2);
+ field1.merge(field2);
+ assertEquals(expected, field1.getValue(TestAllTypes.getDefaultInstance()));
+
+ // Now reverse which one is parsed first.
+ field1 = LazyFieldLite.fromValue(message1);
+ field2 = createLazyFieldLiteFromMessage(message2);
+ field2.getValue(TestAllTypes.getDefaultInstance()); // Force parsing.
+ field1.merge(field2);
+ assertEquals(expected, field1.getValue(TestAllTypes.getDefaultInstance()));
+ }
+
+ public void testMergeInvalid() throws Exception {
+ // Test a few different paths that involve one message that was not parsed.
+ TestAllTypes message = TestAllTypes.newBuilder().setOptionalInt32(1).build();
+ LazyFieldLite valid = LazyFieldLite.fromValue(message);
+ LazyFieldLite invalid = new LazyFieldLite(
+ TestUtil.getExtensionRegistry(), ByteString.copyFromUtf8("invalid"));
+ invalid.merge(valid);
+
+ // We swallow the exception and just use the set field.
+ assertEquals(message, invalid.getValue(TestAllTypes.getDefaultInstance()));
+ }
+
+ public void testMergeKeepsExtensionsWhenPossible() throws Exception {
+ // In this test we attempt to only use the empty registry, which will strip out all extensions
+ // when serializing and then parsing. We verify that each code path will attempt to not
+ // serialize and parse a message that was set directly without going through the
+ // extensionRegistry.
+ TestAllExtensions messageWithExtensions =
+ TestAllExtensions.newBuilder().setExtension(optionalInt32Extension, 42).build();
+ TestAllExtensions emptyMessage = TestAllExtensions.newBuilder().build();
+
+ ExtensionRegistryLite emptyRegistry = ExtensionRegistryLite.getEmptyRegistry();
+
+ LazyFieldLite field = LazyFieldLite.fromValue(messageWithExtensions);
+ field.merge(createLazyFieldLiteFromMessage(emptyRegistry, emptyMessage));
+ assertEquals(messageWithExtensions, field.getValue(TestAllExtensions.getDefaultInstance()));
+
+ // Now reverse the order of the merging.
+ field = createLazyFieldLiteFromMessage(emptyRegistry, emptyMessage);
+ field.merge(LazyFieldLite.fromValue(messageWithExtensions));
+ assertEquals(messageWithExtensions, field.getValue(TestAllExtensions.getDefaultInstance()));
+
+ // Now try parsing the empty field first.
+ field = LazyFieldLite.fromValue(messageWithExtensions);
+ LazyFieldLite other = createLazyFieldLiteFromMessage(emptyRegistry, emptyMessage);
+ other.getValue(TestAllExtensions.getDefaultInstance()); // Force parsing.
+ field.merge(other);
+ assertEquals(messageWithExtensions, field.getValue(TestAllExtensions.getDefaultInstance()));
+
+ // And again reverse.
+ field = createLazyFieldLiteFromMessage(emptyRegistry, emptyMessage);
+ field.getValue(TestAllExtensions.getDefaultInstance()); // Force parsing.
+ other = LazyFieldLite.fromValue(messageWithExtensions);
+ field.merge(other);
+ assertEquals(messageWithExtensions, field.getValue(TestAllExtensions.getDefaultInstance()));
+ }
+
+ public void testMergeMightLoseExtensions() throws Exception {
+ // Test that we don't know about the extensions when parsing.
+ TestAllExtensions message1 =
+ TestAllExtensions.newBuilder().setExtension(optionalInt32Extension, 1).build();
+ TestAllExtensions message2 =
+ TestAllExtensions.newBuilder().setExtension(optionalInt64Extension, 2L).build();
+
+ LazyFieldLite field = LazyFieldLite.fromValue(message1);
+ field.merge(LazyFieldLite.fromValue(message2));
+
+ // We lose the extensions from message 2 because we have to serialize it and then parse it
+ // again, using the empty registry this time.
+ TestAllExtensions value =
+ (TestAllExtensions) field.getValue(TestAllExtensions.getDefaultInstance());
+ assertTrue(value.hasExtension(optionalInt32Extension));
+ assertEquals(Integer.valueOf(1), value.getExtension(optionalInt32Extension));
+ assertFalse(value.hasExtension(optionalInt64Extension));
+
+ // The field is still there, it is just unknown.
+ assertTrue(value.getUnknownFields()
+ .hasField(optionalInt64Extension.getDescriptor().getNumber()));
+ }
+
// Help methods.
private LazyFieldLite createLazyFieldLiteFromMessage(MessageLite message) {
+ return createLazyFieldLiteFromMessage(TestUtil.getExtensionRegistry(), message);
+ }
+
+ private LazyFieldLite createLazyFieldLiteFromMessage(
+ ExtensionRegistryLite extensionRegistry, MessageLite message) {
ByteString bytes = message.toByteString();
- return new LazyFieldLite(TestUtil.getExtensionRegistry(), bytes);
+ return new LazyFieldLite(extensionRegistry, bytes);
}
private void changeValue(LazyFieldLite lazyField) {
diff --git a/java/src/test/java/com/google/protobuf/LazyMessageLiteTest.java b/java/src/test/java/com/google/protobuf/LazyMessageLiteTest.java
index 9de794fe..afe0fffd 100644
--- a/java/src/test/java/com/google/protobuf/LazyMessageLiteTest.java
+++ b/java/src/test/java/com/google/protobuf/LazyMessageLiteTest.java
@@ -30,6 +30,7 @@
package com.google.protobuf;
+import protobuf_unittest.LazyFieldsLite.LazyExtension;
import protobuf_unittest.LazyFieldsLite.LazyInnerMessageLite;
import protobuf_unittest.LazyFieldsLite.LazyMessageLite;
import protobuf_unittest.LazyFieldsLite.LazyNestedInnerMessageLite;
@@ -285,4 +286,22 @@ public class LazyMessageLiteTest extends TestCase {
assertEquals(bytes, deserialized.toByteString());
}
+
+ public void testExtensions() throws Exception {
+ LazyInnerMessageLite.Builder innerBuilder = LazyInnerMessageLite.newBuilder();
+ innerBuilder.setExtension(
+ LazyExtension.extension, LazyExtension.newBuilder()
+ .setName("name").build());
+ assertTrue(innerBuilder.hasExtension(LazyExtension.extension));
+ assertEquals("name", innerBuilder.getExtension(LazyExtension.extension).getName());
+
+ LazyInnerMessageLite innerMessage = innerBuilder.build();
+ assertTrue(innerMessage.hasExtension(LazyExtension.extension));
+ assertEquals("name", innerMessage.getExtension(LazyExtension.extension).getName());
+
+ LazyMessageLite lite = LazyMessageLite.newBuilder()
+ .setInner(innerMessage).build();
+ assertTrue(lite.getInner().hasExtension(LazyExtension.extension));
+ assertEquals("name", lite.getInner().getExtension(LazyExtension.extension).getName());
+ }
}
diff --git a/java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java b/java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java
index f3012b96..0f42ac50 100644
--- a/java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java
+++ b/java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java
@@ -30,9 +30,13 @@
package com.google.protobuf;
+import static java.util.Arrays.asList;
+
import junit.framework.TestCase;
import java.util.ArrayList;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
import java.util.List;
/**
@@ -171,4 +175,188 @@ public class LazyStringArrayListTest extends TestCase {
assertSame(BYTE_STRING_B, list2.getByteString(1));
assertSame(BYTE_STRING_C, list2.getByteString(2));
}
+
+ public void testModificationWithIteration() {
+ LazyStringArrayList list = new LazyStringArrayList();
+ list.addAll(asList(STRING_A, STRING_B, STRING_C));
+ Iterator<String> iterator = list.iterator();
+ assertEquals(3, list.size());
+ assertEquals(STRING_A, list.get(0));
+ assertEquals(STRING_A, iterator.next());
+
+ // Does not structurally modify.
+ iterator = list.iterator();
+ list.set(0, STRING_B);
+ iterator.next();
+
+ list.remove(0);
+ try {
+ iterator.next();
+ fail();
+ } catch (ConcurrentModificationException e) {
+ // expected
+ }
+
+ iterator = list.iterator();
+ list.add(0, STRING_C);
+ try {
+ iterator.next();
+ fail();
+ } catch (ConcurrentModificationException e) {
+ // expected
+ }
+ }
+
+ public void testMakeImmutable() {
+ LazyStringArrayList list = new LazyStringArrayList();
+ list.add(STRING_A);
+ list.add(STRING_B);
+ list.add(STRING_C);
+ list.makeImmutable();
+ assertGenericListImmutable(list, STRING_A);
+
+ // LazyStringArrayList has extra methods not covered in the generic
+ // assertion.
+
+ try {
+ list.add(BYTE_STRING_A.toByteArray());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.add(BYTE_STRING_A);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAllByteArray(asList(BYTE_STRING_A.toByteArray()));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAllByteString(asList(BYTE_STRING_A));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.mergeFrom(new LazyStringArrayList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.set(0, BYTE_STRING_A.toByteArray());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.set(0, BYTE_STRING_A);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ }
+
+ public void testImmutabilityPropagation() {
+ LazyStringArrayList list = new LazyStringArrayList();
+ list.add(STRING_A);
+ list.makeImmutable();
+
+ assertGenericListImmutable(list.asByteStringList(), BYTE_STRING_A);
+
+ // Arrays use reference equality so need to retrieve the underlying value
+ // to properly test deep immutability.
+ List<byte[]> byteArrayList = list.asByteArrayList();
+ assertGenericListImmutable(byteArrayList, byteArrayList.get(0));
+ }
+
+ private static <T> void assertGenericListImmutable(List<T> list, T value) {
+ try {
+ list.add(value);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.add(0, value);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(asList(value));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(0, asList(value));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.clear();
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.remove(0);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.remove(value);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.removeAll(asList(value));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.retainAll(asList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.retainAll(asList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.set(0, value);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ }
}
diff --git a/java/src/test/java/com/google/protobuf/LiteTest.java b/java/src/test/java/com/google/protobuf/LiteTest.java
index 4d8037fc..8c3b5e5c 100644
--- a/java/src/test/java/com/google/protobuf/LiteTest.java
+++ b/java/src/test/java/com/google/protobuf/LiteTest.java
@@ -30,9 +30,18 @@
package com.google.protobuf;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
+
import com.google.protobuf.UnittestLite;
-import com.google.protobuf.UnittestLite.TestAllTypesLite;
+import com.google.protobuf.UnittestLite.ForeignEnumLite;
+import com.google.protobuf.UnittestLite.ForeignMessageLite;
import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
+import com.google.protobuf.UnittestLite.TestAllTypesLite;
+import com.google.protobuf.UnittestLite.TestAllTypesLite.NestedMessage;
+import com.google.protobuf.UnittestLite.TestAllTypesLite.OneofFieldCase;
+import com.google.protobuf.UnittestLite.TestAllTypesLite.OptionalGroup;
+import com.google.protobuf.UnittestLite.TestAllTypesLite.RepeatedGroup;
import com.google.protobuf.UnittestLite.TestNestedExtensionLite;
import junit.framework.TestCase;
@@ -149,13 +158,1302 @@ public class LiteTest extends TestCase {
public void testClone() {
TestAllTypesLite.Builder expected = TestAllTypesLite.newBuilder()
.setOptionalInt32(123);
- assertEquals(
- expected.getOptionalInt32(), expected.clone().getOptionalInt32());
+ assertEquals(
+ expected.getOptionalInt32(), expected.clone().getOptionalInt32());
- TestAllExtensionsLite.Builder expected2 = TestAllExtensionsLite.newBuilder()
- .setExtension(UnittestLite.optionalInt32ExtensionLite, 123);
- assertEquals(
- expected2.getExtension(UnittestLite.optionalInt32ExtensionLite),
- expected2.clone().getExtension(UnittestLite.optionalInt32ExtensionLite));
+ TestAllExtensionsLite.Builder expected2 = TestAllExtensionsLite.newBuilder()
+ .setExtension(UnittestLite.optionalInt32ExtensionLite, 123);
+ assertEquals(
+ expected2.getExtension(UnittestLite.optionalInt32ExtensionLite),
+ expected2.clone().getExtension(UnittestLite.optionalInt32ExtensionLite));
+ }
+
+ public void testAddAll() {
+ try {
+ TestAllTypesLite.newBuilder()
+ .addAllRepeatedBytes(null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected.
+ }
+ }
+
+ public void testSanityCopyOnWrite() throws InvalidProtocolBufferException {
+ // Since builders are implemented as a thin wrapper around a message
+ // instance, we attempt to verify that we can't cause the builder to modify
+ // a produced message.
+
+ TestAllTypesLite.Builder builder = TestAllTypesLite.newBuilder();
+ TestAllTypesLite message = builder.build();
+ TestAllTypesLite messageAfterBuild;
+ builder.setOptionalBool(true);
+ assertEquals(false, message.getOptionalBool());
+ assertEquals(true, builder.getOptionalBool());
+ messageAfterBuild = builder.build();
+ assertEquals(true, messageAfterBuild.getOptionalBool());
+ assertEquals(false, message.getOptionalBool());
+ builder.clearOptionalBool();
+ assertEquals(false, builder.getOptionalBool());
+ assertEquals(true, messageAfterBuild.getOptionalBool());
+
+ message = builder.build();
+ builder.setOptionalBytes(ByteString.copyFromUtf8("hi"));
+ assertEquals(ByteString.EMPTY, message.getOptionalBytes());
+ assertEquals(ByteString.copyFromUtf8("hi"), builder.getOptionalBytes());
+ messageAfterBuild = builder.build();
+ assertEquals(
+ ByteString.copyFromUtf8("hi"), messageAfterBuild.getOptionalBytes());
+ assertEquals(ByteString.EMPTY, message.getOptionalBytes());
+ builder.clearOptionalBytes();
+ assertEquals(ByteString.EMPTY, builder.getOptionalBytes());
+ assertEquals(
+ ByteString.copyFromUtf8("hi"), messageAfterBuild.getOptionalBytes());
+
+ message = builder.build();
+ builder.setOptionalCord("hi");
+ assertEquals("", message.getOptionalCord());
+ assertEquals("hi", builder.getOptionalCord());
+ messageAfterBuild = builder.build();
+ assertEquals("hi", messageAfterBuild.getOptionalCord());
+ assertEquals("", message.getOptionalCord());
+ builder.clearOptionalCord();
+ assertEquals("", builder.getOptionalCord());
+ assertEquals("hi", messageAfterBuild.getOptionalCord());
+
+ message = builder.build();
+ builder.setOptionalCordBytes(ByteString.copyFromUtf8("no"));
+ assertEquals(ByteString.EMPTY, message.getOptionalCordBytes());
+ assertEquals(ByteString.copyFromUtf8("no"), builder.getOptionalCordBytes());
+ messageAfterBuild = builder.build();
+ assertEquals(
+ ByteString.copyFromUtf8("no"),
+ messageAfterBuild.getOptionalCordBytes());
+ assertEquals(ByteString.EMPTY, message.getOptionalCordBytes());
+ builder.clearOptionalCord();
+ assertEquals(ByteString.EMPTY, builder.getOptionalCordBytes());
+ assertEquals(
+ ByteString.copyFromUtf8("no"),
+ messageAfterBuild.getOptionalCordBytes());
+
+ message = builder.build();
+ builder.setOptionalDouble(1);
+ assertEquals(0D, message.getOptionalDouble());
+ assertEquals(1D, builder.getOptionalDouble());
+ messageAfterBuild = builder.build();
+ assertEquals(1D, messageAfterBuild.getOptionalDouble());
+ assertEquals(0D, message.getOptionalDouble());
+ builder.clearOptionalDouble();
+ assertEquals(0D, builder.getOptionalDouble());
+ assertEquals(1D, messageAfterBuild.getOptionalDouble());
+
+ message = builder.build();
+ builder.setOptionalFixed32(1);
+ assertEquals(0, message.getOptionalFixed32());
+ assertEquals(1, builder.getOptionalFixed32());
+ messageAfterBuild = builder.build();
+ assertEquals(1, messageAfterBuild.getOptionalFixed32());
+ assertEquals(0, message.getOptionalFixed32());
+ builder.clearOptionalFixed32();
+ assertEquals(0, builder.getOptionalFixed32());
+ assertEquals(1, messageAfterBuild.getOptionalFixed32());
+
+ message = builder.build();
+ builder.setOptionalFixed64(1);
+ assertEquals(0L, message.getOptionalFixed64());
+ assertEquals(1L, builder.getOptionalFixed64());
+ messageAfterBuild = builder.build();
+ assertEquals(1L, messageAfterBuild.getOptionalFixed64());
+ assertEquals(0L, message.getOptionalFixed64());
+ builder.clearOptionalFixed64();
+ assertEquals(0L, builder.getOptionalFixed64());
+ assertEquals(1L, messageAfterBuild.getOptionalFixed64());
+
+ message = builder.build();
+ builder.setOptionalFloat(1);
+ assertEquals(0F, message.getOptionalFloat());
+ assertEquals(1F, builder.getOptionalFloat());
+ messageAfterBuild = builder.build();
+ assertEquals(1F, messageAfterBuild.getOptionalFloat());
+ assertEquals(0F, message.getOptionalFloat());
+ builder.clearOptionalFloat();
+ assertEquals(0F, builder.getOptionalFloat());
+ assertEquals(1F, messageAfterBuild.getOptionalFloat());
+
+ message = builder.build();
+ builder.setOptionalForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR);
+ assertEquals(
+ ForeignEnumLite.FOREIGN_LITE_FOO, message.getOptionalForeignEnum());
+ assertEquals(
+ ForeignEnumLite.FOREIGN_LITE_BAR, builder.getOptionalForeignEnum());
+ messageAfterBuild = builder.build();
+ assertEquals(
+ ForeignEnumLite.FOREIGN_LITE_BAR,
+ messageAfterBuild.getOptionalForeignEnum());
+ assertEquals(
+ ForeignEnumLite.FOREIGN_LITE_FOO, message.getOptionalForeignEnum());
+ builder.clearOptionalForeignEnum();
+ assertEquals(
+ ForeignEnumLite.FOREIGN_LITE_FOO, builder.getOptionalForeignEnum());
+ assertEquals(
+ ForeignEnumLite.FOREIGN_LITE_BAR,
+ messageAfterBuild.getOptionalForeignEnum());
+
+ message = builder.build();
+ ForeignMessageLite foreignMessage = ForeignMessageLite.newBuilder()
+ .setC(1)
+ .build();
+ builder.setOptionalForeignMessage(foreignMessage);
+ assertEquals(
+ ForeignMessageLite.getDefaultInstance(),
+ message.getOptionalForeignMessage());
+ assertEquals(foreignMessage, builder.getOptionalForeignMessage());
+ messageAfterBuild = builder.build();
+ assertEquals(foreignMessage, messageAfterBuild.getOptionalForeignMessage());
+ assertEquals(
+ ForeignMessageLite.getDefaultInstance(),
+ message.getOptionalForeignMessage());
+ builder.clearOptionalForeignMessage();
+ assertEquals(
+ ForeignMessageLite.getDefaultInstance(),
+ builder.getOptionalForeignMessage());
+ assertEquals(foreignMessage, messageAfterBuild.getOptionalForeignMessage());
+
+ message = builder.build();
+ ForeignMessageLite.Builder foreignMessageBuilder =
+ ForeignMessageLite.newBuilder()
+ .setC(3);
+ builder.setOptionalForeignMessage(foreignMessageBuilder);
+ assertEquals(
+ ForeignMessageLite.getDefaultInstance(),
+ message.getOptionalForeignMessage());
+ // LITE_RUNTIME doesn't implement equals so we compare on a property and
+ // ensure the property isn't set on foreignMessage.
+ assertEquals(3, builder.getOptionalForeignMessage().getC());
+ messageAfterBuild = builder.build();
+ assertEquals(3, messageAfterBuild.getOptionalForeignMessage().getC());
+ assertEquals(
+ ForeignMessageLite.getDefaultInstance(),
+ message.getOptionalForeignMessage());
+ builder.clearOptionalForeignMessage();
+ assertEquals(
+ ForeignMessageLite.getDefaultInstance(),
+ builder.getOptionalForeignMessage());
+ assertEquals(3, messageAfterBuild.getOptionalForeignMessage().getC());
+
+ message = builder.build();
+ OptionalGroup optionalGroup = OptionalGroup.newBuilder()
+ .setA(1)
+ .build();
+ builder.setOptionalGroup(optionalGroup);
+ assertEquals(
+ OptionalGroup.getDefaultInstance(), message.getOptionalGroup());
+ assertEquals(optionalGroup, builder.getOptionalGroup());
+ messageAfterBuild = builder.build();
+ assertEquals(optionalGroup, messageAfterBuild.getOptionalGroup());
+ assertEquals(
+ OptionalGroup.getDefaultInstance(), message.getOptionalGroup());
+ builder.clearOptionalGroup();
+ assertEquals(
+ OptionalGroup.getDefaultInstance(), builder.getOptionalGroup());
+ assertEquals(optionalGroup, messageAfterBuild.getOptionalGroup());
+
+ message = builder.build();
+ OptionalGroup.Builder optionalGroupBuilder = OptionalGroup.newBuilder()
+ .setA(3);
+ builder.setOptionalGroup(optionalGroupBuilder);
+ assertEquals(
+ OptionalGroup.getDefaultInstance(), message.getOptionalGroup());
+ // LITE_RUNTIME doesn't implement equals so we compare on a property and
+ // ensure the property isn't set on optionalGroup.
+ assertEquals(3, builder.getOptionalGroup().getA());
+ messageAfterBuild = builder.build();
+ assertEquals(3, messageAfterBuild.getOptionalGroup().getA());
+ assertEquals(
+ OptionalGroup.getDefaultInstance(), message.getOptionalGroup());
+ builder.clearOptionalGroup();
+ assertEquals(
+ OptionalGroup.getDefaultInstance(), builder.getOptionalGroup());
+ assertEquals(3, messageAfterBuild.getOptionalGroup().getA());
+
+ message = builder.build();
+ builder.setOptionalInt32(1);
+ assertEquals(0, message.getOptionalInt32());
+ assertEquals(1, builder.getOptionalInt32());
+ messageAfterBuild = builder.build();
+ assertEquals(1, messageAfterBuild.getOptionalInt32());
+ assertEquals(0, message.getOptionalInt32());
+ builder.clearOptionalInt32();
+ assertEquals(0, builder.getOptionalInt32());
+ assertEquals(1, messageAfterBuild.getOptionalInt32());
+
+ message = builder.build();
+ builder.setOptionalInt64(1);
+ assertEquals(0L, message.getOptionalInt64());
+ assertEquals(1L, builder.getOptionalInt64());
+ messageAfterBuild = builder.build();
+ assertEquals(1L, messageAfterBuild.getOptionalInt64());
+ assertEquals(0L, message.getOptionalInt64());
+ builder.clearOptionalInt64();
+ assertEquals(0L, builder.getOptionalInt64());
+ assertEquals(1L, messageAfterBuild.getOptionalInt64());
+
+ message = builder.build();
+ NestedMessage nestedMessage = NestedMessage.newBuilder()
+ .setBb(1)
+ .build();
+ builder.setOptionalLazyMessage(nestedMessage);
+ assertEquals(
+ NestedMessage.getDefaultInstance(),
+ message.getOptionalLazyMessage());
+ assertEquals(nestedMessage, builder.getOptionalLazyMessage());
+ messageAfterBuild = builder.build();
+ assertEquals(nestedMessage, messageAfterBuild.getOptionalLazyMessage());
+ assertEquals(
+ NestedMessage.getDefaultInstance(),
+ message.getOptionalLazyMessage());
+ builder.clearOptionalLazyMessage();
+ assertEquals(
+ NestedMessage.getDefaultInstance(), builder.getOptionalLazyMessage());
+ assertEquals(nestedMessage, messageAfterBuild.getOptionalLazyMessage());
+
+ message = builder.build();
+ NestedMessage.Builder nestedMessageBuilder =
+ NestedMessage.newBuilder()
+ .setBb(3);
+ builder.setOptionalLazyMessage(nestedMessageBuilder);
+ assertEquals(
+ NestedMessage.getDefaultInstance(),
+ message.getOptionalLazyMessage());
+ // LITE_RUNTIME doesn't implement equals so we compare on a property.
+ assertEquals(3, builder.getOptionalLazyMessage().getBb());
+ messageAfterBuild = builder.build();
+ assertEquals(3, messageAfterBuild.getOptionalLazyMessage().getBb());
+ assertEquals(
+ NestedMessage.getDefaultInstance(),
+ message.getOptionalLazyMessage());
+ builder.clearOptionalLazyMessage();
+ assertEquals(
+ NestedMessage.getDefaultInstance(), builder.getOptionalLazyMessage());
+ assertEquals(3, messageAfterBuild.getOptionalLazyMessage().getBb());
+
+ message = builder.build();
+ builder.setOptionalSfixed32(1);
+ assertEquals(0, message.getOptionalSfixed32());
+ assertEquals(1, builder.getOptionalSfixed32());
+ messageAfterBuild = builder.build();
+ assertEquals(1, messageAfterBuild.getOptionalSfixed32());
+ assertEquals(0, message.getOptionalSfixed32());
+ builder.clearOptionalSfixed32();
+ assertEquals(0, builder.getOptionalSfixed32());
+ assertEquals(1, messageAfterBuild.getOptionalSfixed32());
+
+ message = builder.build();
+ builder.setOptionalSfixed64(1);
+ assertEquals(0L, message.getOptionalSfixed64());
+ assertEquals(1L, builder.getOptionalSfixed64());
+ messageAfterBuild = builder.build();
+ assertEquals(1L, messageAfterBuild.getOptionalSfixed64());
+ assertEquals(0L, message.getOptionalSfixed64());
+ builder.clearOptionalSfixed64();
+ assertEquals(0L, builder.getOptionalSfixed64());
+ assertEquals(1L, messageAfterBuild.getOptionalSfixed64());
+
+ message = builder.build();
+ builder.setOptionalSint32(1);
+ assertEquals(0, message.getOptionalSint32());
+ assertEquals(1, builder.getOptionalSint32());
+ messageAfterBuild = builder.build();
+ assertEquals(1, messageAfterBuild.getOptionalSint32());
+ builder.clearOptionalSint32();
+ assertEquals(0, builder.getOptionalSint32());
+ assertEquals(1, messageAfterBuild.getOptionalSint32());
+
+ message = builder.build();
+ builder.setOptionalSint64(1);
+ assertEquals(0L, message.getOptionalSint64());
+ assertEquals(1L, builder.getOptionalSint64());
+ messageAfterBuild = builder.build();
+ assertEquals(1L, messageAfterBuild.getOptionalSint64());
+ assertEquals(0L, message.getOptionalSint64());
+ builder.clearOptionalSint64();
+ assertEquals(0L, builder.getOptionalSint64());
+ assertEquals(1L, messageAfterBuild.getOptionalSint64());
+
+ message = builder.build();
+ builder.setOptionalString("hi");
+ assertEquals("", message.getOptionalString());
+ assertEquals("hi", builder.getOptionalString());
+ messageAfterBuild = builder.build();
+ assertEquals("hi", messageAfterBuild.getOptionalString());
+ assertEquals("", message.getOptionalString());
+ builder.clearOptionalString();
+ assertEquals("", builder.getOptionalString());
+ assertEquals("hi", messageAfterBuild.getOptionalString());
+
+ message = builder.build();
+ builder.setOptionalStringBytes(ByteString.copyFromUtf8("no"));
+ assertEquals(ByteString.EMPTY, message.getOptionalStringBytes());
+ assertEquals(
+ ByteString.copyFromUtf8("no"), builder.getOptionalStringBytes());
+ messageAfterBuild = builder.build();
+ assertEquals(
+ ByteString.copyFromUtf8("no"),
+ messageAfterBuild.getOptionalStringBytes());
+ assertEquals(ByteString.EMPTY, message.getOptionalStringBytes());
+ builder.clearOptionalString();
+ assertEquals(ByteString.EMPTY, builder.getOptionalStringBytes());
+ assertEquals(
+ ByteString.copyFromUtf8("no"),
+ messageAfterBuild.getOptionalStringBytes());
+
+ message = builder.build();
+ builder.setOptionalStringPiece("hi");
+ assertEquals("", message.getOptionalStringPiece());
+ assertEquals("hi", builder.getOptionalStringPiece());
+ messageAfterBuild = builder.build();
+ assertEquals("hi", messageAfterBuild.getOptionalStringPiece());
+ assertEquals("", message.getOptionalStringPiece());
+ builder.clearOptionalStringPiece();
+ assertEquals("", builder.getOptionalStringPiece());
+ assertEquals("hi", messageAfterBuild.getOptionalStringPiece());
+
+ message = builder.build();
+ builder.setOptionalStringPieceBytes(ByteString.copyFromUtf8("no"));
+ assertEquals(ByteString.EMPTY, message.getOptionalStringPieceBytes());
+ assertEquals(
+ ByteString.copyFromUtf8("no"), builder.getOptionalStringPieceBytes());
+ messageAfterBuild = builder.build();
+ assertEquals(
+ ByteString.copyFromUtf8("no"),
+ messageAfterBuild.getOptionalStringPieceBytes());
+ assertEquals(ByteString.EMPTY, message.getOptionalStringPieceBytes());
+ builder.clearOptionalStringPiece();
+ assertEquals(ByteString.EMPTY, builder.getOptionalStringPieceBytes());
+ assertEquals(
+ ByteString.copyFromUtf8("no"),
+ messageAfterBuild.getOptionalStringPieceBytes());
+
+ message = builder.build();
+ builder.setOptionalUint32(1);
+ assertEquals(0, message.getOptionalUint32());
+ assertEquals(1, builder.getOptionalUint32());
+ messageAfterBuild = builder.build();
+ assertEquals(1, messageAfterBuild.getOptionalUint32());
+ assertEquals(0, message.getOptionalUint32());
+ builder.clearOptionalUint32();
+ assertEquals(0, builder.getOptionalUint32());
+ assertEquals(1, messageAfterBuild.getOptionalUint32());
+
+ message = builder.build();
+ builder.setOptionalUint64(1);
+ assertEquals(0L, message.getOptionalUint64());
+ assertEquals(1L, builder.getOptionalUint64());
+ messageAfterBuild = builder.build();
+ assertEquals(1L, messageAfterBuild.getOptionalUint64());
+ assertEquals(0L, message.getOptionalUint64());
+ builder.clearOptionalUint64();
+ assertEquals(0L, builder.getOptionalUint64());
+ assertEquals(1L, messageAfterBuild.getOptionalUint64());
+
+ message = builder.build();
+ builder.addAllRepeatedBool(singletonList(true));
+ assertEquals(emptyList(), message.getRepeatedBoolList());
+ assertEquals(singletonList(true), builder.getRepeatedBoolList());
+ assertEquals(emptyList(), message.getRepeatedBoolList());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedBool();
+ assertEquals(emptyList(), builder.getRepeatedBoolList());
+ assertEquals(singletonList(true), messageAfterBuild.getRepeatedBoolList());
+
+ message = builder.build();
+ builder.addAllRepeatedBytes(singletonList(ByteString.copyFromUtf8("hi")));
+ assertEquals(emptyList(), message.getRepeatedBytesList());
+ assertEquals(
+ singletonList(ByteString.copyFromUtf8("hi")),
+ builder.getRepeatedBytesList());
+ assertEquals(emptyList(), message.getRepeatedBytesList());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedBytes();
+ assertEquals(emptyList(), builder.getRepeatedBytesList());
+ assertEquals(
+ singletonList(ByteString.copyFromUtf8("hi")),
+ messageAfterBuild.getRepeatedBytesList());
+
+ message = builder.build();
+ builder.addAllRepeatedCord(singletonList("hi"));
+ assertEquals(emptyList(), message.getRepeatedCordList());
+ assertEquals(singletonList("hi"), builder.getRepeatedCordList());
+ assertEquals(emptyList(), message.getRepeatedCordList());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedCord();
+ assertEquals(emptyList(), builder.getRepeatedCordList());
+ assertEquals(singletonList("hi"), messageAfterBuild.getRepeatedCordList());
+
+ message = builder.build();
+ builder.addAllRepeatedDouble(singletonList(1D));
+ assertEquals(emptyList(), message.getRepeatedDoubleList());
+ assertEquals(singletonList(1D), builder.getRepeatedDoubleList());
+ assertEquals(emptyList(), message.getRepeatedDoubleList());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedDouble();
+ assertEquals(emptyList(), builder.getRepeatedDoubleList());
+ assertEquals(singletonList(1D), messageAfterBuild.getRepeatedDoubleList());
+
+ message = builder.build();
+ builder.addAllRepeatedFixed32(singletonList(1));
+ assertEquals(emptyList(), message.getRepeatedFixed32List());
+ assertEquals(singletonList(1), builder.getRepeatedFixed32List());
+ assertEquals(emptyList(), message.getRepeatedFixed32List());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedFixed32();
+ assertEquals(emptyList(), builder.getRepeatedFixed32List());
+ assertEquals(singletonList(1), messageAfterBuild.getRepeatedFixed32List());
+
+ message = builder.build();
+ builder.addAllRepeatedFixed64(singletonList(1L));
+ assertEquals(emptyList(), message.getRepeatedFixed64List());
+ assertEquals(singletonList(1L), builder.getRepeatedFixed64List());
+ assertEquals(emptyList(), message.getRepeatedFixed64List());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedFixed64();
+ assertEquals(emptyList(), builder.getRepeatedFixed64List());
+ assertEquals(singletonList(1L), messageAfterBuild.getRepeatedFixed64List());
+
+ message = builder.build();
+ builder.addAllRepeatedFloat(singletonList(1F));
+ assertEquals(emptyList(), message.getRepeatedFloatList());
+ assertEquals(singletonList(1F), builder.getRepeatedFloatList());
+ assertEquals(emptyList(), message.getRepeatedFloatList());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedFloat();
+ assertEquals(emptyList(), builder.getRepeatedFloatList());
+ assertEquals(singletonList(1F), messageAfterBuild.getRepeatedFloatList());
+
+ message = builder.build();
+ builder.addAllRepeatedForeignEnum(
+ singletonList(ForeignEnumLite.FOREIGN_LITE_BAR));
+ assertEquals(emptyList(), message.getRepeatedForeignEnumList());
+ assertEquals(
+ singletonList(ForeignEnumLite.FOREIGN_LITE_BAR),
+ builder.getRepeatedForeignEnumList());
+ assertEquals(emptyList(), message.getRepeatedForeignEnumList());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedForeignEnum();
+ assertEquals(emptyList(), builder.getRepeatedForeignEnumList());
+ assertEquals(
+ singletonList(ForeignEnumLite.FOREIGN_LITE_BAR),
+ messageAfterBuild.getRepeatedForeignEnumList());
+
+ message = builder.build();
+ builder.addAllRepeatedForeignMessage(singletonList(foreignMessage));
+ assertEquals(emptyList(), message.getRepeatedForeignMessageList());
+ assertEquals(
+ singletonList(foreignMessage), builder.getRepeatedForeignMessageList());
+ assertEquals(emptyList(), message.getRepeatedForeignMessageList());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedForeignMessage();
+ assertEquals(emptyList(), builder.getRepeatedForeignMessageList());
+ assertEquals(
+ singletonList(foreignMessage),
+ messageAfterBuild.getRepeatedForeignMessageList());
+
+ message = builder.build();
+ builder.addAllRepeatedGroup(
+ singletonList(RepeatedGroup.getDefaultInstance()));
+ assertEquals(emptyList(), message.getRepeatedGroupList());
+ assertEquals(
+ singletonList(RepeatedGroup.getDefaultInstance()),
+ builder.getRepeatedGroupList());
+ assertEquals(emptyList(), message.getRepeatedGroupList());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedGroup();
+ assertEquals(emptyList(), builder.getRepeatedGroupList());
+ assertEquals(
+ singletonList(RepeatedGroup.getDefaultInstance()),
+ messageAfterBuild.getRepeatedGroupList());
+
+ message = builder.build();
+ builder.addAllRepeatedInt32(singletonList(1));
+ assertEquals(emptyList(), message.getRepeatedInt32List());
+ assertEquals(singletonList(1), builder.getRepeatedInt32List());
+ assertEquals(emptyList(), message.getRepeatedInt32List());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedInt32();
+ assertEquals(emptyList(), builder.getRepeatedInt32List());
+ assertEquals(singletonList(1), messageAfterBuild.getRepeatedInt32List());
+
+ message = builder.build();
+ builder.addAllRepeatedInt64(singletonList(1L));
+ assertEquals(emptyList(), message.getRepeatedInt64List());
+ assertEquals(singletonList(1L), builder.getRepeatedInt64List());
+ assertEquals(emptyList(), message.getRepeatedInt64List());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedInt64();
+ assertEquals(emptyList(), builder.getRepeatedInt64List());
+ assertEquals(singletonList(1L), messageAfterBuild.getRepeatedInt64List());
+
+ message = builder.build();
+ builder.addAllRepeatedLazyMessage(singletonList(nestedMessage));
+ assertEquals(emptyList(), message.getRepeatedLazyMessageList());
+ assertEquals(
+ singletonList(nestedMessage), builder.getRepeatedLazyMessageList());
+ assertEquals(emptyList(), message.getRepeatedLazyMessageList());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedLazyMessage();
+ assertEquals(emptyList(), builder.getRepeatedLazyMessageList());
+ assertEquals(
+ singletonList(nestedMessage),
+ messageAfterBuild.getRepeatedLazyMessageList());
+
+ message = builder.build();
+ builder.addAllRepeatedSfixed32(singletonList(1));
+ assertEquals(emptyList(), message.getRepeatedSfixed32List());
+ assertEquals(singletonList(1), builder.getRepeatedSfixed32List());
+ assertEquals(emptyList(), message.getRepeatedSfixed32List());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedSfixed32();
+ assertEquals(emptyList(), builder.getRepeatedSfixed32List());
+ assertEquals(singletonList(1), messageAfterBuild.getRepeatedSfixed32List());
+
+ message = builder.build();
+ builder.addAllRepeatedSfixed64(singletonList(1L));
+ assertEquals(emptyList(), message.getRepeatedSfixed64List());
+ assertEquals(singletonList(1L), builder.getRepeatedSfixed64List());
+ assertEquals(emptyList(), message.getRepeatedSfixed64List());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedSfixed64();
+ assertEquals(emptyList(), builder.getRepeatedSfixed64List());
+ assertEquals(
+ singletonList(1L), messageAfterBuild.getRepeatedSfixed64List());
+
+ message = builder.build();
+ builder.addAllRepeatedSint32(singletonList(1));
+ assertEquals(emptyList(), message.getRepeatedSint32List());
+ assertEquals(singletonList(1), builder.getRepeatedSint32List());
+ assertEquals(emptyList(), message.getRepeatedSint32List());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedSint32();
+ assertEquals(emptyList(), builder.getRepeatedSint32List());
+ assertEquals(singletonList(1), messageAfterBuild.getRepeatedSint32List());
+
+ message = builder.build();
+ builder.addAllRepeatedSint64(singletonList(1L));
+ assertEquals(emptyList(), message.getRepeatedSint64List());
+ assertEquals(singletonList(1L), builder.getRepeatedSint64List());
+ assertEquals(emptyList(), message.getRepeatedSint64List());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedSint64();
+ assertEquals(emptyList(), builder.getRepeatedSint64List());
+ assertEquals(singletonList(1L), messageAfterBuild.getRepeatedSint64List());
+
+ message = builder.build();
+ builder.addAllRepeatedString(singletonList("hi"));
+ assertEquals(emptyList(), message.getRepeatedStringList());
+ assertEquals(singletonList("hi"), builder.getRepeatedStringList());
+ assertEquals(emptyList(), message.getRepeatedStringList());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedString();
+ assertEquals(emptyList(), builder.getRepeatedStringList());
+ assertEquals(
+ singletonList("hi"), messageAfterBuild.getRepeatedStringList());
+
+ message = builder.build();
+ builder.addAllRepeatedStringPiece(singletonList("hi"));
+ assertEquals(emptyList(), message.getRepeatedStringPieceList());
+ assertEquals(singletonList("hi"), builder.getRepeatedStringPieceList());
+ assertEquals(emptyList(), message.getRepeatedStringPieceList());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedStringPiece();
+ assertEquals(emptyList(), builder.getRepeatedStringPieceList());
+ assertEquals(
+ singletonList("hi"), messageAfterBuild.getRepeatedStringPieceList());
+
+ message = builder.build();
+ builder.addAllRepeatedUint32(singletonList(1));
+ assertEquals(emptyList(), message.getRepeatedUint32List());
+ assertEquals(singletonList(1), builder.getRepeatedUint32List());
+ assertEquals(emptyList(), message.getRepeatedUint32List());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedUint32();
+ assertEquals(emptyList(), builder.getRepeatedUint32List());
+ assertEquals(singletonList(1), messageAfterBuild.getRepeatedUint32List());
+
+ message = builder.build();
+ builder.addAllRepeatedUint64(singletonList(1L));
+ assertEquals(emptyList(), message.getRepeatedUint64List());
+ assertEquals(singletonList(1L), builder.getRepeatedUint64List());
+ assertEquals(emptyList(), message.getRepeatedUint64List());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedUint64();
+ assertEquals(emptyList(), builder.getRepeatedUint64List());
+ assertEquals(singletonList(1L), messageAfterBuild.getRepeatedUint64List());
+
+ message = builder.build();
+ builder.addRepeatedBool(true);
+ assertEquals(emptyList(), message.getRepeatedBoolList());
+ assertEquals(singletonList(true), builder.getRepeatedBoolList());
+ assertEquals(emptyList(), message.getRepeatedBoolList());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedBool();
+ assertEquals(emptyList(), builder.getRepeatedBoolList());
+ assertEquals(singletonList(true), messageAfterBuild.getRepeatedBoolList());
+
+ message = builder.build();
+ builder.addRepeatedBytes(ByteString.copyFromUtf8("hi"));
+ assertEquals(emptyList(), message.getRepeatedBytesList());
+ assertEquals(
+ singletonList(ByteString.copyFromUtf8("hi")),
+ builder.getRepeatedBytesList());
+ assertEquals(emptyList(), message.getRepeatedBytesList());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedBytes();
+ assertEquals(emptyList(), builder.getRepeatedBytesList());
+ assertEquals(
+ singletonList(ByteString.copyFromUtf8("hi")),
+ messageAfterBuild.getRepeatedBytesList());
+
+ message = builder.build();
+ builder.addRepeatedCord("hi");
+ assertEquals(emptyList(), message.getRepeatedCordList());
+ assertEquals(singletonList("hi"), builder.getRepeatedCordList());
+ assertEquals(emptyList(), message.getRepeatedCordList());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedCord();
+ assertEquals(emptyList(), builder.getRepeatedCordList());
+ assertEquals(singletonList("hi"), messageAfterBuild.getRepeatedCordList());
+
+ message = builder.build();
+ builder.addRepeatedDouble(1D);
+ assertEquals(emptyList(), message.getRepeatedDoubleList());
+ assertEquals(singletonList(1D), builder.getRepeatedDoubleList());
+ assertEquals(emptyList(), message.getRepeatedDoubleList());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedDouble();
+ assertEquals(emptyList(), builder.getRepeatedDoubleList());
+ assertEquals(singletonList(1D), messageAfterBuild.getRepeatedDoubleList());
+
+ message = builder.build();
+ builder.addRepeatedFixed32(1);
+ assertEquals(emptyList(), message.getRepeatedFixed32List());
+ assertEquals(singletonList(1), builder.getRepeatedFixed32List());
+ assertEquals(emptyList(), message.getRepeatedFixed32List());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedFixed32();
+ assertEquals(emptyList(), builder.getRepeatedFixed32List());
+ assertEquals(singletonList(1), messageAfterBuild.getRepeatedFixed32List());
+
+ message = builder.build();
+ builder.addRepeatedFixed64(1L);
+ assertEquals(emptyList(), message.getRepeatedFixed64List());
+ assertEquals(singletonList(1L), builder.getRepeatedFixed64List());
+ assertEquals(emptyList(), message.getRepeatedFixed64List());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedFixed64();
+ assertEquals(emptyList(), builder.getRepeatedFixed64List());
+ assertEquals(singletonList(1L), messageAfterBuild.getRepeatedFixed64List());
+
+ message = builder.build();
+ builder.addRepeatedFloat(1F);
+ assertEquals(emptyList(), message.getRepeatedFloatList());
+ assertEquals(singletonList(1F), builder.getRepeatedFloatList());
+ assertEquals(emptyList(), message.getRepeatedFloatList());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedFloat();
+ assertEquals(emptyList(), builder.getRepeatedFloatList());
+ assertEquals(singletonList(1F), messageAfterBuild.getRepeatedFloatList());
+
+ message = builder.build();
+ builder.addRepeatedForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR);
+ assertEquals(emptyList(), message.getRepeatedForeignEnumList());
+ assertEquals(
+ singletonList(ForeignEnumLite.FOREIGN_LITE_BAR),
+ builder.getRepeatedForeignEnumList());
+ assertEquals(emptyList(), message.getRepeatedForeignEnumList());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedForeignEnum();
+ assertEquals(emptyList(), builder.getRepeatedForeignEnumList());
+ assertEquals(
+ singletonList(ForeignEnumLite.FOREIGN_LITE_BAR),
+ messageAfterBuild.getRepeatedForeignEnumList());
+
+ message = builder.build();
+ builder.addRepeatedForeignMessage(foreignMessage);
+ assertEquals(emptyList(), message.getRepeatedForeignMessageList());
+ assertEquals(
+ singletonList(foreignMessage), builder.getRepeatedForeignMessageList());
+ assertEquals(emptyList(), message.getRepeatedForeignMessageList());
+ messageAfterBuild = builder.build();
+ builder.removeRepeatedForeignMessage(0);
+ assertEquals(emptyList(), builder.getRepeatedForeignMessageList());
+ assertEquals(
+ singletonList(foreignMessage),
+ messageAfterBuild.getRepeatedForeignMessageList());
+
+ message = builder.build();
+ builder.addRepeatedGroup(RepeatedGroup.getDefaultInstance());
+ assertEquals(emptyList(), message.getRepeatedGroupList());
+ assertEquals(
+ singletonList(RepeatedGroup.getDefaultInstance()),
+ builder.getRepeatedGroupList());
+ assertEquals(emptyList(), message.getRepeatedGroupList());
+ messageAfterBuild = builder.build();
+ builder.removeRepeatedGroup(0);
+ assertEquals(emptyList(), builder.getRepeatedGroupList());
+ assertEquals(
+ singletonList(RepeatedGroup.getDefaultInstance()),
+ messageAfterBuild.getRepeatedGroupList());
+
+ message = builder.build();
+ builder.addRepeatedInt32(1);
+ assertEquals(emptyList(), message.getRepeatedInt32List());
+ assertEquals(singletonList(1), builder.getRepeatedInt32List());
+ assertEquals(emptyList(), message.getRepeatedInt32List());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedInt32();
+ assertEquals(emptyList(), builder.getRepeatedInt32List());
+ assertEquals(singletonList(1), messageAfterBuild.getRepeatedInt32List());
+
+ message = builder.build();
+ builder.addRepeatedInt64(1L);
+ assertEquals(emptyList(), message.getRepeatedInt64List());
+ assertEquals(singletonList(1L), builder.getRepeatedInt64List());
+ assertEquals(emptyList(), message.getRepeatedInt64List());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedInt64();
+ assertEquals(emptyList(), builder.getRepeatedInt64List());
+ assertEquals(singletonList(1L), messageAfterBuild.getRepeatedInt64List());
+
+ message = builder.build();
+ builder.addRepeatedLazyMessage(nestedMessage);
+ assertEquals(emptyList(), message.getRepeatedLazyMessageList());
+ assertEquals(
+ singletonList(nestedMessage), builder.getRepeatedLazyMessageList());
+ assertEquals(emptyList(), message.getRepeatedLazyMessageList());
+ messageAfterBuild = builder.build();
+ builder.removeRepeatedLazyMessage(0);
+ assertEquals(emptyList(), builder.getRepeatedLazyMessageList());
+ assertEquals(
+ singletonList(nestedMessage),
+ messageAfterBuild.getRepeatedLazyMessageList());
+
+ message = builder.build();
+ builder.addRepeatedSfixed32(1);
+ assertEquals(emptyList(), message.getRepeatedSfixed32List());
+ assertEquals(singletonList(1), builder.getRepeatedSfixed32List());
+ assertEquals(emptyList(), message.getRepeatedSfixed32List());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedSfixed32();
+ assertEquals(emptyList(), builder.getRepeatedSfixed32List());
+ assertEquals(singletonList(1), messageAfterBuild.getRepeatedSfixed32List());
+
+ message = builder.build();
+ builder.addRepeatedSfixed64(1L);
+ assertEquals(emptyList(), message.getRepeatedSfixed64List());
+ assertEquals(singletonList(1L), builder.getRepeatedSfixed64List());
+ assertEquals(emptyList(), message.getRepeatedSfixed64List());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedSfixed64();
+ assertEquals(emptyList(), builder.getRepeatedSfixed64List());
+ assertEquals(
+ singletonList(1L), messageAfterBuild.getRepeatedSfixed64List());
+
+ message = builder.build();
+ builder.addRepeatedSint32(1);
+ assertEquals(emptyList(), message.getRepeatedSint32List());
+ assertEquals(singletonList(1), builder.getRepeatedSint32List());
+ assertEquals(emptyList(), message.getRepeatedSint32List());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedSint32();
+ assertEquals(emptyList(), builder.getRepeatedSint32List());
+ assertEquals(singletonList(1), messageAfterBuild.getRepeatedSint32List());
+
+ message = builder.build();
+ builder.addRepeatedSint64(1L);
+ assertEquals(emptyList(), message.getRepeatedSint64List());
+ assertEquals(singletonList(1L), builder.getRepeatedSint64List());
+ assertEquals(emptyList(), message.getRepeatedSint64List());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedSint64();
+ assertEquals(emptyList(), builder.getRepeatedSint64List());
+ assertEquals(singletonList(1L), messageAfterBuild.getRepeatedSint64List());
+
+ message = builder.build();
+ builder.addRepeatedString("hi");
+ assertEquals(emptyList(), message.getRepeatedStringList());
+ assertEquals(singletonList("hi"), builder.getRepeatedStringList());
+ assertEquals(emptyList(), message.getRepeatedStringList());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedString();
+ assertEquals(emptyList(), builder.getRepeatedStringList());
+ assertEquals(
+ singletonList("hi"), messageAfterBuild.getRepeatedStringList());
+
+ message = builder.build();
+ builder.addRepeatedStringPiece("hi");
+ assertEquals(emptyList(), message.getRepeatedStringPieceList());
+ assertEquals(singletonList("hi"), builder.getRepeatedStringPieceList());
+ assertEquals(emptyList(), message.getRepeatedStringPieceList());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedStringPiece();
+ assertEquals(emptyList(), builder.getRepeatedStringPieceList());
+ assertEquals(
+ singletonList("hi"), messageAfterBuild.getRepeatedStringPieceList());
+
+ message = builder.build();
+ builder.addRepeatedUint32(1);
+ assertEquals(emptyList(), message.getRepeatedUint32List());
+ assertEquals(singletonList(1), builder.getRepeatedUint32List());
+ assertEquals(emptyList(), message.getRepeatedUint32List());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedUint32();
+ assertEquals(emptyList(), builder.getRepeatedUint32List());
+ assertEquals(singletonList(1), messageAfterBuild.getRepeatedUint32List());
+
+ message = builder.build();
+ builder.addRepeatedUint64(1L);
+ assertEquals(emptyList(), message.getRepeatedUint64List());
+ assertEquals(singletonList(1L), builder.getRepeatedUint64List());
+ assertEquals(emptyList(), message.getRepeatedUint64List());
+ messageAfterBuild = builder.build();
+ builder.clearRepeatedUint64();
+ assertEquals(emptyList(), builder.getRepeatedUint64List());
+ assertEquals(singletonList(1L), messageAfterBuild.getRepeatedUint64List());
+
+ message = builder.build();
+ builder.addRepeatedBool(true);
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedBoolCount());
+ builder.setRepeatedBool(0, false);
+ assertEquals(true, messageAfterBuild.getRepeatedBool(0));
+ assertEquals(false, builder.getRepeatedBool(0));
+ builder.clearRepeatedBool();
+
+ message = builder.build();
+ builder.addRepeatedBytes(ByteString.copyFromUtf8("hi"));
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedBytesCount());
+ builder.setRepeatedBytes(0, ByteString.EMPTY);
+ assertEquals(
+ ByteString.copyFromUtf8("hi"), messageAfterBuild.getRepeatedBytes(0));
+ assertEquals(ByteString.EMPTY, builder.getRepeatedBytes(0));
+ builder.clearRepeatedBytes();
+
+ message = builder.build();
+ builder.addRepeatedCord("hi");
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedCordCount());
+ builder.setRepeatedCord(0, "");
+ assertEquals("hi", messageAfterBuild.getRepeatedCord(0));
+ assertEquals("", builder.getRepeatedCord(0));
+ builder.clearRepeatedCord();
+ message = builder.build();
+
+ builder.addRepeatedCordBytes(ByteString.copyFromUtf8("hi"));
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedCordCount());
+ builder.setRepeatedCord(0, "");
+ assertEquals(
+ ByteString.copyFromUtf8("hi"), messageAfterBuild.getRepeatedCordBytes(0));
+ assertEquals(ByteString.EMPTY, builder.getRepeatedCordBytes(0));
+ builder.clearRepeatedCord();
+
+ message = builder.build();
+ builder.addRepeatedDouble(1D);
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedDoubleCount());
+ builder.setRepeatedDouble(0, 0D);
+ assertEquals(1D, messageAfterBuild.getRepeatedDouble(0));
+ assertEquals(0D, builder.getRepeatedDouble(0));
+ builder.clearRepeatedDouble();
+
+ message = builder.build();
+ builder.addRepeatedFixed32(1);
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedFixed32Count());
+ builder.setRepeatedFixed32(0, 0);
+ assertEquals(1, messageAfterBuild.getRepeatedFixed32(0));
+ assertEquals(0, builder.getRepeatedFixed32(0));
+ builder.clearRepeatedFixed32();
+
+ message = builder.build();
+ builder.addRepeatedFixed64(1L);
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedFixed64Count());
+ builder.setRepeatedFixed64(0, 0L);
+ assertEquals(1L, messageAfterBuild.getRepeatedFixed64(0));
+ assertEquals(0L, builder.getRepeatedFixed64(0));
+ builder.clearRepeatedFixed64();
+
+ message = builder.build();
+ builder.addRepeatedFloat(1F);
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedFloatCount());
+ builder.setRepeatedFloat(0, 0F);
+ assertEquals(1F, messageAfterBuild.getRepeatedFloat(0));
+ assertEquals(0F, builder.getRepeatedFloat(0));
+ builder.clearRepeatedFloat();
+
+ message = builder.build();
+ builder.addRepeatedForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR);
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedForeignEnumCount());
+ builder.setRepeatedForeignEnum(0, ForeignEnumLite.FOREIGN_LITE_FOO);
+ assertEquals(
+ ForeignEnumLite.FOREIGN_LITE_BAR,
+ messageAfterBuild.getRepeatedForeignEnum(0));
+ assertEquals(
+ ForeignEnumLite.FOREIGN_LITE_FOO, builder.getRepeatedForeignEnum(0));
+ builder.clearRepeatedForeignEnum();
+
+ message = builder.build();
+ builder.addRepeatedForeignMessage(foreignMessage);
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedForeignMessageCount());
+ builder.setRepeatedForeignMessage(
+ 0, ForeignMessageLite.getDefaultInstance());
+ assertEquals(
+ foreignMessage, messageAfterBuild.getRepeatedForeignMessage(0));
+ assertEquals(
+ ForeignMessageLite.getDefaultInstance(),
+ builder.getRepeatedForeignMessage(0));
+ builder.clearRepeatedForeignMessage();
+
+ message = builder.build();
+ builder.addRepeatedForeignMessage(foreignMessageBuilder);
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedForeignMessageCount());
+ builder.setRepeatedForeignMessage(
+ 0, ForeignMessageLite.getDefaultInstance());
+ // LITE_RUNTIME doesn't implement equals so we compare on a property.
+ assertEquals(3, messageAfterBuild.getRepeatedForeignMessage(0).getC());
+ assertEquals(
+ ForeignMessageLite.getDefaultInstance(),
+ builder.getRepeatedForeignMessage(0));
+ builder.clearRepeatedForeignMessage();
+
+ message = builder.build();
+ builder.addRepeatedForeignMessage(0, foreignMessage);
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedForeignMessageCount());
+ builder.setRepeatedForeignMessage(0, foreignMessageBuilder);
+ assertEquals(
+ foreignMessage, messageAfterBuild.getRepeatedForeignMessage(0));
+ // LITE_RUNTIME doesn't implement equals so we compare on a property.
+ assertEquals(3, builder.getRepeatedForeignMessage(0).getC());
+ builder.clearRepeatedForeignMessage();
+
+ message = builder.build();
+ RepeatedGroup repeatedGroup = RepeatedGroup.newBuilder()
+ .setA(1)
+ .build();
+ builder.addRepeatedGroup(repeatedGroup);
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedGroupCount());
+ builder.setRepeatedGroup(0, RepeatedGroup.getDefaultInstance());
+ assertEquals(repeatedGroup, messageAfterBuild.getRepeatedGroup(0));
+ assertEquals(
+ RepeatedGroup.getDefaultInstance(), builder.getRepeatedGroup(0));
+ builder.clearRepeatedGroup();
+
+ message = builder.build();
+ builder.addRepeatedGroup(0, repeatedGroup);
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedGroupCount());
+ builder.setRepeatedGroup(0, RepeatedGroup.getDefaultInstance());
+ assertEquals(repeatedGroup, messageAfterBuild.getRepeatedGroup(0));
+ assertEquals(
+ RepeatedGroup.getDefaultInstance(), builder.getRepeatedGroup(0));
+ builder.clearRepeatedGroup();
+
+ message = builder.build();
+ RepeatedGroup.Builder repeatedGroupBuilder = RepeatedGroup.newBuilder()
+ .setA(3);
+ builder.addRepeatedGroup(repeatedGroupBuilder);
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedGroupCount());
+ builder.setRepeatedGroup(0, RepeatedGroup.getDefaultInstance());
+ // LITE_RUNTIME doesn't implement equals so we compare on a property and
+ // ensure the property isn't set on repeatedGroup.
+ assertEquals(3, messageAfterBuild.getRepeatedGroup(0).getA());
+ assertEquals(
+ RepeatedGroup.getDefaultInstance(), builder.getRepeatedGroup(0));
+ builder.clearRepeatedGroup();
+
+ message = builder.build();
+ builder.addRepeatedGroup(0, repeatedGroupBuilder);
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedGroupCount());
+ builder.setRepeatedGroup(0, RepeatedGroup.getDefaultInstance());
+ // LITE_RUNTIME doesn't implement equals so we compare on a property and
+ // ensure the property isn't set on repeatedGroup.
+ assertEquals(3, messageAfterBuild.getRepeatedGroup(0).getA());
+ assertEquals(
+ RepeatedGroup.getDefaultInstance(), builder.getRepeatedGroup(0));
+ builder.clearRepeatedGroup();
+
+ message = builder.build();
+ builder.addRepeatedInt32(1);
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedInt32Count());
+ builder.setRepeatedInt32(0, 0);
+ assertEquals(1, messageAfterBuild.getRepeatedInt32(0));
+ assertEquals(0, builder.getRepeatedInt32(0));
+ builder.clearRepeatedInt32();
+
+ message = builder.build();
+ builder.addRepeatedInt64(1L);
+ messageAfterBuild = builder.build();
+ assertEquals(0L, message.getRepeatedInt64Count());
+ builder.setRepeatedInt64(0, 0L);
+ assertEquals(1L, messageAfterBuild.getRepeatedInt64(0));
+ assertEquals(0L, builder.getRepeatedInt64(0));
+ builder.clearRepeatedInt64();
+
+ message = builder.build();
+ builder.addRepeatedLazyMessage(nestedMessage);
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedLazyMessageCount());
+ builder.setRepeatedLazyMessage(0, NestedMessage.getDefaultInstance());
+ assertEquals(nestedMessage, messageAfterBuild.getRepeatedLazyMessage(0));
+ assertEquals(
+ NestedMessage.getDefaultInstance(), builder.getRepeatedLazyMessage(0));
+ builder.clearRepeatedLazyMessage();
+
+ message = builder.build();
+ builder.addRepeatedLazyMessage(0, nestedMessage);
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedLazyMessageCount());
+ builder.setRepeatedLazyMessage(0, NestedMessage.getDefaultInstance());
+ assertEquals(nestedMessage, messageAfterBuild.getRepeatedLazyMessage(0));
+ assertEquals(
+ NestedMessage.getDefaultInstance(), builder.getRepeatedLazyMessage(0));
+ builder.clearRepeatedLazyMessage();
+
+ message = builder.build();
+ builder.addRepeatedLazyMessage(nestedMessageBuilder);
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedLazyMessageCount());
+ builder.setRepeatedLazyMessage(0, NestedMessage.getDefaultInstance());
+ // LITE_RUNTIME doesn't implement equals so we compare on a property and
+ // ensure the property isn't set on repeatedGroup.
+ assertEquals(3, messageAfterBuild.getRepeatedLazyMessage(0).getBb());
+ assertEquals(
+ NestedMessage.getDefaultInstance(), builder.getRepeatedLazyMessage(0));
+ builder.clearRepeatedLazyMessage();
+
+ message = builder.build();
+ builder.addRepeatedLazyMessage(0, nestedMessageBuilder);
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedLazyMessageCount());
+ builder.setRepeatedLazyMessage(0, NestedMessage.getDefaultInstance());
+ // LITE_RUNTIME doesn't implement equals so we compare on a property and
+ // ensure the property isn't set on repeatedGroup.
+ assertEquals(3, messageAfterBuild.getRepeatedLazyMessage(0).getBb());
+ assertEquals(
+ NestedMessage.getDefaultInstance(), builder.getRepeatedLazyMessage(0));
+ builder.clearRepeatedLazyMessage();
+
+ message = builder.build();
+ builder.addRepeatedSfixed32(1);
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedSfixed32Count());
+ builder.setRepeatedSfixed32(0, 0);
+ assertEquals(1, messageAfterBuild.getRepeatedSfixed32(0));
+ assertEquals(0, builder.getRepeatedSfixed32(0));
+ builder.clearRepeatedSfixed32();
+
+ message = builder.build();
+ builder.addRepeatedSfixed64(1L);
+ messageAfterBuild = builder.build();
+ assertEquals(0L, message.getRepeatedSfixed64Count());
+ builder.setRepeatedSfixed64(0, 0L);
+ assertEquals(1L, messageAfterBuild.getRepeatedSfixed64(0));
+ assertEquals(0L, builder.getRepeatedSfixed64(0));
+ builder.clearRepeatedSfixed64();
+
+ message = builder.build();
+ builder.addRepeatedSint32(1);
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedSint32Count());
+ builder.setRepeatedSint32(0, 0);
+ assertEquals(1, messageAfterBuild.getRepeatedSint32(0));
+ assertEquals(0, builder.getRepeatedSint32(0));
+ builder.clearRepeatedSint32();
+
+ message = builder.build();
+ builder.addRepeatedSint64(1L);
+ messageAfterBuild = builder.build();
+ assertEquals(0L, message.getRepeatedSint64Count());
+ builder.setRepeatedSint64(0, 0L);
+ assertEquals(1L, messageAfterBuild.getRepeatedSint64(0));
+ assertEquals(0L, builder.getRepeatedSint64(0));
+ builder.clearRepeatedSint64();
+
+ message = builder.build();
+ builder.addRepeatedString("hi");
+ messageAfterBuild = builder.build();
+ assertEquals(0L, message.getRepeatedStringCount());
+ builder.setRepeatedString(0, "");
+ assertEquals("hi", messageAfterBuild.getRepeatedString(0));
+ assertEquals("", builder.getRepeatedString(0));
+ builder.clearRepeatedString();
+
+ message = builder.build();
+ builder.addRepeatedStringBytes(ByteString.copyFromUtf8("hi"));
+ messageAfterBuild = builder.build();
+ assertEquals(0L, message.getRepeatedStringCount());
+ builder.setRepeatedString(0, "");
+ assertEquals(
+ ByteString.copyFromUtf8("hi"),
+ messageAfterBuild.getRepeatedStringBytes(0));
+ assertEquals(ByteString.EMPTY, builder.getRepeatedStringBytes(0));
+ builder.clearRepeatedString();
+
+ message = builder.build();
+ builder.addRepeatedStringPiece("hi");
+ messageAfterBuild = builder.build();
+ assertEquals(0L, message.getRepeatedStringPieceCount());
+ builder.setRepeatedStringPiece(0, "");
+ assertEquals("hi", messageAfterBuild.getRepeatedStringPiece(0));
+ assertEquals("", builder.getRepeatedStringPiece(0));
+ builder.clearRepeatedStringPiece();
+
+ message = builder.build();
+ builder.addRepeatedStringPieceBytes(ByteString.copyFromUtf8("hi"));
+ messageAfterBuild = builder.build();
+ assertEquals(0L, message.getRepeatedStringPieceCount());
+ builder.setRepeatedStringPiece(0, "");
+ assertEquals(
+ ByteString.copyFromUtf8("hi"),
+ messageAfterBuild.getRepeatedStringPieceBytes(0));
+ assertEquals(ByteString.EMPTY, builder.getRepeatedStringPieceBytes(0));
+ builder.clearRepeatedStringPiece();
+
+ message = builder.build();
+ builder.addRepeatedUint32(1);
+ messageAfterBuild = builder.build();
+ assertEquals(0, message.getRepeatedUint32Count());
+ builder.setRepeatedUint32(0, 0);
+ assertEquals(1, messageAfterBuild.getRepeatedUint32(0));
+ assertEquals(0, builder.getRepeatedUint32(0));
+ builder.clearRepeatedUint32();
+
+ message = builder.build();
+ builder.addRepeatedUint64(1L);
+ messageAfterBuild = builder.build();
+ assertEquals(0L, message.getRepeatedUint64Count());
+ builder.setRepeatedUint64(0, 0L);
+ assertEquals(1L, messageAfterBuild.getRepeatedUint64(0));
+ assertEquals(0L, builder.getRepeatedUint64(0));
+ builder.clearRepeatedUint64();
+
+ message = builder.build();
+ assertEquals(0, message.getSerializedSize());
+ builder.mergeFrom(TestAllTypesLite.newBuilder()
+ .setOptionalBool(true)
+ .build());
+ assertEquals(0, message.getSerializedSize());
+ assertEquals(true, builder.build().getOptionalBool());
+ builder.clearOptionalBool();
+
+ message = builder.build();
+ assertEquals(0, message.getSerializedSize());
+ builder.mergeFrom(TestAllTypesLite.newBuilder()
+ .setOptionalBool(true)
+ .build());
+ assertEquals(0, message.getSerializedSize());
+ assertEquals(true, builder.build().getOptionalBool());
+ builder.clear();
+ assertEquals(0, builder.build().getSerializedSize());
+
+ message = builder.build();
+ assertEquals(0, message.getSerializedSize());
+ builder.mergeOptionalForeignMessage(foreignMessage);
+ assertEquals(0, message.getSerializedSize());
+ assertEquals(
+ foreignMessage.getC(),
+ builder.build().getOptionalForeignMessage().getC());
+ builder.clearOptionalForeignMessage();
+
+ message = builder.build();
+ assertEquals(0, message.getSerializedSize());
+ builder.mergeOptionalLazyMessage(nestedMessage);
+ assertEquals(0, message.getSerializedSize());
+ assertEquals(
+ nestedMessage.getBb(),
+ builder.build().getOptionalLazyMessage().getBb());
+ builder.clearOptionalLazyMessage();
+
+ message = builder.build();
+ builder.setOneofString("hi");
+ assertEquals(
+ OneofFieldCase.ONEOFFIELD_NOT_SET, message.getOneofFieldCase());
+ assertEquals(OneofFieldCase.ONEOF_STRING, builder.getOneofFieldCase());
+ assertEquals("hi", builder.getOneofString());
+ messageAfterBuild = builder.build();
+ assertEquals(
+ OneofFieldCase.ONEOF_STRING, messageAfterBuild.getOneofFieldCase());
+ assertEquals("hi", messageAfterBuild.getOneofString());
+ builder.setOneofUint32(1);
+ assertEquals(
+ OneofFieldCase.ONEOF_STRING, messageAfterBuild.getOneofFieldCase());
+ assertEquals("hi", messageAfterBuild.getOneofString());
+ assertEquals(OneofFieldCase.ONEOF_UINT32, builder.getOneofFieldCase());
+ assertEquals(1, builder.getOneofUint32());
+
+ TestAllExtensionsLite.Builder extendableMessageBuilder =
+ TestAllExtensionsLite.newBuilder();
+ TestAllExtensionsLite extendableMessage = extendableMessageBuilder.build();
+ extendableMessageBuilder.setExtension(
+ UnittestLite.optionalInt32ExtensionLite, 1);
+ assertFalse(extendableMessage.hasExtension(
+ UnittestLite.optionalInt32ExtensionLite));
+ extendableMessage = extendableMessageBuilder.build();
+ assertEquals(
+ 1, (int) extendableMessageBuilder.getExtension(
+ UnittestLite.optionalInt32ExtensionLite));
+ assertEquals(
+ 1, (int) extendableMessage.getExtension(
+ UnittestLite.optionalInt32ExtensionLite));
+ extendableMessageBuilder.setExtension(
+ UnittestLite.optionalInt32ExtensionLite, 3);
+ assertEquals(
+ 3, (int) extendableMessageBuilder.getExtension(
+ UnittestLite.optionalInt32ExtensionLite));
+ assertEquals(
+ 1, (int) extendableMessage.getExtension(
+ UnittestLite.optionalInt32ExtensionLite));
+ extendableMessage = extendableMessageBuilder.build();
+ assertEquals(
+ 3, (int) extendableMessageBuilder.getExtension(
+ UnittestLite.optionalInt32ExtensionLite));
+ assertEquals(
+ 3, (int) extendableMessage.getExtension(
+ UnittestLite.optionalInt32ExtensionLite));
+
+ // No extension registry, so it should be in unknown fields.
+ extendableMessage =
+ TestAllExtensionsLite.parseFrom(extendableMessage.toByteArray());
+ assertFalse(extendableMessage.hasExtension(
+ UnittestLite.optionalInt32ExtensionLite));
+
+ extendableMessageBuilder = extendableMessage.toBuilder();
+ extendableMessageBuilder.mergeFrom(TestAllExtensionsLite.newBuilder()
+ .setExtension(UnittestLite.optionalFixed32ExtensionLite, 11)
+ .build());
+
+ extendableMessage = extendableMessageBuilder.build();
+ ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance();
+ UnittestLite.registerAllExtensions(registry);
+ extendableMessage = TestAllExtensionsLite.parseFrom(
+ extendableMessage.toByteArray(), registry);
+
+ // The unknown field was preserved.
+ assertEquals(
+ 3, (int) extendableMessage.getExtension(
+ UnittestLite.optionalInt32ExtensionLite));
+ assertEquals(
+ 11, (int) extendableMessage.getExtension(
+ UnittestLite.optionalFixed32ExtensionLite));
}
}
diff --git a/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java b/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java
index ff39ca3f..958b6a7e 100644
--- a/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java
+++ b/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java
@@ -248,7 +248,7 @@ public class LiteralByteStringTest extends TestCase {
assertTrue(classUnderTest + ".writeTo() must give back the same bytes",
Arrays.equals(referenceBytes, roundTripBytes));
}
-
+
public void testWriteTo_mutating() throws IOException {
OutputStream os = new OutputStream() {
@Override
@@ -289,16 +289,44 @@ public class LiteralByteStringTest extends TestCase {
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));
+ LiteralByteString unicode = new LiteralByteString(testString.getBytes(Internal.UTF_8));
String roundTripString = unicode.toString(UTF_8);
assertEquals(classUnderTest + " unicode must match", testString, roundTripString);
}
+ public void testCharsetToString() throws UnsupportedEncodingException {
+ String testString = "I love unicode \u1234\u5678 characters";
+ LiteralByteString unicode = new LiteralByteString(testString.getBytes(Internal.UTF_8));
+ String roundTripString = unicode.toString(Internal.UTF_8);
+ assertEquals(classUnderTest + " unicode must match", testString, roundTripString);
+ }
+
+ public void testToString_returnsCanonicalEmptyString() throws UnsupportedEncodingException{
+ assertSame(classUnderTest + " must be the same string references",
+ ByteString.EMPTY.toString(Internal.UTF_8),
+ new LiteralByteString(new byte[]{}).toString(Internal.UTF_8));
+ }
+
+ public void testToString_raisesException() throws UnsupportedEncodingException{
+ try {
+ ByteString.EMPTY.toString("invalid");
+ fail("Should have thrown an exception.");
+ } catch (UnsupportedEncodingException expected) {
+ // This is success
+ }
+
+ try {
+ new LiteralByteString(referenceBytes).toString("invalid");
+ fail("Should have thrown an exception.");
+ } catch (UnsupportedEncodingException expected) {
+ // This is success
+ }
+ }
+
public void testEquals() {
assertEquals(classUnderTest + " must not equal null", false, stringUnderTest.equals(null));
assertEquals(classUnderTest + " must equal self", stringUnderTest, stringUnderTest);
@@ -311,7 +339,7 @@ public class LiteralByteStringTest extends TestCase {
byte[] mungedBytes = new byte[referenceBytes.length];
System.arraycopy(referenceBytes, 0, mungedBytes, 0, referenceBytes.length);
- mungedBytes[mungedBytes.length - 5] ^= 0xFF;
+ mungedBytes[mungedBytes.length - 5] = (byte) (mungedBytes[mungedBytes.length - 5] ^ 0xFF);
assertFalse(classUnderTest + " must not equal every string with the same length",
stringUnderTest.equals(new LiteralByteString(mungedBytes)));
}
diff --git a/java/src/test/java/com/google/protobuf/LongArrayListTest.java b/java/src/test/java/com/google/protobuf/LongArrayListTest.java
new file mode 100644
index 00000000..3a52ec7f
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/LongArrayListTest.java
@@ -0,0 +1,473 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import static java.util.Arrays.asList;
+
+import junit.framework.TestCase;
+
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+
+/**
+ * Tests for {@link LongArrayList}.
+ *
+ * @author dweis@google.com (Daniel Weis)
+ */
+public class LongArrayListTest extends TestCase {
+
+ private static final LongArrayList UNARY_LIST = newImmutableLongArrayList(1);
+ private static final LongArrayList TERTIARY_LIST =
+ newImmutableLongArrayList(1, 2, 3);
+
+ private LongArrayList list;
+
+ @Override
+ protected void setUp() throws Exception {
+ list = new LongArrayList();
+ }
+
+ public void testEmptyListReturnsSameInstance() {
+ assertSame(LongArrayList.emptyList(), LongArrayList.emptyList());
+ }
+
+ public void testEmptyListIsImmutable() {
+ assertImmutable(LongArrayList.emptyList());
+ }
+
+ public void testMakeImmutable() {
+ list.addLong(2);
+ list.addLong(4);
+ list.addLong(6);
+ list.addLong(8);
+ list.makeImmutable();
+ assertImmutable(list);
+ }
+
+ public void testCopyConstructor() {
+ LongArrayList copy = new LongArrayList(TERTIARY_LIST);
+ assertEquals(TERTIARY_LIST, copy);
+
+ copy = new LongArrayList(LongArrayList.emptyList());
+ assertEquals(LongArrayList.emptyList(), copy);
+
+ copy = new LongArrayList(asList(1L, 2L, 3L));
+ assertEquals(asList(1L, 2L, 3L), copy);
+
+ copy = new LongArrayList(Collections.<Long>emptyList());
+ assertEquals(LongArrayList.emptyList(), copy);
+ }
+
+ public void testModificationWithIteration() {
+ list.addAll(asList(1L, 2L, 3L, 4L));
+ Iterator<Long> iterator = list.iterator();
+ assertEquals(4, list.size());
+ assertEquals(1, (long) list.get(0));
+ assertEquals(1, (long) iterator.next());
+ list.set(0, 1L);
+ assertEquals(2, (long) iterator.next());
+
+ list.remove(0);
+ try {
+ iterator.next();
+ fail();
+ } catch (ConcurrentModificationException e) {
+ // expected
+ }
+
+ iterator = list.iterator();
+ list.add(0, 0L);
+ try {
+ iterator.next();
+ fail();
+ } catch (ConcurrentModificationException e) {
+ // expected
+ }
+ }
+
+ public void testGet() {
+ assertEquals(1, (long) TERTIARY_LIST.get(0));
+ assertEquals(2, (long) TERTIARY_LIST.get(1));
+ assertEquals(3, (long) TERTIARY_LIST.get(2));
+
+ try {
+ TERTIARY_LIST.get(-1);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ TERTIARY_LIST.get(3);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testGetLong() {
+ assertEquals(1, TERTIARY_LIST.getLong(0));
+ assertEquals(2, TERTIARY_LIST.getLong(1));
+ assertEquals(3, TERTIARY_LIST.getLong(2));
+
+ try {
+ TERTIARY_LIST.get(-1);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ TERTIARY_LIST.get(3);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testSize() {
+ assertEquals(0, LongArrayList.emptyList().size());
+ assertEquals(1, UNARY_LIST.size());
+ assertEquals(3, TERTIARY_LIST.size());
+
+ list.addLong(2);
+ list.addLong(4);
+ list.addLong(6);
+ list.addLong(8);
+ assertEquals(4, list.size());
+
+ list.remove(0);
+ assertEquals(3, list.size());
+
+ list.add(16L);
+ assertEquals(4, list.size());
+ }
+
+ public void testSet() {
+ list.addLong(2);
+ list.addLong(4);
+
+ assertEquals(2, (long) list.set(0, 0L));
+ assertEquals(0, list.getLong(0));
+
+ assertEquals(4, (long) list.set(1, 0L));
+ assertEquals(0, list.getLong(1));
+
+ try {
+ list.set(-1, 0L);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ list.set(2, 0L);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testSetLong() {
+ list.addLong(2);
+ list.addLong(4);
+
+ assertEquals(2, list.setLong(0, 0));
+ assertEquals(0, list.getLong(0));
+
+ assertEquals(4, list.setLong(1, 0));
+ assertEquals(0, list.getLong(1));
+
+ try {
+ list.setLong(-1, 0);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ list.setLong(2, 0);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testAdd() {
+ assertEquals(0, list.size());
+
+ assertTrue(list.add(2L));
+ assertEquals(asList(2L), list);
+
+ assertTrue(list.add(3L));
+ list.add(0, 4L);
+ assertEquals(asList(4L, 2L, 3L), list);
+
+ list.add(0, 1L);
+ list.add(0, 0L);
+ // Force a resize by getting up to 11 elements.
+ for (int i = 0; i < 6; i++) {
+ list.add(Long.valueOf(5 + i));
+ }
+ assertEquals(asList(0L, 1L, 4L, 2L, 3L, 5L, 6L, 7L, 8L, 9L, 10L), list);
+
+ try {
+ list.add(-1, 5L);
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ list.add(4, 5L);
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ public void testAddLong() {
+ assertEquals(0, list.size());
+
+ list.addLong(2);
+ assertEquals(asList(2L), list);
+
+ list.addLong(3);
+ assertEquals(asList(2L, 3L), list);
+ }
+
+ public void testAddAll() {
+ assertEquals(0, list.size());
+
+ assertTrue(list.addAll(Collections.singleton(1L)));
+ assertEquals(1, list.size());
+ assertEquals(1, (long) list.get(0));
+ assertEquals(1, list.getLong(0));
+
+ assertTrue(list.addAll(asList(2L, 3L, 4L, 5L, 6L)));
+ assertEquals(asList(1L, 2L, 3L, 4L, 5L, 6L), list);
+
+ assertTrue(list.addAll(TERTIARY_LIST));
+ assertEquals(asList(1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L), list);
+
+ assertFalse(list.addAll(Collections.<Long>emptyList()));
+ assertFalse(list.addAll(LongArrayList.emptyList()));
+ }
+
+ public void testRemove() {
+ list.addAll(TERTIARY_LIST);
+ assertEquals(1, (long) list.remove(0));
+ assertEquals(asList(2L, 3L), list);
+
+ assertTrue(list.remove(3L));
+ assertEquals(asList(2L), list);
+
+ assertFalse(list.remove(3L));
+ assertEquals(asList(2L), list);
+
+ assertEquals(2, (long) list.remove(0));
+ assertEquals(asList(), list);
+
+ try {
+ list.remove(-1);
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ try {
+ list.remove(0);
+ } catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+ }
+
+ private void assertImmutable(LongArrayList list) {
+ if (list.contains(1)) {
+ throw new RuntimeException("Cannot test the immutability of lists that contain 1.");
+ }
+
+ try {
+ list.add(1L);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.add(0, 1L);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(Collections.<Long>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(Collections.singletonList(1L));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(new LongArrayList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(0, Collections.singleton(1L));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(0, UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(0, Collections.<Long>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addLong(0);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.clear();
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.remove(1);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.remove(new Object());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.removeAll(Collections.<Long>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.removeAll(Collections.singleton(1));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.removeAll(UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.retainAll(Collections.<Long>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.retainAll(Collections.singleton(1));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.retainAll(UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.set(0, 0L);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.setLong(0, 0);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ }
+
+ private static LongArrayList newImmutableLongArrayList(long... elements) {
+ LongArrayList list = new LongArrayList();
+ for (long element : elements) {
+ list.addLong(element);
+ }
+ list.makeImmutable();
+ return list;
+ }
+}
diff --git a/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java b/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java
index 22dbeb76..6cff689f 100644
--- a/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java
+++ b/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java
@@ -36,6 +36,11 @@ import map_lite_test.MapForProto2TestProto.TestUnknownEnumValue;
import junit.framework.TestCase;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* Unit tests for map fields.
*/
@@ -170,6 +175,140 @@ public class MapForProto2LiteTest extends TestCase {
assertEquals(0, message.getStringToInt32Field().size());
}
+ public void testSanityCopyOnWrite() throws InvalidProtocolBufferException {
+ // Since builders are implemented as a thin wrapper around a message
+ // instance, we attempt to verify that we can't cause the builder to modify
+ // a produced message.
+
+ TestMap.Builder builder = TestMap.newBuilder();
+ TestMap message = builder.build();
+ Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field();
+ intMap.put(1, 2);
+ assertTrue(message.getInt32ToInt32Field().isEmpty());
+ message = builder.build();
+ try {
+ intMap.put(2, 3);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ assertEquals(newMap(1, 2), message.getInt32ToInt32Field());
+ assertEquals(newMap(1, 2), builder.getInt32ToInt32Field());
+ builder.getMutableInt32ToInt32Field().put(2, 3);
+ assertEquals(newMap(1, 2), message.getInt32ToInt32Field());
+ assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field());
+ }
+
+ public void testMutableMapLifecycle() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field();
+ intMap.put(1, 2);
+ assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
+ try {
+ intMap.put(2, 3);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ assertEquals(newMap(1, 2), builder.getInt32ToInt32Field());
+ builder.getMutableInt32ToInt32Field().put(2, 3);
+ assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field());
+
+ Map<Integer, TestMap.EnumValue> enumMap = builder.getMutableInt32ToEnumField();
+ enumMap.put(1, TestMap.EnumValue.BAR);
+ assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.build().getInt32ToEnumField());
+ try {
+ enumMap.put(2, TestMap.EnumValue.FOO);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.getInt32ToEnumField());
+ builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.FOO);
+ assertEquals(
+ newMap(1, TestMap.EnumValue.BAR, 2, TestMap.EnumValue.FOO),
+ builder.getInt32ToEnumField());
+
+ Map<Integer, String> stringMap = builder.getMutableInt32ToStringField();
+ stringMap.put(1, "1");
+ assertEquals(newMap(1, "1"), builder.build().getInt32ToStringField());
+ try {
+ stringMap.put(2, "2");
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ assertEquals(newMap(1, "1"), builder.getInt32ToStringField());
+ builder.getMutableInt32ToStringField().put(2, "2");
+ assertEquals(
+ newMap(1, "1", 2, "2"),
+ builder.getInt32ToStringField());
+
+ Map<Integer, TestMap.MessageValue> messageMap = builder.getMutableInt32ToMessageField();
+ messageMap.put(1, TestMap.MessageValue.getDefaultInstance());
+ assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()),
+ builder.build().getInt32ToMessageField());
+ try {
+ messageMap.put(2, TestMap.MessageValue.getDefaultInstance());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()),
+ builder.getInt32ToMessageField());
+ builder.getMutableInt32ToMessageField().put(2, TestMap.MessageValue.getDefaultInstance());
+ assertEquals(
+ newMap(1, TestMap.MessageValue.getDefaultInstance(),
+ 2, TestMap.MessageValue.getDefaultInstance()),
+ builder.getInt32ToMessageField());
+ }
+
+ public void testMutableMapLifecycle_collections() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field();
+ intMap.put(1, 2);
+ assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
+ try {
+ intMap.remove(2);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ try {
+ intMap.entrySet().remove(new Object());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ try {
+ intMap.entrySet().iterator().remove();
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ try {
+ intMap.keySet().remove(new Object());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ try {
+ intMap.values().remove(new Object());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ try {
+ intMap.values().iterator().remove();
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ assertEquals(newMap(1, 2), intMap);
+ assertEquals(newMap(1, 2), builder.getInt32ToInt32Field());
+ assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
+ }
+
public void testGettersAndSetters() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
TestMap message = builder.build();
@@ -274,4 +413,26 @@ public class MapForProto2LiteTest extends TestCase {
assertEquals(54321, messageWithUnknownEnums.getInt32ToInt32Field().get(2).intValue());
}
+
+ public void testIterationOrder() throws Exception {
+ TestMap.Builder builder = TestMap.newBuilder();
+ setMapValues(builder);
+ TestMap message = builder.build();
+
+ assertEquals(Arrays.asList("1", "2", "3"),
+ new ArrayList<String>(message.getStringToInt32Field().keySet()));
+ }
+
+ private static <K, V> Map<K, V> newMap(K key1, V value1) {
+ Map<K, V> map = new HashMap<K, V>();
+ map.put(key1, value1);
+ return map;
+ }
+
+ private static <K, V> Map<K, V> newMap(K key1, V value1, K key2, V value2) {
+ Map<K, V> map = new HashMap<K, V>();
+ map.put(key1, value1);
+ map.put(key2, value2);
+ return map;
+ }
}
diff --git a/java/src/test/java/com/google/protobuf/MapForProto2Test.java b/java/src/test/java/com/google/protobuf/MapForProto2Test.java
index 33ba7150..7e984040 100644
--- a/java/src/test/java/com/google/protobuf/MapForProto2Test.java
+++ b/java/src/test/java/com/google/protobuf/MapForProto2Test.java
@@ -34,11 +34,13 @@ import com.google.protobuf.Descriptors.FieldDescriptor;
import map_test.MapForProto2TestProto.TestMap;
import map_test.MapForProto2TestProto.TestMap.MessageValue;
import map_test.MapForProto2TestProto.TestMap.MessageWithRequiredFields;
+import map_test.MapForProto2TestProto.TestRecursiveMap;
import map_test.MapForProto2TestProto.TestUnknownEnumValue;
import junit.framework.TestCase;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -176,6 +178,116 @@ public class MapForProto2Test extends TestCase {
assertEquals(0, message.getInt32ToMessageField().size());
assertEquals(0, message.getStringToInt32Field().size());
}
+
+ public void testMutableMapLifecycle() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field();
+ intMap.put(1, 2);
+ assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
+ try {
+ intMap.put(2, 3);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ assertEquals(newMap(1, 2), builder.getInt32ToInt32Field());
+ builder.getMutableInt32ToInt32Field().put(2, 3);
+ assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field());
+
+ Map<Integer, TestMap.EnumValue> enumMap = builder.getMutableInt32ToEnumField();
+ enumMap.put(1, TestMap.EnumValue.BAR);
+ assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.build().getInt32ToEnumField());
+ try {
+ enumMap.put(2, TestMap.EnumValue.FOO);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.getInt32ToEnumField());
+ builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.FOO);
+ assertEquals(
+ newMap(1, TestMap.EnumValue.BAR, 2, TestMap.EnumValue.FOO),
+ builder.getInt32ToEnumField());
+
+ Map<Integer, String> stringMap = builder.getMutableInt32ToStringField();
+ stringMap.put(1, "1");
+ assertEquals(newMap(1, "1"), builder.build().getInt32ToStringField());
+ try {
+ stringMap.put(2, "2");
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ assertEquals(newMap(1, "1"), builder.getInt32ToStringField());
+ builder.getMutableInt32ToStringField().put(2, "2");
+ assertEquals(
+ newMap(1, "1", 2, "2"),
+ builder.getInt32ToStringField());
+
+ Map<Integer, TestMap.MessageValue> messageMap = builder.getMutableInt32ToMessageField();
+ messageMap.put(1, TestMap.MessageValue.getDefaultInstance());
+ assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()),
+ builder.build().getInt32ToMessageField());
+ try {
+ messageMap.put(2, TestMap.MessageValue.getDefaultInstance());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()),
+ builder.getInt32ToMessageField());
+ builder.getMutableInt32ToMessageField().put(2, TestMap.MessageValue.getDefaultInstance());
+ assertEquals(
+ newMap(1, TestMap.MessageValue.getDefaultInstance(),
+ 2, TestMap.MessageValue.getDefaultInstance()),
+ builder.getInt32ToMessageField());
+ }
+
+ public void testMutableMapLifecycle_collections() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field();
+ intMap.put(1, 2);
+ assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
+ try {
+ intMap.remove(2);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ try {
+ intMap.entrySet().remove(new Object());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ try {
+ intMap.entrySet().iterator().remove();
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ try {
+ intMap.keySet().remove(new Object());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ try {
+ intMap.values().remove(new Object());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ try {
+ intMap.values().iterator().remove();
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ assertEquals(newMap(1, 2), intMap);
+ assertEquals(newMap(1, 2), builder.getInt32ToInt32Field());
+ assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
+ }
public void testGettersAndSetters() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
@@ -499,4 +611,54 @@ public class MapForProto2Test extends TestCase {
message = builder.build();
assertTrue(message.isInitialized());
}
+
+ public void testRecursiveMap() throws Exception {
+ TestRecursiveMap.Builder builder = TestRecursiveMap.newBuilder();
+ builder.getMutableRecursiveMapField().put(
+ 1, TestRecursiveMap.newBuilder().setValue(2).build());
+ builder.getMutableRecursiveMapField().put(
+ 3, TestRecursiveMap.newBuilder().setValue(4).build());
+ ByteString data = builder.build().toByteString();
+
+ TestRecursiveMap message = TestRecursiveMap.parseFrom(data);
+ assertEquals(2, message.getRecursiveMapField().get(1).getValue());
+ assertEquals(4, message.getRecursiveMapField().get(3).getValue());
+ }
+
+ public void testIterationOrder() throws Exception {
+ TestMap.Builder builder = TestMap.newBuilder();
+ setMapValues(builder);
+ TestMap message = builder.build();
+
+ assertEquals(Arrays.asList("1", "2", "3"),
+ new ArrayList<String>(message.getStringToInt32Field().keySet()));
+ }
+
+ // Regression test for b/20494788
+ public void testMapInitializationOrder() throws Exception {
+ assertEquals("RedactAllTypes", map_test.RedactAllTypes
+ .getDefaultInstance().getDescriptorForType().getName());
+
+ map_test.Message1.Builder builder =
+ map_test.Message1.newBuilder();
+ builder.getMutableMapField().put("key", true);
+ map_test.Message1 message = builder.build();
+ Message mapEntry = (Message) message.getRepeatedField(
+ message.getDescriptorForType().findFieldByName("map_field"), 0);
+ assertEquals(2, mapEntry.getAllFields().size());
+ }
+
+ private static <K, V> Map<K, V> newMap(K key1, V value1) {
+ Map<K, V> map = new HashMap<K, V>();
+ map.put(key1, value1);
+ return map;
+ }
+
+ private static <K, V> Map<K, V> newMap(K key1, V value1, K key2, V value2) {
+ Map<K, V> map = new HashMap<K, V>();
+ map.put(key1, value1);
+ map.put(key2, value2);
+ return map;
+ }
}
+
diff --git a/java/src/test/java/com/google/protobuf/MapTest.java b/java/src/test/java/com/google/protobuf/MapTest.java
index 9a25e302..0509be15 100644
--- a/java/src/test/java/com/google/protobuf/MapTest.java
+++ b/java/src/test/java/com/google/protobuf/MapTest.java
@@ -41,6 +41,7 @@ import map_test.MapTestProto.TestOnChangeEventPropagation;
import junit.framework.TestCase;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -57,22 +58,22 @@ public class MapTest extends TestCase {
builder.getMutableInt32ToStringField().put(1, "11");
builder.getMutableInt32ToStringField().put(2, "22");
builder.getMutableInt32ToStringField().put(3, "33");
-
+
builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("11"));
builder.getMutableInt32ToBytesField().put(2, TestUtil.toBytes("22"));
builder.getMutableInt32ToBytesField().put(3, TestUtil.toBytes("33"));
-
+
builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.FOO);
builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.BAR);
builder.getMutableInt32ToEnumField().put(3, TestMap.EnumValue.BAZ);
-
+
builder.getMutableInt32ToMessageField().put(
1, MessageValue.newBuilder().setValue(11).build());
builder.getMutableInt32ToMessageField().put(
2, MessageValue.newBuilder().setValue(22).build());
builder.getMutableInt32ToMessageField().put(
3, MessageValue.newBuilder().setValue(33).build());
-
+
builder.getMutableStringToInt32Field().put("1", 11);
builder.getMutableStringToInt32Field().put("2", 22);
builder.getMutableStringToInt32Field().put("3", 33);
@@ -88,22 +89,22 @@ public class MapTest extends TestCase {
assertEquals("11", message.getInt32ToStringField().get(1));
assertEquals("22", message.getInt32ToStringField().get(2));
assertEquals("33", message.getInt32ToStringField().get(3));
-
+
assertEquals(3, message.getInt32ToBytesField().size());
assertEquals(TestUtil.toBytes("11"), message.getInt32ToBytesField().get(1));
assertEquals(TestUtil.toBytes("22"), message.getInt32ToBytesField().get(2));
assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3));
-
+
assertEquals(3, message.getInt32ToEnumField().size());
assertEquals(TestMap.EnumValue.FOO, message.getInt32ToEnumField().get(1));
assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(2));
assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3));
-
+
assertEquals(3, message.getInt32ToMessageField().size());
assertEquals(11, message.getInt32ToMessageField().get(1).getValue());
assertEquals(22, message.getInt32ToMessageField().get(2).getValue());
assertEquals(33, message.getInt32ToMessageField().get(3).getValue());
-
+
assertEquals(3, message.getStringToInt32Field().size());
assertEquals(11, message.getStringToInt32Field().get("1").intValue());
assertEquals(22, message.getStringToInt32Field().get("2").intValue());
@@ -118,21 +119,21 @@ public class MapTest extends TestCase {
builder.getMutableInt32ToStringField().put(1, "111");
builder.getMutableInt32ToStringField().remove(2);
builder.getMutableInt32ToStringField().put(4, "44");
-
+
builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("111"));
builder.getMutableInt32ToBytesField().remove(2);
builder.getMutableInt32ToBytesField().put(4, TestUtil.toBytes("44"));
-
+
builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.BAR);
builder.getMutableInt32ToEnumField().remove(2);
builder.getMutableInt32ToEnumField().put(4, TestMap.EnumValue.QUX);
-
+
builder.getMutableInt32ToMessageField().put(
1, MessageValue.newBuilder().setValue(111).build());
builder.getMutableInt32ToMessageField().remove(2);
builder.getMutableInt32ToMessageField().put(
4, MessageValue.newBuilder().setValue(44).build());
-
+
builder.getMutableStringToInt32Field().put("1", 111);
builder.getMutableStringToInt32Field().remove("2");
builder.getMutableStringToInt32Field().put("4", 44);
@@ -148,22 +149,22 @@ public class MapTest extends TestCase {
assertEquals("111", message.getInt32ToStringField().get(1));
assertEquals("33", message.getInt32ToStringField().get(3));
assertEquals("44", message.getInt32ToStringField().get(4));
-
+
assertEquals(3, message.getInt32ToBytesField().size());
assertEquals(TestUtil.toBytes("111"), message.getInt32ToBytesField().get(1));
assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3));
assertEquals(TestUtil.toBytes("44"), message.getInt32ToBytesField().get(4));
-
+
assertEquals(3, message.getInt32ToEnumField().size());
assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(1));
assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3));
assertEquals(TestMap.EnumValue.QUX, message.getInt32ToEnumField().get(4));
-
+
assertEquals(3, message.getInt32ToMessageField().size());
assertEquals(111, message.getInt32ToMessageField().get(1).getValue());
assertEquals(33, message.getInt32ToMessageField().get(3).getValue());
assertEquals(44, message.getInt32ToMessageField().get(4).getValue());
-
+
assertEquals(3, message.getStringToInt32Field().size());
assertEquals(111, message.getStringToInt32Field().get("1").intValue());
assertEquals(33, message.getStringToInt32Field().get("3").intValue());
@@ -178,22 +179,132 @@ public class MapTest extends TestCase {
assertEquals(0, message.getInt32ToMessageField().size());
assertEquals(0, message.getStringToInt32Field().size());
}
+
+ public void testMutableMapLifecycle() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field();
+ intMap.put(1, 2);
+ assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
+ try {
+ intMap.put(2, 3);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ assertEquals(newMap(1, 2), builder.getInt32ToInt32Field());
+ builder.getMutableInt32ToInt32Field().put(2, 3);
+ assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field());
+
+ Map<Integer, TestMap.EnumValue> enumMap = builder.getMutableInt32ToEnumField();
+ enumMap.put(1, TestMap.EnumValue.BAR);
+ assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.build().getInt32ToEnumField());
+ try {
+ enumMap.put(2, TestMap.EnumValue.FOO);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.getInt32ToEnumField());
+ builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.FOO);
+ assertEquals(
+ newMap(1, TestMap.EnumValue.BAR, 2, TestMap.EnumValue.FOO),
+ builder.getInt32ToEnumField());
+
+ Map<Integer, String> stringMap = builder.getMutableInt32ToStringField();
+ stringMap.put(1, "1");
+ assertEquals(newMap(1, "1"), builder.build().getInt32ToStringField());
+ try {
+ stringMap.put(2, "2");
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ assertEquals(newMap(1, "1"), builder.getInt32ToStringField());
+ builder.getMutableInt32ToStringField().put(2, "2");
+ assertEquals(
+ newMap(1, "1", 2, "2"),
+ builder.getInt32ToStringField());
+
+ Map<Integer, TestMap.MessageValue> messageMap = builder.getMutableInt32ToMessageField();
+ messageMap.put(1, TestMap.MessageValue.getDefaultInstance());
+ assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()),
+ builder.build().getInt32ToMessageField());
+ try {
+ messageMap.put(2, TestMap.MessageValue.getDefaultInstance());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()),
+ builder.getInt32ToMessageField());
+ builder.getMutableInt32ToMessageField().put(2, TestMap.MessageValue.getDefaultInstance());
+ assertEquals(
+ newMap(1, TestMap.MessageValue.getDefaultInstance(),
+ 2, TestMap.MessageValue.getDefaultInstance()),
+ builder.getInt32ToMessageField());
+ }
+ public void testMutableMapLifecycle_collections() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field();
+ intMap.put(1, 2);
+ assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
+ try {
+ intMap.remove(2);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ try {
+ intMap.entrySet().remove(new Object());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ try {
+ intMap.entrySet().iterator().remove();
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ try {
+ intMap.keySet().remove(new Object());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ try {
+ intMap.values().remove(new Object());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ try {
+ intMap.values().iterator().remove();
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ assertEquals(newMap(1, 2), intMap);
+ assertEquals(newMap(1, 2), builder.getInt32ToInt32Field());
+ assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
+ }
+
public void testGettersAndSetters() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
TestMap message = builder.build();
assertMapValuesCleared(message);
-
+
builder = message.toBuilder();
setMapValues(builder);
message = builder.build();
assertMapValuesSet(message);
-
+
builder = message.toBuilder();
updateMapValues(builder);
message = builder.build();
assertMapValuesUpdated(message);
-
+
builder = message.toBuilder();
builder.clear();
message = builder.build();
@@ -207,14 +318,14 @@ public class MapTest extends TestCase {
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.PARSER.parseFrom(message.toByteString());
assertMapValuesSet(message);
-
+
builder = message.toBuilder();
updateMapValues(builder);
message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.PARSER.parseFrom(message.toByteString());
assertMapValuesUpdated(message);
-
+
builder = message.toBuilder();
builder.clear();
message = builder.build();
@@ -222,12 +333,12 @@ public class MapTest extends TestCase {
message = TestMap.PARSER.parseFrom(message.toByteString());
assertMapValuesCleared(message);
}
-
+
public void testMergeFrom() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
TestMap message = builder.build();
-
+
TestMap.Builder other = TestMap.newBuilder();
other.mergeFrom(message);
assertMapValuesSet(other.build());
@@ -236,7 +347,7 @@ public class MapTest extends TestCase {
public void testEqualsAndHashCode() throws Exception {
// Test that generated equals() and hashCode() will disregard the order
// of map entries when comparing/hashing map fields.
-
+
// We can't control the order of elements in a HashMap. The best we can do
// here is to add elements in different order.
TestMap.Builder b1 = TestMap.newBuilder();
@@ -244,23 +355,23 @@ public class MapTest extends TestCase {
b1.getMutableInt32ToInt32Field().put(3, 4);
b1.getMutableInt32ToInt32Field().put(5, 6);
TestMap m1 = b1.build();
-
+
TestMap.Builder b2 = TestMap.newBuilder();
b2.getMutableInt32ToInt32Field().put(5, 6);
b2.getMutableInt32ToInt32Field().put(1, 2);
b2.getMutableInt32ToInt32Field().put(3, 4);
TestMap m2 = b2.build();
-
+
assertEquals(m1, m2);
assertEquals(m1.hashCode(), m2.hashCode());
-
+
// Make sure we did compare map fields.
b2.getMutableInt32ToInt32Field().put(1, 0);
m2 = b2.build();
assertFalse(m1.equals(m2));
// Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed
// to be different.
-
+
// Regression test for b/18549190: if a map is a subset of the other map,
// equals() should return false.
b2.getMutableInt32ToInt32Field().remove(1);
@@ -268,57 +379,95 @@ public class MapTest extends TestCase {
assertFalse(m1.equals(m2));
assertFalse(m2.equals(m1));
}
-
-
+
public void testNestedBuilderOnChangeEventPropagation() {
TestOnChangeEventPropagation.Builder parent =
TestOnChangeEventPropagation.newBuilder();
parent.getOptionalMessageBuilder().getMutableInt32ToInt32Field().put(1, 2);
TestOnChangeEventPropagation message = parent.build();
assertEquals(2, message.getOptionalMessage().getInt32ToInt32Field().get(1).intValue());
-
+
// Make a change using nested builder.
parent.getOptionalMessageBuilder().getMutableInt32ToInt32Field().put(1, 3);
-
+
// Should be able to observe the change.
message = parent.build();
assertEquals(3, message.getOptionalMessage().getInt32ToInt32Field().get(1).intValue());
-
+
// Make another change using mergeFrom()
TestMap.Builder other = TestMap.newBuilder();
other.getMutableInt32ToInt32Field().put(1, 4);
parent.getOptionalMessageBuilder().mergeFrom(other.build());
-
+
// Should be able to observe the change.
message = parent.build();
assertEquals(4, message.getOptionalMessage().getInt32ToInt32Field().get(1).intValue());
-
+
// Make yet another change by clearing the nested builder.
parent.getOptionalMessageBuilder().clear();
-
+
// Should be able to observe the change.
message = parent.build();
assertEquals(0, message.getOptionalMessage().getInt32ToInt32Field().size());
}
-
+
+ public void testNestedBuilderOnChangeEventPropagationReflection() {
+ FieldDescriptor intMapField = f("int32_to_int32_field");
+ // Create an outer message builder with nested builder.
+ TestOnChangeEventPropagation.Builder parentBuilder =
+ TestOnChangeEventPropagation.newBuilder();
+ TestMap.Builder testMapBuilder = parentBuilder.getOptionalMessageBuilder();
+
+ // Create a map entry message.
+ TestMap.Builder entryBuilder = TestMap.newBuilder();
+ entryBuilder.getMutableInt32ToInt32Field().put(1, 1);
+
+ // Put the entry into the nested builder.
+ testMapBuilder.addRepeatedField(
+ intMapField, entryBuilder.getRepeatedField(intMapField, 0));
+
+ // Should be able to observe the change.
+ TestOnChangeEventPropagation message = parentBuilder.build();
+ assertEquals(1, message.getOptionalMessage().getInt32ToInt32Field().size());
+
+ // Change the entry value.
+ entryBuilder.getMutableInt32ToInt32Field().put(1, 4);
+ testMapBuilder = parentBuilder.getOptionalMessageBuilder();
+ testMapBuilder.setRepeatedField(
+ intMapField, 0, entryBuilder.getRepeatedField(intMapField, 0));
+
+ // Should be able to observe the change.
+ message = parentBuilder.build();
+ assertEquals(4,
+ message.getOptionalMessage().getInt32ToInt32Field().get(1).intValue());
+
+ // Clear the nested builder.
+ testMapBuilder = parentBuilder.getOptionalMessageBuilder();
+ testMapBuilder.clearField(intMapField);
+
+ // Should be able to observe the change.
+ message = parentBuilder.build();
+ assertEquals(0, message.getOptionalMessage().getInt32ToInt32Field().size());
+ }
+
// The following methods are used to test reflection API.
-
+
private static FieldDescriptor f(String name) {
return TestMap.getDescriptor().findFieldByName(name);
}
-
+
private static Object getFieldValue(Message mapEntry, String name) {
FieldDescriptor field = mapEntry.getDescriptorForType().findFieldByName(name);
return mapEntry.getField(field);
}
-
+
private static Message.Builder setFieldValue(
Message.Builder mapEntry, String name, Object value) {
FieldDescriptor field = mapEntry.getDescriptorForType().findFieldByName(name);
mapEntry.setField(field, value);
return mapEntry;
}
-
+
private static void assertHasMapValues(Message message, String name, Map<?, ?> values) {
FieldDescriptor field = f(name);
for (Object entry : (List<?>) message.getField(field)) {
@@ -337,7 +486,7 @@ public class MapTest extends TestCase {
assertEquals(value, values.get(key));
}
}
-
+
private static <KeyType, ValueType>
Message newMapEntry(Message.Builder builder, String name, KeyType key, ValueType value) {
FieldDescriptor field = builder.getDescriptorForType().findFieldByName(name);
@@ -348,7 +497,7 @@ public class MapTest extends TestCase {
entryBuilder.setField(valueField, value);
return entryBuilder.build();
}
-
+
private static void setMapValues(Message.Builder builder, String name, Map<?, ?> values) {
List<Message> entryList = new ArrayList<Message>();
for (Map.Entry<?, ?> entry : values.entrySet()) {
@@ -357,7 +506,7 @@ public class MapTest extends TestCase {
FieldDescriptor field = builder.getDescriptorForType().findFieldByName(name);
builder.setField(field, entryList);
}
-
+
private static <KeyType, ValueType>
Map<KeyType, ValueType> mapForValues(
KeyType key1, ValueType value1, KeyType key2, ValueType value2) {
@@ -385,14 +534,14 @@ public class MapTest extends TestCase {
mapForValues(
11, MessageValue.newBuilder().setValue(22).build(),
33, MessageValue.newBuilder().setValue(44).build()));
-
+
// Test clearField()
builder.clearField(f("int32_to_int32_field"));
builder.clearField(f("int32_to_message_field"));
message = builder.build();
assertEquals(0, message.getInt32ToInt32Field().size());
assertEquals(0, message.getInt32ToMessageField().size());
-
+
// Test setField()
setMapValues(builder, "int32_to_int32_field",
mapForValues(11, 22, 33, 44));
@@ -405,7 +554,7 @@ public class MapTest extends TestCase {
assertEquals(44, message.getInt32ToInt32Field().get(33).intValue());
assertEquals(222, message.getInt32ToMessageField().get(111).getValue());
assertEquals(444, message.getInt32ToMessageField().get(333).getValue());
-
+
// Test addRepeatedField
builder.addRepeatedField(f("int32_to_int32_field"),
newMapEntry(builder, "int32_to_int32_field", 55, 66));
@@ -425,7 +574,7 @@ public class MapTest extends TestCase {
message = builder.build();
assertEquals(55, message.getInt32ToInt32Field().get(55).intValue());
assertEquals(555, message.getInt32ToMessageField().get(555).getValue());
-
+
// Test setRepeatedField
for (int i = 0; i < builder.getRepeatedFieldCount(f("int32_to_int32_field")); i++) {
Message mapEntry = (Message) builder.getRepeatedField(f("int32_to_int32_field"), i);
@@ -442,35 +591,35 @@ public class MapTest extends TestCase {
assertEquals(33, message.getInt32ToInt32Field().get(44).intValue());
assertEquals(55, message.getInt32ToInt32Field().get(55).intValue());
}
-
+
public void testTextFormat() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
TestMap message = builder.build();
-
+
String textData = TextFormat.printToString(message);
-
+
builder = TestMap.newBuilder();
TextFormat.merge(textData, builder);
message = builder.build();
-
+
assertMapValuesSet(message);
}
-
+
public void testDynamicMessage() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
TestMap message = builder.build();
-
+
Message dynamicDefaultInstance =
DynamicMessage.getDefaultInstance(TestMap.getDescriptor());
Message dynamicMessage = dynamicDefaultInstance
.newBuilderForType().mergeFrom(message.toByteString()).build();
-
+
assertEquals(message, dynamicMessage);
assertEquals(message.hashCode(), dynamicMessage.hashCode());
}
-
+
public void testReflectionEqualsAndHashCode() throws Exception {
// Test that generated equals() and hashCode() will disregard the order
// of map entries when comparing/hashing map fields.
@@ -479,22 +628,22 @@ public class MapTest extends TestCase {
Message dynamicDefaultInstance =
DynamicMessage.getDefaultInstance(TestMap.getDescriptor());
FieldDescriptor field = f("int32_to_int32_field");
-
+
Message.Builder b1 = dynamicDefaultInstance.newBuilderForType();
b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 1, 2));
b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 3, 4));
b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 5, 6));
Message m1 = b1.build();
-
+
Message.Builder b2 = dynamicDefaultInstance.newBuilderForType();
b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 5, 6));
b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 1, 2));
b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 3, 4));
Message m2 = b2.build();
-
+
assertEquals(m1, m2);
assertEquals(m1.hashCode(), m2.hashCode());
-
+
// Make sure we did compare map fields.
b2.setRepeatedField(field, 0, newMapEntry(b1, "int32_to_int32_field", 0, 0));
m2 = b2.build();
@@ -502,7 +651,7 @@ public class MapTest extends TestCase {
// Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed
// to be different.
}
-
+
public void testUnknownEnumValues() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
builder.getMutableInt32ToEnumFieldValue().put(0, 0);
@@ -517,7 +666,7 @@ public class MapTest extends TestCase {
assertEquals(TestMap.EnumValue.UNRECOGNIZED,
message.getInt32ToEnumField().get(2));
assertEquals(1000, message.getInt32ToEnumFieldValue().get(2).intValue());
-
+
// Unknown enum values should be preserved after:
// 1. Serialization and parsing.
// 2. toBuild().
@@ -528,7 +677,7 @@ public class MapTest extends TestCase {
assertEquals(1000, builder.getInt32ToEnumFieldValue().get(2).intValue());
builder = TestMap.newBuilder().mergeFrom(message);
assertEquals(1000, builder.getInt32ToEnumFieldValue().get(2).intValue());
-
+
// hashCode()/equals() should take unknown enum values into account.
builder.getMutableInt32ToEnumFieldValue().put(2, 1001);
TestMap message2 = builder.build();
@@ -538,17 +687,17 @@ public class MapTest extends TestCase {
// should be the same.
assertTrue(message.getInt32ToEnumField().equals(message2.getInt32ToEnumField()));
}
-
+
public void testUnknownEnumValuesInReflectionApi() throws Exception {
Descriptor descriptor = TestMap.getDescriptor();
EnumDescriptor enumDescriptor = TestMap.EnumValue.getDescriptor();
FieldDescriptor field = descriptor.findFieldByName("int32_to_enum_field");
-
+
Map<Integer, Integer> data = new HashMap<Integer, Integer>();
data.put(0, 0);
data.put(1, 1);
data.put(2, 1000); // unknown value.
-
+
TestMap.Builder builder = TestMap.newBuilder();
for (Map.Entry<Integer, Integer> entry : data.entrySet()) {
builder.getMutableInt32ToEnumFieldValue().put(entry.getKey(), entry.getValue());
@@ -573,4 +722,26 @@ public class MapTest extends TestCase {
assertEquals(data.get(entry.getKey()) + 1, entry.getValue().intValue());
}
}
+
+ public void testIterationOrder() throws Exception {
+ TestMap.Builder builder = TestMap.newBuilder();
+ setMapValues(builder);
+ TestMap message = builder.build();
+
+ assertEquals(Arrays.asList("1", "2", "3"),
+ new ArrayList<String>(message.getStringToInt32Field().keySet()));
+ }
+
+ private static <K, V> Map<K, V> newMap(K key1, V value1) {
+ Map<K, V> map = new HashMap<K, V>();
+ map.put(key1, value1);
+ return map;
+ }
+
+ private static <K, V> Map<K, V> newMap(K key1, V value1, K key2, V value2) {
+ Map<K, V> map = new HashMap<K, V>();
+ map.put(key1, value1);
+ map.put(key2, value2);
+ return map;
+ }
}
diff --git a/java/src/test/java/com/google/protobuf/ProtobufArrayListTest.java b/java/src/test/java/com/google/protobuf/ProtobufArrayListTest.java
new file mode 100644
index 00000000..f9f5e9c7
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/ProtobufArrayListTest.java
@@ -0,0 +1,303 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import static java.util.Arrays.asList;
+
+import junit.framework.TestCase;
+
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Tests for {@link ProtobufArrayList}.
+ */
+public class ProtobufArrayListTest extends TestCase {
+
+ private static final ProtobufArrayList<Integer> UNARY_LIST = newImmutableProtoArrayList(1);
+ private static final ProtobufArrayList<Integer> TERTIARY_LIST =
+ newImmutableProtoArrayList(1, 2, 3);
+
+ private ProtobufArrayList<Integer> list;
+
+ @Override
+ protected void setUp() throws Exception {
+ list = new ProtobufArrayList<Integer>();
+ }
+
+ public void testEmptyListReturnsSameInstance() {
+ assertSame(ProtobufArrayList.emptyList(), ProtobufArrayList.emptyList());
+ }
+
+ public void testEmptyListIsImmutable() {
+ assertImmutable(ProtobufArrayList.<Integer>emptyList());
+ }
+
+ public void testCopyConstructor() {
+ ProtobufArrayList<Integer> copy = new ProtobufArrayList<Integer>(TERTIARY_LIST);
+ assertEquals(TERTIARY_LIST, copy);
+
+ copy = new ProtobufArrayList<Integer>(IntArrayList.emptyList());
+ assertEquals(ProtobufArrayList.emptyList(), copy);
+
+ copy = new ProtobufArrayList<Integer>(asList(1, 2, 3));
+ assertEquals(asList(1, 2, 3), copy);
+
+ copy = new ProtobufArrayList<Integer>(Collections.<Integer>emptyList());
+ assertEquals(ProtobufArrayList.emptyList(), copy);
+ }
+
+ public void testModificationWithIteration() {
+ list.addAll(asList(1, 2, 3, 4));
+ Iterator<Integer> iterator = list.iterator();
+ assertEquals(4, list.size());
+ assertEquals(1, (int) list.get(0));
+ assertEquals(1, (int) iterator.next());
+
+ list.remove(0);
+ try {
+ iterator.next();
+ fail();
+ } catch (ConcurrentModificationException e) {
+ // expected
+ }
+
+ iterator = list.iterator();
+ list.set(0, 1);
+ try {
+ iterator.next();
+ fail();
+ } catch (ConcurrentModificationException e) {
+ // expected
+ }
+
+ iterator = list.iterator();
+ list.add(0, 0);
+ try {
+ iterator.next();
+ fail();
+ } catch (ConcurrentModificationException e) {
+ // expected
+ }
+ }
+
+ public void testMakeImmutable() {
+ list.add(2);
+ list.add(4);
+ list.add(6);
+ list.add(8);
+ list.makeImmutable();
+ assertImmutable(list);
+ }
+
+ public void testRemove() {
+ list.add(2);
+ list.add(4);
+ list.add(6);
+
+ list.remove(1);
+ assertEquals(asList(2, 6), list);
+
+ list.remove(1);
+ assertEquals(asList(2), list);
+
+ list.remove(0);
+ assertEquals(asList(), list);
+ }
+
+ public void testGet() {
+ list.add(2);
+ list.add(6);
+
+ assertEquals(2, (int) list.get(0));
+ assertEquals(6, (int) list.get(1));
+ }
+
+ public void testSet() {
+ list.add(2);
+ list.add(6);
+
+ list.set(0, 1);
+ assertEquals(1, (int) list.get(0));
+ list.set(1, 2);
+ assertEquals(2, (int) list.get(1));
+ }
+
+ private void assertImmutable(List<Integer> list) {
+ if (list.contains(1)) {
+ throw new RuntimeException("Cannot test the immutability of lists that contain 1.");
+ }
+
+ try {
+ list.add(1);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.add(0, 1);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(Collections.<Integer>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(Collections.singletonList(1));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(new ProtobufArrayList<Integer>());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(0, Collections.singleton(1));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(0, UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.addAll(0, Collections.<Integer>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.clear();
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.remove(1);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.remove(new Object());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.removeAll(Collections.<Double>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.removeAll(Collections.singleton(1));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.removeAll(UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.retainAll(Collections.<Double>emptyList());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.retainAll(Collections.singleton(1));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.retainAll(UNARY_LIST);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ list.set(0, 0);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ }
+
+ private static ProtobufArrayList<Integer> newImmutableProtoArrayList(int... elements) {
+ ProtobufArrayList<Integer> list = new ProtobufArrayList<Integer>();
+ for (int element : elements) {
+ list.add(element);
+ }
+ list.makeImmutable();
+ return list;
+ }
+}
diff --git a/java/src/test/java/com/google/protobuf/RopeByteStringSubstringTest.java b/java/src/test/java/com/google/protobuf/RopeByteStringSubstringTest.java
index 212f8d68..cc385599 100644
--- a/java/src/test/java/com/google/protobuf/RopeByteStringSubstringTest.java
+++ b/java/src/test/java/com/google/protobuf/RopeByteStringSubstringTest.java
@@ -35,7 +35,7 @@ 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.
+ * {@link LiteralByteStringTest}. Only a couple of methods are overridden.
*
* @author carlanton@google.com (Carl Haverl)
*/
@@ -94,4 +94,34 @@ public class RopeByteStringSubstringTest extends LiteralByteStringTest {
assertEquals(classUnderTest + " string must must have same hashCode as the flat string",
flatString.hashCode(), unicode.hashCode());
}
+
+ @Override
+ public void testCharsetToString() 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(Internal.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());
+ }
}
diff --git a/java/src/test/java/com/google/protobuf/RopeByteStringTest.java b/java/src/test/java/com/google/protobuf/RopeByteStringTest.java
index b3970196..bd0d15e3 100644
--- a/java/src/test/java/com/google/protobuf/RopeByteStringTest.java
+++ b/java/src/test/java/com/google/protobuf/RopeByteStringTest.java
@@ -42,7 +42,7 @@ 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}.
*
@@ -118,6 +118,60 @@ public class RopeByteStringTest extends LiteralByteStringTest {
flatString.hashCode(), unicode.hashCode());
}
+ @Override
+ public void testCharsetToString() 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(Internal.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());
+ }
+
+ @Override
+ public void testToString_returnsCanonicalEmptyString() throws UnsupportedEncodingException {
+ RopeByteString ropeByteString =
+ RopeByteString.newInstanceForTest(ByteString.EMPTY, ByteString.EMPTY);
+ assertSame(classUnderTest + " must be the same string references",
+ ByteString.EMPTY.toString(Internal.UTF_8), ropeByteString.toString(Internal.UTF_8));
+ }
+
+ public void testToString_raisesException() throws UnsupportedEncodingException{
+ try {
+ ByteString byteString =
+ RopeByteString.newInstanceForTest(ByteString.EMPTY, ByteString.EMPTY);
+ byteString.toString("invalid");
+ fail("Should have thrown an exception.");
+ } catch (UnsupportedEncodingException expected) {
+ // This is success
+ }
+
+ try {
+ ByteString byteString = RopeByteString.concatenate(ByteString.copyFromUtf8("foo"),
+ ByteString.copyFromUtf8("bar"));
+ byteString.toString("invalid");
+ fail("Should have thrown an exception.");
+ } catch (UnsupportedEncodingException expected) {
+ // This is success
+ }
+ }
+
public void testJavaSerialization() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(out);
diff --git a/java/src/test/java/com/google/protobuf/TestUtil.java b/java/src/test/java/com/google/protobuf/TestUtil.java
index 135a1174..19a96d0e 100644
--- a/java/src/test/java/com/google/protobuf/TestUtil.java
+++ b/java/src/test/java/com/google/protobuf/TestUtil.java
@@ -276,11 +276,7 @@ public final class TestUtil {
/** Helper to convert a String to ByteString. */
static ByteString toBytes(String str) {
- try {
- return ByteString.copyFrom(str.getBytes("UTF-8"));
- } catch(java.io.UnsupportedEncodingException e) {
- throw new RuntimeException("UTF-8 not supported.", e);
- }
+ return ByteString.copyFrom(str.getBytes(Internal.UTF_8));
}
/**
diff --git a/java/src/test/java/com/google/protobuf/TextFormatTest.java b/java/src/test/java/com/google/protobuf/TextFormatTest.java
index eba06ca0..5d846646 100644
--- a/java/src/test/java/com/google/protobuf/TextFormatTest.java
+++ b/java/src/test/java/com/google/protobuf/TextFormatTest.java
@@ -243,8 +243,8 @@ public class TextFormatTest extends TestCase {
* characters. The characters are converted directly to bytes, *not*
* encoded using UTF-8.
*/
- private ByteString bytes(String str) throws Exception {
- return ByteString.copyFrom(str.getBytes("ISO-8859-1"));
+ private ByteString bytes(String str) {
+ return ByteString.copyFrom(str.getBytes(Internal.ISO_8859_1));
}
/**
@@ -863,7 +863,7 @@ public class TextFormatTest extends TestCase {
TextFormat.merge(TextFormat.printToUnicodeString(message), builder);
assertEquals(message.getOptionalString(), builder.getOptionalString());
}
-
+
public void testPrintToUnicodeStringWithNewlines() throws Exception {
// No newlines at start and end
assertEquals("optional_string: \"test newlines\\n\\nin\\nstring\"\n",
diff --git a/java/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java b/java/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java
index cec3da1e..e76b4a67 100644
--- a/java/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java
+++ b/java/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java
@@ -229,7 +229,7 @@ public class UnknownFieldSetLiteTest extends TestCase {
public void testMalformedBytes() throws Exception {
try {
- Foo.parseFrom("this is a malformed protocol buffer".getBytes("UTF-8"));
+ Foo.parseFrom("this is a malformed protocol buffer".getBytes(Internal.UTF_8));
fail();
} catch (InvalidProtocolBufferException e) {
// Expected.
diff --git a/java/src/test/java/com/google/protobuf/WellKnownTypesTest.java b/java/src/test/java/com/google/protobuf/WellKnownTypesTest.java
new file mode 100644
index 00000000..982e200f
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/WellKnownTypesTest.java
@@ -0,0 +1,65 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import com.google.protobuf.Descriptors.Descriptor;
+import com.google.protobuf.Descriptors.EnumDescriptor;
+import com.google.protobuf.Descriptors.EnumValueDescriptor;
+import com.google.protobuf.Descriptors.FieldDescriptor;
+import com.google.protobuf.test.TestWellKnownTypes;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This test ensures that well-known types are included in protobuf Java
+ * runtime library.
+ */
+public class WellKnownTypesTest extends TestCase {
+ public void testWellKnownTypes() {
+ // The test passes if it compiles.
+ TestWellKnownTypes message = TestWellKnownTypes.newBuilder().build();
+ assertEquals(0, message.getAnyField().getSerializedSize());
+ assertEquals(0, message.getApiField().getSerializedSize());
+ assertEquals(0, message.getDurationField().getSerializedSize());
+ assertEquals(0, message.getEmptyField().getSerializedSize());
+ assertEquals(0, message.getFieldMaskField().getSerializedSize());
+ assertEquals(0, message.getSourceContextField().getSerializedSize());
+ assertEquals(0, message.getStructField().getSerializedSize());
+ assertEquals(0, message.getTimestampField().getSerializedSize());
+ assertEquals(0, message.getTypeField().getSerializedSize());
+ assertEquals(0, message.getInt32Field().getSerializedSize());
+ }
+}
diff --git a/java/src/test/java/com/google/protobuf/field_presence_test.proto b/java/src/test/java/com/google/protobuf/field_presence_test.proto
index 8d8ea8ef..8f3ca8c6 100644
--- a/java/src/test/java/com/google/protobuf/field_presence_test.proto
+++ b/java/src/test/java/com/google/protobuf/field_presence_test.proto
@@ -45,15 +45,15 @@ message TestAllTypes {
BAZ = 2;
}
message NestedMessage {
- optional int32 value = 1;
+ int32 value = 1;
}
- optional int32 optional_int32 = 1;
- optional string optional_string = 2;
- optional bytes optional_bytes = 3;
- optional NestedEnum optional_nested_enum = 4;
- optional NestedMessage optional_nested_message = 5;
- optional protobuf_unittest.TestRequired optional_proto2_message = 6;
+ int32 optional_int32 = 1;
+ string optional_string = 2;
+ bytes optional_bytes = 3;
+ NestedEnum optional_nested_enum = 4;
+ NestedMessage optional_nested_message = 5;
+ protobuf_unittest.TestRequired optional_proto2_message = 6;
oneof oneof_field {
int32 oneof_int32 = 11;
@@ -75,12 +75,12 @@ message TestAllTypes {
}
message TestOptionalFieldsOnly {
- optional int32 optional_int32 = 1;
- optional string optional_string = 2;
- optional bytes optional_bytes = 3;
- optional TestAllTypes.NestedEnum optional_nested_enum = 4;
- optional TestAllTypes.NestedMessage optional_nested_message = 5;
- optional protobuf_unittest.TestRequired optional_proto2_message = 6;
+ int32 optional_int32 = 1;
+ string optional_string = 2;
+ bytes optional_bytes = 3;
+ TestAllTypes.NestedEnum optional_nested_enum = 4;
+ TestAllTypes.NestedMessage optional_nested_message = 5;
+ protobuf_unittest.TestRequired optional_proto2_message = 6;
}
message TestRepeatedFieldsOnly {
diff --git a/java/src/test/java/com/google/protobuf/lazy_fields_lite.proto b/java/src/test/java/com/google/protobuf/lazy_fields_lite.proto
index 015dc267..5580f72d 100644
--- a/java/src/test/java/com/google/protobuf/lazy_fields_lite.proto
+++ b/java/src/test/java/com/google/protobuf/lazy_fields_lite.proto
@@ -54,6 +54,15 @@ message LazyInnerMessageLite {
optional int32 num = 1;
optional int32 num_with_default = 2 [default = 42];
optional LazyNestedInnerMessageLite nested = 3 [lazy = true];
+
+ extensions 1000 to max;
+}
+
+message LazyExtension {
+ extend LazyInnerMessageLite {
+ optional LazyExtension extension = 1000;
+ }
+ optional string name = 1;
}
message LazyNestedInnerMessageLite {
diff --git a/java/src/test/java/com/google/protobuf/map_for_proto2_lite_test.proto b/java/src/test/java/com/google/protobuf/map_for_proto2_lite_test.proto
index a1fe856c..d5418f28 100644
--- a/java/src/test/java/com/google/protobuf/map_for_proto2_lite_test.proto
+++ b/java/src/test/java/com/google/protobuf/map_for_proto2_lite_test.proto
@@ -63,6 +63,13 @@ message TestUnknownEnumValue {
// parsing behavior of TestMap regarding unknown enum values.
map<int32, int32> int32_to_int32_field = 4;
}
+
+// Test that the maps initialization code works correctly when the map field
+// references the containing message.
+message TestRecursiveMap {
+ optional int32 value = 1;
+ map<int32, TestRecursiveMap> recursive_map_field = 2;
+}
package map_for_proto2_lite_test;
option java_package = "map_lite_test";
option optimize_for = LITE_RUNTIME;
diff --git a/java/src/test/java/com/google/protobuf/map_for_proto2_test.proto b/java/src/test/java/com/google/protobuf/map_for_proto2_test.proto
index a0ec7ac5..a9be5166 100644
--- a/java/src/test/java/com/google/protobuf/map_for_proto2_test.proto
+++ b/java/src/test/java/com/google/protobuf/map_for_proto2_test.proto
@@ -65,3 +65,10 @@ message TestUnknownEnumValue {
// parsing behavior of TestMap regarding unknown enum values.
map<int32, int32> int32_to_int32_field = 4;
}
+
+// Test that the maps initialization code works correctly when the map field
+// references the containing message.
+message TestRecursiveMap {
+ optional int32 value = 1;
+ map<int32, TestRecursiveMap> recursive_map_field = 2;
+}
diff --git a/java/src/test/java/com/google/protobuf/map_initialization_order_test.proto b/java/src/test/java/com/google/protobuf/map_initialization_order_test.proto
new file mode 100644
index 00000000..b02ac599
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/map_initialization_order_test.proto
@@ -0,0 +1,61 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Regression test for a map initilaization order bug. The bug only manifests
+// when:
+// 1. A message contains map fields and is also extendable.
+// 2. There is a file-level extension defined in the same file referencing
+// the above message as the extension type.
+// 3. The program executes in the following order:
+// a. getDescriptor() is called on another message in the same file.
+// b. use protobuf reflection to access the map field.
+// The symptom is a NullPointerException being thrown.
+syntax = "proto2";
+
+package map_test;
+
+option java_package = "map_test";
+option java_outer_classname = "MapInitializationOrderTest";
+option java_multiple_files = true;
+
+// Mirrors the structure of
+// javatests/com/google/cloud/common/logging/logging_test.proto.
+
+message Message1 {
+ map<string, bool> map_field = 1;
+ extensions 1000 to max;
+}
+
+extend Message1 {
+ optional Message1 recursive_extension = 1001;
+}
+
+message RedactAllTypes {
+}
diff --git a/java/src/test/java/com/google/protobuf/map_test.proto b/java/src/test/java/com/google/protobuf/map_test.proto
index 105ee3f8..2f7709be 100644
--- a/java/src/test/java/com/google/protobuf/map_test.proto
+++ b/java/src/test/java/com/google/protobuf/map_test.proto
@@ -36,9 +36,10 @@ option java_package = "map_test";
option java_outer_classname = "MapTestProto";
option java_generate_equals_and_hash = true;
+
message TestMap {
message MessageValue {
- optional int32 value = 1;
+ int32 value = 1;
}
enum EnumValue {
FOO = 0;
@@ -59,5 +60,5 @@ message TestMap {
// propagate the onChange event and mark its parent dirty when a change
// is made to a map field.
message TestOnChangeEventPropagation {
- optional TestMap optional_message = 1;
+ TestMap optional_message = 1;
}
diff --git a/java/src/test/java/com/google/protobuf/test_bad_identifiers.proto b/java/src/test/java/com/google/protobuf/test_bad_identifiers.proto
index 67035fd5..dc082615 100644
--- a/java/src/test/java/com/google/protobuf/test_bad_identifiers.proto
+++ b/java/src/test/java/com/google/protobuf/test_bad_identifiers.proto
@@ -45,6 +45,7 @@ option java_package = "com.google.protobuf";
option java_outer_classname = "TestBadIdentifiersProto";
option java_generate_equals_and_hash = true;
+
message TestMessage {
optional string cached_size = 1;
optional string serialized_size = 2;