Submit recent changes from internal branch. See CHANGES.txt for more details.
diff --git a/java/pom.xml b/java/pom.xml
index 94f1d7f..cdf85de 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -105,6 +105,12 @@
                   <arg value="../src/google/protobuf/unittest_mset.proto" />
                   <arg
                     value="src/test/java/com/google/protobuf/multiple_files_test.proto" />
+                  <arg value="src/test/java/com/google/protobuf/nested_builders_test.proto" />
+                  <arg value="src/test/java/com/google/protobuf/nested_extension.proto" />
+                  <arg value="src/test/java/com/google/protobuf/nested_extension_lite.proto" />
+                  <arg value="src/test/java/com/google/protobuf/non_nested_extension.proto" />
+                  <arg value="src/test/java/com/google/protobuf/non_nested_extension_lite.proto" />
+                  <arg value="src/test/java/com/google/protobuf/test_bad_identifiers.proto" />
                   <arg
                     value="../src/google/protobuf/unittest_optimize_for.proto" />
                   <arg
diff --git a/java/src/main/java/com/google/protobuf/AbstractMessage.java b/java/src/main/java/com/google/protobuf/AbstractMessage.java
index fb416bd..b9d8301 100644
--- a/java/src/main/java/com/google/protobuf/AbstractMessage.java
+++ b/java/src/main/java/com/google/protobuf/AbstractMessage.java
@@ -32,9 +32,10 @@
 
 import com.google.protobuf.Descriptors.Descriptor;
 import com.google.protobuf.Descriptors.FieldDescriptor;
+import com.google.protobuf.Internal.EnumLite;
 
-import java.io.InputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -167,11 +168,66 @@
   public int hashCode() {
     int hash = 41;
     hash = (19 * hash) + getDescriptorForType().hashCode();
-    hash = (53 * hash) + getAllFields().hashCode();
+    hash = hashFields(hash, getAllFields());
     hash = (29 * hash) + getUnknownFields().hashCode();
     return hash;
   }
 
+  /** Get a hash code for given fields and values, using the given seed. */
+  @SuppressWarnings("unchecked")
+  protected int hashFields(int hash, Map<FieldDescriptor, Object> map) {
+    for (Map.Entry<FieldDescriptor, Object> entry : map.entrySet()) {
+      FieldDescriptor field = entry.getKey();
+      Object value = entry.getValue();
+      hash = (37 * hash) + field.getNumber();
+      if (field.getType() != FieldDescriptor.Type.ENUM){
+        hash = (53 * hash) + value.hashCode();
+      } else if (field.isRepeated()) {
+        List<? extends EnumLite> list = (List<? extends EnumLite>) value;
+        hash = (53 * hash) + hashEnumList(list);
+      } else {
+        hash = (53 * hash) + hashEnum((EnumLite) value);
+      }
+    }
+    return hash;
+  }
+
+  /**
+   * Helper method for implementing {@link Message#hashCode()}.
+   * @see Boolean#hashCode()
+   */
+  protected static int hashLong(long n) {
+    return (int) (n ^ (n >>> 32));
+  }
+
+  /**
+   * Helper method for implementing {@link Message#hashCode()}.
+   * @see Boolean#hashCode()
+   */
+  protected static int hashBoolean(boolean b) {
+    return b ? 1231 : 1237;
+  }
+
+  /**
+   * Helper method for implementing {@link Message#hashCode()}.
+   * <p>
+   * This is needed because {@link java.lang.Enum#hashCode()} is final, but we
+   * need to use the field number as the hash code to ensure compatibility
+   * between statically and dynamically generated enum objects.
+   */
+  protected static int hashEnum(EnumLite e) {
+    return e.getNumber();
+  }
+
+  /** Helper method for implementing {@link Message#hashCode()}. */
+  protected static int hashEnumList(List<? extends EnumLite> list) {
+    int hash = 1;
+    for (EnumLite e : list) {
+      hash = 31 * hash + hashEnum(e);
+    }
+    return hash;
+  }
+
   // =================================================================
 
   /**
diff --git a/java/src/main/java/com/google/protobuf/ByteString.java b/java/src/main/java/com/google/protobuf/ByteString.java
index 5fade03..9135635 100644
--- a/java/src/main/java/com/google/protobuf/ByteString.java
+++ b/java/src/main/java/com/google/protobuf/ByteString.java
@@ -194,6 +194,18 @@
   }
 
   /**
+   * Copies bytes into a ByteBuffer.
+   *
+   * @param target ByteBuffer to copy into.
+   * @throws ReadOnlyBufferException if the {@code target} is read-only
+   * @throws BufferOverflowException if the {@code target}'s remaining()
+   *         space is not large enough to hold the data.
+   */
+  public void copyTo(ByteBuffer target) {
+    target.put(bytes, 0, bytes.length);
+  }
+
+  /**
    * Copies bytes to a {@code byte[]}.
    */
   public byte[] toByteArray() {
diff --git a/java/src/main/java/com/google/protobuf/CodedInputStream.java b/java/src/main/java/com/google/protobuf/CodedInputStream.java
index ad43f96..b3e0855 100644
--- a/java/src/main/java/com/google/protobuf/CodedInputStream.java
+++ b/java/src/main/java/com/google/protobuf/CodedInputStream.java
@@ -67,7 +67,25 @@
    */
   public static CodedInputStream newInstance(final byte[] buf, final int off,
                                              final int len) {
-    return new CodedInputStream(buf, off, len);
+    CodedInputStream result = new CodedInputStream(buf, off, len);
+    try {
+      // Some uses of CodedInputStream can be more efficient if they know
+      // exactly how many bytes are available.  By pushing the end point of the
+      // buffer as a limit, we allow them to get this information via
+      // getBytesUntilLimit().  Pushing a limit that we know is at the end of
+      // the stream can never hurt, since we can never past that point anyway.
+      result.pushLimit(len);
+    } catch (InvalidProtocolBufferException ex) {
+      // The only reason pushLimit() might throw an exception here is if len
+      // is negative. Normally pushLimit()'s parameter comes directly off the
+      // wire, so it's important to catch exceptions in case of corrupt or
+      // malicious data. However, in this case, we expect that len is not a
+      // user-supplied value, so we can assume that it being negative indicates
+      // a programming error. Therefore, throwing an unchecked exception is
+      // appropriate.
+      throw new IllegalArgumentException(ex);
+    }
+    return result;
   }
 
   // -----------------------------------------------------------------
@@ -263,7 +281,9 @@
   /** Read a {@code bytes} field value from the stream. */
   public ByteString readBytes() throws IOException {
     final int size = readRawVarint32();
-    if (size <= (bufferSize - bufferPos) && size > 0) {
+    if (size == 0) {
+      return ByteString.EMPTY;
+    } else if (size <= (bufferSize - bufferPos) && size > 0) {
       // Fast path:  We already have the bytes in a contiguous buffer, so
       //   just copy directly from it.
       final ByteString result = ByteString.copyFrom(buffer, bufferPos, size);
@@ -368,8 +388,8 @@
    * has already read one byte.  This allows the caller to determine if EOF
    * has been reached before attempting to read.
    */
-  static int readRawVarint32(final int firstByte,
-                             final InputStream input) throws IOException {
+  public static int readRawVarint32(
+      final int firstByte, final InputStream input) throws IOException {
     if ((firstByte & 0x80) == 0) {
       return firstByte;
     }
@@ -847,19 +867,19 @@
     } else {
       // Skipping more bytes than are in the buffer.  First skip what we have.
       int pos = bufferSize - bufferPos;
-      totalBytesRetired += pos;
-      bufferPos = 0;
-      bufferSize = 0;
+      bufferPos = bufferSize;
 
-      // Then skip directly from the InputStream for the rest.
-      while (pos < size) {
-        final int n = (input == null) ? -1 : (int) input.skip(size - pos);
-        if (n <= 0) {
-          throw InvalidProtocolBufferException.truncatedMessage();
-        }
-        pos += n;
-        totalBytesRetired += n;
+      // Keep refilling the buffer until we get to the point we wanted to skip
+      // to.  This has the side effect of ensuring the limits are updated
+      // correctly.
+      refillBuffer(true);
+      while (size - pos > bufferSize) {
+        pos += bufferSize;
+        bufferPos = bufferSize;
+        refillBuffer(true);
       }
+
+      bufferPos = size - pos; 
     }
   }
 }
diff --git a/java/src/main/java/com/google/protobuf/CodedOutputStream.java b/java/src/main/java/com/google/protobuf/CodedOutputStream.java
index 58dd150..51a932a 100644
--- a/java/src/main/java/com/google/protobuf/CodedOutputStream.java
+++ b/java/src/main/java/com/google/protobuf/CodedOutputStream.java
@@ -33,6 +33,7 @@
 import java.io.OutputStream;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
+import java.io.InputStream;
 
 /**
  * Encodes and writes protocol message fields.
@@ -381,9 +382,8 @@
 
   /** Write a {@code bytes} field to the stream. */
   public void writeBytesNoTag(final ByteString value) throws IOException {
-    final byte[] bytes = value.toByteArray();
-    writeRawVarint32(bytes.length);
-    writeRawBytes(bytes);
+    writeRawVarint32(value.size());
+    writeRawBytes(value);
   }
 
   /** Write a {@code uint32} field to the stream. */
@@ -870,6 +870,11 @@
     writeRawByte((byte) value);
   }
 
+  /** Write a byte string. */
+  public void writeRawBytes(final ByteString value) throws IOException {
+    writeRawBytes(value, 0, value.size());
+  }
+
   /** Write an array of bytes. */
   public void writeRawBytes(final byte[] value) throws IOException {
     writeRawBytes(value, 0, value.length);
@@ -906,6 +911,53 @@
     }
   }
 
+  /** Write part of a byte string. */
+  public void writeRawBytes(final ByteString value, int offset, int length)
+                            throws IOException {
+    if (limit - position >= length) {
+      // We have room in the current buffer.
+      value.copyTo(buffer, offset, position, length);
+      position += length;
+    } else {
+      // Write extends past current buffer.  Fill the rest of this buffer and
+      // flush.
+      final int bytesWritten = limit - position;
+      value.copyTo(buffer, offset, position, bytesWritten);
+      offset += bytesWritten;
+      length -= bytesWritten;
+      position = limit;
+      refreshBuffer();
+
+      // Now deal with the rest.
+      // Since we have an output stream, this is our buffer
+      // and buffer offset == 0
+      if (length <= limit) {
+        // Fits in new buffer.
+        value.copyTo(buffer, offset, 0, length);
+        position = length;
+      } else {
+        // Write is very big, but we can't do it all at once without allocating
+        // an a copy of the byte array since ByteString does not give us access
+        // to the underlying bytes. Use the InputStream interface on the
+        // ByteString and our buffer to copy between the two.
+        InputStream inputStreamFrom = value.newInput();
+        if (offset != inputStreamFrom.skip(offset)) {
+          throw new IllegalStateException("Skip failed? Should never happen.");
+        }
+        // Use the buffer as the temporary buffer to avoid allocating memory.
+        while (length > 0) {
+          int bytesToRead = Math.min(length, limit);
+          int bytesRead = inputStreamFrom.read(buffer, 0, bytesToRead);
+          if (bytesRead != bytesToRead) {
+            throw new IllegalStateException("Read failed? Should never happen");
+          }
+          output.write(buffer, 0, bytesRead);
+          length -= bytesRead;
+        }
+      }
+    }
+  }
+
   /** Encode and write a tag. */
   public void writeTag(final int fieldNumber, final int wireType)
                        throws IOException {
diff --git a/java/src/main/java/com/google/protobuf/Descriptors.java b/java/src/main/java/com/google/protobuf/Descriptors.java
index c5e9a04..2ee8459 100644
--- a/java/src/main/java/com/google/protobuf/Descriptors.java
+++ b/java/src/main/java/com/google/protobuf/Descriptors.java
@@ -46,6 +46,11 @@
  * its fields and other information about a type.  You can get a message
  * type's descriptor by calling {@code MessageType.getDescriptor()}, or
  * (given a message object of the type) {@code message.getDescriptorForType()}.
+ * Furthermore, each message is associated with a {@link FileDescriptor} for
+ * a relevant {@code .proto} file. You can obtain it by calling
+ * {@code Descriptor.getFile()}. A {@link FileDescriptor} contains descriptors
+ * for all the messages defined in that file, and file descriptors for all the
+ * imported {@code .proto} files.
  *
  * Descriptors are built from DescriptorProtos, as defined in
  * {@code google/protobuf/descriptor.proto}.
@@ -55,6 +60,9 @@
 public final class Descriptors {
   /**
    * Describes a {@code .proto} file, including everything defined within.
+   * That includes, in particular, descriptors for all the messages and
+   * file descriptors for all other imported {@code .proto} files
+   * (dependencies).
    */
   public static final class FileDescriptor {
     /** Convert the descriptor to its protocol message representation. */
diff --git a/java/src/main/java/com/google/protobuf/FieldSet.java b/java/src/main/java/com/google/protobuf/FieldSet.java
index bc1bb79..a85dbaa 100644
--- a/java/src/main/java/com/google/protobuf/FieldSet.java
+++ b/java/src/main/java/com/google/protobuf/FieldSet.java
@@ -33,7 +33,6 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
-import java.util.TreeMap;
 import java.util.List;
 import java.util.Map;
 import java.io.IOException;
@@ -67,16 +66,12 @@
         MessageLite.Builder to, MessageLite from);
   }
 
-  private Map<FieldDescriptorType, Object> fields;
+  private final SmallSortedMap<FieldDescriptorType, Object> fields;
+  private boolean isImmutable;
 
   /** Construct a new FieldSet. */
   private FieldSet() {
-    // Use a TreeMap because fields need to be in canonical order when
-    // serializing.
-    // TODO(kenton):  Maybe use some sort of sparse array instead?  It would
-    //   even make sense to store the first 16 or so tags in a flat array
-    //   to make DynamicMessage faster.
-    fields = new TreeMap<FieldDescriptorType, Object>();
+    this.fields = SmallSortedMap.newFieldMap(16);
   }
 
   /**
@@ -84,7 +79,8 @@
    * DEFAULT_INSTANCE.
    */
   private FieldSet(final boolean dummy) {
-    this.fields = Collections.emptyMap();
+    this.fields = SmallSortedMap.newFieldMap(0);
+    makeImmutable();
   }
 
   /** Construct a new FieldSet. */
@@ -105,14 +101,45 @@
   /** Make this FieldSet immutable from this point forward. */
   @SuppressWarnings("unchecked")
   public void makeImmutable() {
-    for (final Map.Entry<FieldDescriptorType, Object> entry:
-         fields.entrySet()) {
-      if (entry.getKey().isRepeated()) {
-        final List value = (List)entry.getValue();
-        fields.put(entry.getKey(), Collections.unmodifiableList(value));
-      }
+    if (isImmutable) {
+      return;
     }
-    fields = Collections.unmodifiableMap(fields);
+    fields.makeImmutable();
+    isImmutable = true;
+  }
+
+  /**
+   * Retuns whether the FieldSet is immutable. This is true if it is the
+   * {@link #emptySet} or if {@link #makeImmutable} were called.
+   *
+   * @return whether the FieldSet is immutable.
+   */
+  public boolean isImmutable() {
+    return isImmutable;
+  }
+
+  /**
+   * Clones the FieldSet. The returned FieldSet will be mutable even if the
+   * original FieldSet was immutable.
+   *
+   * @return the newly cloned FieldSet
+   */
+  @Override
+  public FieldSet<FieldDescriptorType> clone() {
+    // We can't just call fields.clone because List objects in the map
+    // should not be shared.
+    FieldSet<FieldDescriptorType> clone = FieldSet.newFieldSet();
+    for (int i = 0; i < fields.getNumArrayEntries(); i++) {
+      Map.Entry<FieldDescriptorType, Object> entry = fields.getArrayEntryAt(i);
+      FieldDescriptorType descriptor = entry.getKey();
+      clone.setField(descriptor, entry.getValue());
+    }
+    for (Map.Entry<FieldDescriptorType, Object> entry :
+             fields.getOverflowEntries()) {
+      FieldDescriptorType descriptor = entry.getKey();
+      clone.setField(descriptor, entry.getValue());
+    }
+    return clone;
   }
 
   // =================================================================
@@ -126,12 +153,13 @@
    * Get a simple map containing all the fields.
    */
   public Map<FieldDescriptorType, Object> getAllFields() {
-    return Collections.unmodifiableMap(fields);
+    return fields.isImmutable() ? fields : Collections.unmodifiableMap(fields);
   }
 
   /**
-   * Get an iterator to the field map.  This iterator should not be leaked
-   * out of the protobuf library as it is not protected from mutation.
+   * Get an iterator to the field map. This iterator should not be leaked out
+   * of the protobuf library as it is not protected from mutation when
+   * fields is not immutable.
    */
   public Iterator<Map.Entry<FieldDescriptorType, Object>> iterator() {
     return fields.entrySet().iterator();
@@ -336,27 +364,39 @@
    * aren't actually present in the set, it is up to the caller to check
    * that all required fields are present.
    */
-  @SuppressWarnings("unchecked")
   public boolean isInitialized() {
-    for (final Map.Entry<FieldDescriptorType, Object> entry:
-         fields.entrySet()) {
-      final FieldDescriptorType descriptor = entry.getKey();
-      if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
-        if (descriptor.isRepeated()) {
-          for (final MessageLite element:
-               (List<MessageLite>) entry.getValue()) {
-            if (!element.isInitialized()) {
-              return false;
-            }
-          }
-        } else {
-          if (!((MessageLite) entry.getValue()).isInitialized()) {
+    for (int i = 0; i < fields.getNumArrayEntries(); i++) {
+      if (!isInitialized(fields.getArrayEntryAt(i))) {
+        return false;
+      }
+    }
+    for (final Map.Entry<FieldDescriptorType, Object> entry :
+             fields.getOverflowEntries()) {
+      if (!isInitialized(entry)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  @SuppressWarnings("unchecked")
+  private boolean isInitialized(
+      final Map.Entry<FieldDescriptorType, Object> entry) {
+    final FieldDescriptorType descriptor = entry.getKey();
+    if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
+      if (descriptor.isRepeated()) {
+        for (final MessageLite element:
+                 (List<MessageLite>) entry.getValue()) {
+          if (!element.isInitialized()) {
             return false;
           }
         }
+      } else {
+        if (!((MessageLite) entry.getValue()).isInitialized()) {
+          return false;
+        }
       }
     }
-
     return true;
   }
 
@@ -378,39 +418,48 @@
   /**
    * Like {@link #mergeFrom(Message)}, but merges from another {@link FieldSet}.
    */
-  @SuppressWarnings("unchecked")
   public void mergeFrom(final FieldSet<FieldDescriptorType> other) {
-    for (final Map.Entry<FieldDescriptorType, Object> entry:
-         other.fields.entrySet()) {
-      final FieldDescriptorType descriptor = entry.getKey();
-      final Object otherValue = entry.getValue();
+    for (int i = 0; i < other.fields.getNumArrayEntries(); i++) {
+      mergeFromField(other.fields.getArrayEntryAt(i));
+    }
+    for (final Map.Entry<FieldDescriptorType, Object> entry :
+             other.fields.getOverflowEntries()) {
+      mergeFromField(entry);
+    }
+  }
 
-      if (descriptor.isRepeated()) {
-        Object value = fields.get(descriptor);
-        if (value == null) {
-          // Our list is empty, but we still need to make a defensive copy of
-          // the other list since we don't know if the other FieldSet is still
-          // mutable.
-          fields.put(descriptor, new ArrayList((List) otherValue));
-        } else {
-          // Concatenate the lists.
-          ((List) value).addAll((List) otherValue);
-        }
-      } else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
-        Object value = fields.get(descriptor);
-        if (value == null) {
-          fields.put(descriptor, otherValue);
-        } else {
-          // Merge the messages.
-          fields.put(descriptor,
-              descriptor.internalMergeFrom(
-                ((MessageLite) value).toBuilder(), (MessageLite) otherValue)
-              .build());
-        }
+  @SuppressWarnings("unchecked")
+  private void mergeFromField(
+      final Map.Entry<FieldDescriptorType, Object> entry) {
+    final FieldDescriptorType descriptor = entry.getKey();
+    final Object otherValue = entry.getValue();
 
+    if (descriptor.isRepeated()) {
+      Object value = fields.get(descriptor);
+      if (value == null) {
+        // Our list is empty, but we still need to make a defensive copy of
+        // the other list since we don't know if the other FieldSet is still
+        // mutable.
+        fields.put(descriptor, new ArrayList((List) otherValue));
       } else {
-        fields.put(descriptor, otherValue);
+        // Concatenate the lists.
+        ((List) value).addAll((List) otherValue);
       }
+    } else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
+      Object value = fields.get(descriptor);
+      if (value == null) {
+        fields.put(descriptor, otherValue);
+      } else {
+        // Merge the messages.
+        fields.put(
+            descriptor,
+            descriptor.internalMergeFrom(
+                ((MessageLite) value).toBuilder(), (MessageLite) otherValue)
+            .build());
+      }
+
+    } else {
+      fields.put(descriptor, otherValue);
     }
   }
 
@@ -468,8 +517,13 @@
   /** See {@link Message#writeTo(CodedOutputStream)}. */
   public void writeTo(final CodedOutputStream output)
                       throws IOException {
-    for (final Map.Entry<FieldDescriptorType, Object> entry:
-         fields.entrySet()) {
+    for (int i = 0; i < fields.getNumArrayEntries(); i++) {
+      final Map.Entry<FieldDescriptorType, Object> entry =
+          fields.getArrayEntryAt(i);
+      writeField(entry.getKey(), entry.getValue(), output);
+    }
+    for (final Map.Entry<FieldDescriptorType, Object> entry :
+         fields.getOverflowEntries()) {
       writeField(entry.getKey(), entry.getValue(), output);
     }
   }
@@ -479,16 +533,25 @@
    */
   public void writeMessageSetTo(final CodedOutputStream output)
                                 throws IOException {
-    for (final Map.Entry<FieldDescriptorType, Object> entry:
-         fields.entrySet()) {
-      final FieldDescriptorType descriptor = entry.getKey();
-      if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE &&
-          !descriptor.isRepeated() && !descriptor.isPacked()) {
-        output.writeMessageSetExtension(entry.getKey().getNumber(),
-                                        (MessageLite) entry.getValue());
-      } else {
-        writeField(descriptor, entry.getValue(), output);
-      }
+    for (int i = 0; i < fields.getNumArrayEntries(); i++) {
+      writeMessageSetTo(fields.getArrayEntryAt(i), output);
+    }
+    for (final Map.Entry<FieldDescriptorType, Object> entry :
+             fields.getOverflowEntries()) {
+      writeMessageSetTo(entry, output);
+    }
+  }
+
+  private void writeMessageSetTo(
+      final Map.Entry<FieldDescriptorType, Object> entry,
+      final CodedOutputStream output) throws IOException {
+    final FieldDescriptorType descriptor = entry.getKey();
+    if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE &&
+        !descriptor.isRepeated() && !descriptor.isPacked()) {
+      output.writeMessageSetExtension(entry.getKey().getNumber(),
+                                      (MessageLite) entry.getValue());
+    } else {
+      writeField(descriptor, entry.getValue(), output);
     }
   }
 
@@ -593,8 +656,13 @@
    */
   public int getSerializedSize() {
     int size = 0;
-    for (final Map.Entry<FieldDescriptorType, Object> entry:
-         fields.entrySet()) {
+    for (int i = 0; i < fields.getNumArrayEntries(); i++) {
+      final Map.Entry<FieldDescriptorType, Object> entry =
+          fields.getArrayEntryAt(i);
+      size += computeFieldSize(entry.getKey(), entry.getValue());
+    }
+    for (final Map.Entry<FieldDescriptorType, Object> entry :
+         fields.getOverflowEntries()) {
       size += computeFieldSize(entry.getKey(), entry.getValue());
     }
     return size;
@@ -605,20 +673,28 @@
    */
   public int getMessageSetSerializedSize() {
     int size = 0;
-    for (final Map.Entry<FieldDescriptorType, Object> entry:
-         fields.entrySet()) {
-      final FieldDescriptorType descriptor = entry.getKey();
-      if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE &&
-          !descriptor.isRepeated() && !descriptor.isPacked()) {
-        size += CodedOutputStream.computeMessageSetExtensionSize(
-                  entry.getKey().getNumber(), (MessageLite) entry.getValue());
-      } else {
-        size += computeFieldSize(descriptor, entry.getValue());
-      }
+    for (int i = 0; i < fields.getNumArrayEntries(); i++) {
+      size += getMessageSetSerializedSize(fields.getArrayEntryAt(i));
+    }
+    for (final Map.Entry<FieldDescriptorType, Object> entry :
+             fields.getOverflowEntries()) {
+      size += getMessageSetSerializedSize(entry);
     }
     return size;
   }
 
+  private int getMessageSetSerializedSize(
+      final Map.Entry<FieldDescriptorType, Object> entry) {
+    final FieldDescriptorType descriptor = entry.getKey();
+    if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE &&
+        !descriptor.isRepeated() && !descriptor.isPacked()) {
+      return CodedOutputStream.computeMessageSetExtensionSize(
+          entry.getKey().getNumber(), (MessageLite) entry.getValue());
+    } else {
+      return computeFieldSize(descriptor, entry.getValue());
+    }
+  }
+
   /**
    * Compute the number of bytes that would be needed to encode a
    * single tag/value pair of arbitrary type.
diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessage.java b/java/src/main/java/com/google/protobuf/GeneratedMessage.java
index 42ccbfd..fc2e530 100644
--- a/java/src/main/java/com/google/protobuf/GeneratedMessage.java
+++ b/java/src/main/java/com/google/protobuf/GeneratedMessage.java
@@ -35,8 +35,10 @@
 import com.google.protobuf.Descriptors.FieldDescriptor;
 
 import java.io.IOException;
-import java.lang.reflect.Method;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
@@ -52,10 +54,35 @@
  *
  * @author [email protected] Kenton Varda
  */
-public abstract class GeneratedMessage extends AbstractMessage {
-  protected GeneratedMessage() {}
+public abstract class GeneratedMessage extends AbstractMessage
+    implements Serializable {
 
-  private UnknownFieldSet unknownFields = UnknownFieldSet.getDefaultInstance();
+  private final UnknownFieldSet unknownFields;
+
+  /**
+   * For testing. Allows a test to disable the optimization that avoids using
+   * field builders for nested messages until they are requested. By disabling
+   * this optimization, existing tests can be reused to test the field builders.
+   */
+  protected static boolean alwaysUseFieldBuilders = false;
+
+  protected GeneratedMessage() {
+    this.unknownFields = UnknownFieldSet.getDefaultInstance();
+  }
+
+  protected GeneratedMessage(Builder<?> builder) {
+    this.unknownFields = builder.getUnknownFields();
+  }
+
+ /**
+  * For testing. Allows a test to disable the optimization that avoids using
+  * field builders for nested messages until they are requested. By disabling
+  * this optimization, existing tests can be reused to test the field builders.
+  * See {@link RepeatedFieldBuilder} and {@link SingleFieldBuilder}.
+  */
+  static void enableAlwaysUseFieldBuildersForTesting() {
+    alwaysUseFieldBuilders = true;
+  }
 
   /**
    * Get the FieldAccessorTable for this type.  We can't have the message
@@ -64,6 +91,7 @@
    */
   protected abstract FieldAccessorTable internalGetFieldAccessorTable();
 
+  //@Override (Java 1.6 override semantics, but we must support 1.5)
   public Descriptor getDescriptorForType() {
     return internalGetFieldAccessorTable().descriptor;
   }
@@ -118,36 +146,115 @@
     return true;
   }
 
+  //@Override (Java 1.6 override semantics, but we must support 1.5)
   public Map<FieldDescriptor, Object> getAllFields() {
     return Collections.unmodifiableMap(getAllFieldsMutable());
   }
 
+  //@Override (Java 1.6 override semantics, but we must support 1.5)
   public boolean hasField(final FieldDescriptor field) {
     return internalGetFieldAccessorTable().getField(field).has(this);
   }
 
+  //@Override (Java 1.6 override semantics, but we must support 1.5)
   public Object getField(final FieldDescriptor field) {
     return internalGetFieldAccessorTable().getField(field).get(this);
   }
 
+  //@Override (Java 1.6 override semantics, but we must support 1.5)
   public int getRepeatedFieldCount(final FieldDescriptor field) {
     return internalGetFieldAccessorTable().getField(field)
       .getRepeatedCount(this);
   }
 
+  //@Override (Java 1.6 override semantics, but we must support 1.5)
   public Object getRepeatedField(final FieldDescriptor field, final int index) {
     return internalGetFieldAccessorTable().getField(field)
       .getRepeated(this, index);
   }
 
+  //@Override (Java 1.6 override semantics, but we must support 1.5)
   public final UnknownFieldSet getUnknownFields() {
     return unknownFields;
   }
 
+  protected abstract Message.Builder newBuilderForType(BuilderParent parent);
+
+  /**
+   * Interface for the parent of a Builder that allows the builder to
+   * communicate invalidations back to the parent for use when using nested
+   * builders.
+   */
+  protected interface BuilderParent {
+
+    /**
+     * A builder becomes dirty whenever a field is modified -- including fields
+     * in nested builders -- and becomes clean when build() is called.  Thus,
+     * when a builder becomes dirty, all its parents become dirty as well, and
+     * when it becomes clean, all its children become clean.  The dirtiness
+     * state is used to invalidate certain cached values.
+     * <br>
+     * To this end, a builder calls markAsDirty() on its parent whenever it
+     * transitions from clean to dirty.  The parent must propagate this call to
+     * its own parent, unless it was already dirty, in which case the
+     * grandparent must necessarily already be dirty as well.  The parent can
+     * only transition back to "clean" after calling build() on all children.
+     */
+    void markDirty();
+  }
+
   @SuppressWarnings("unchecked")
   public abstract static class Builder <BuilderType extends Builder>
       extends AbstractMessage.Builder<BuilderType> {
-    protected Builder() {}
+
+    private BuilderParent builderParent;
+
+    private BuilderParentImpl meAsParent;
+
+    // Indicates that we've built a message and so we are now obligated
+    // to dispatch dirty invalidations. See GeneratedMessage.BuilderListener.
+    private boolean isClean;
+
+    private UnknownFieldSet unknownFields =
+        UnknownFieldSet.getDefaultInstance();
+
+    protected Builder() {
+      this(null);
+    }
+
+    protected Builder(BuilderParent builderParent) {
+      this.builderParent = builderParent;
+    }
+
+    void dispose() {
+      builderParent = null;
+    }
+
+    /**
+     * Called by the subclass when a message is built.
+     */
+    protected void onBuilt() {
+      if (builderParent != null) {
+        markClean();
+      }
+    }
+
+    /**
+     * Called by the subclass or a builder to notify us that a message was
+     * built and may be cached and therefore invalidations are needed.
+     */
+    protected void markClean() {
+      this.isClean = true;
+    }
+
+    /**
+     * Gets whether invalidations are needed
+     *
+     * @return whether invalidations are needed
+     */
+    protected boolean isClean() {
+      return isClean;
+    }
 
     // This is implemented here only to work around an apparent bug in the
     // Java compiler and/or build system.  See bug #1898463.  The mere presence
@@ -159,26 +266,50 @@
     }
 
     /**
-     * Get the message being built.  We don't just pass this to the
-     * constructor because it becomes null when build() is called.
+     * Called by the initialization and clear code paths to allow subclasses to
+     * reset any of their builtin fields back to the initial values.
      */
-    protected abstract GeneratedMessage internalGetResult();
+    public BuilderType clear() {
+      unknownFields = UnknownFieldSet.getDefaultInstance();
+      onChanged();
+      return (BuilderType) this;
+    }
 
     /**
      * Get the FieldAccessorTable for this type.  We can't have the message
      * class pass this in to the constructor because of bootstrapping trouble
      * with DescriptorProtos.
      */
-    private FieldAccessorTable internalGetFieldAccessorTable() {
-      return internalGetResult().internalGetFieldAccessorTable();
-    }
+    protected abstract FieldAccessorTable internalGetFieldAccessorTable();
 
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
     public Descriptor getDescriptorForType() {
       return internalGetFieldAccessorTable().descriptor;
     }
 
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
     public Map<FieldDescriptor, Object> getAllFields() {
-      return internalGetResult().getAllFields();
+      return Collections.unmodifiableMap(getAllFieldsMutable());
+    }
+
+    /** Internal helper which returns a mutable map. */
+    private Map<FieldDescriptor, Object> getAllFieldsMutable() {
+      final TreeMap<FieldDescriptor, Object> result =
+        new TreeMap<FieldDescriptor, Object>();
+      final Descriptor descriptor = internalGetFieldAccessorTable().descriptor;
+      for (final FieldDescriptor field : descriptor.getFields()) {
+        if (field.isRepeated()) {
+          final List value = (List) getField(field);
+          if (!value.isEmpty()) {
+            result.put(field, value);
+          }
+        } else {
+          if (hasField(field)) {
+            result.put(field, getField(field));
+          }
+        }
+      }
+      return result;
     }
 
     public Message.Builder newBuilderForField(
@@ -186,18 +317,20 @@
       return internalGetFieldAccessorTable().getField(field).newBuilder();
     }
 
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
     public boolean hasField(final FieldDescriptor field) {
-      return internalGetResult().hasField(field);
+      return internalGetFieldAccessorTable().getField(field).has(this);
     }
 
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
     public Object getField(final FieldDescriptor field) {
+      Object object = internalGetFieldAccessorTable().getField(field).get(this);
       if (field.isRepeated()) {
         // The underlying list object is still modifiable at this point.
         // Make sure not to expose the modifiable list to the caller.
-        return Collections.unmodifiableList(
-          (List) internalGetResult().getField(field));
+        return Collections.unmodifiableList((List) object);
       } else {
-        return internalGetResult().getField(field);
+        return object;
       }
     }
 
@@ -207,18 +340,23 @@
       return (BuilderType) this;
     }
 
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
     public BuilderType clearField(final FieldDescriptor field) {
       internalGetFieldAccessorTable().getField(field).clear(this);
       return (BuilderType) this;
     }
 
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
     public int getRepeatedFieldCount(final FieldDescriptor field) {
-      return internalGetResult().getRepeatedFieldCount(field);
+      return internalGetFieldAccessorTable().getField(field)
+          .getRepeatedCount(this);
     }
 
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
     public Object getRepeatedField(final FieldDescriptor field,
                                    final int index) {
-      return internalGetResult().getRepeatedField(field, index);
+      return internalGetFieldAccessorTable().getField(field)
+          .getRepeated(this, index);
     }
 
     public BuilderType setRepeatedField(final FieldDescriptor field,
@@ -234,29 +372,57 @@
       return (BuilderType) this;
     }
 
-    public final UnknownFieldSet getUnknownFields() {
-      return internalGetResult().unknownFields;
-    }
-
     public final BuilderType setUnknownFields(
         final UnknownFieldSet unknownFields) {
-      internalGetResult().unknownFields = unknownFields;
+      this.unknownFields = unknownFields;
+      onChanged();
       return (BuilderType) this;
     }
 
     @Override
     public final BuilderType mergeUnknownFields(
         final UnknownFieldSet unknownFields) {
-      final GeneratedMessage result = internalGetResult();
-      result.unknownFields =
-        UnknownFieldSet.newBuilder(result.unknownFields)
+      this.unknownFields =
+        UnknownFieldSet.newBuilder(this.unknownFields)
                        .mergeFrom(unknownFields)
                        .build();
+      onChanged();
       return (BuilderType) this;
     }
 
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
     public boolean isInitialized() {
-      return internalGetResult().isInitialized();
+      for (final FieldDescriptor field : getDescriptorForType().getFields()) {
+        // Check that all required fields are present.
+        if (field.isRequired()) {
+          if (!hasField(field)) {
+            return false;
+          }
+        }
+        // Check that embedded messages are initialized.
+        if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
+          if (field.isRepeated()) {
+            @SuppressWarnings("unchecked") final
+            List<Message> messageList = (List<Message>) getField(field);
+            for (final Message element : messageList) {
+              if (!element.isInitialized()) {
+                return false;
+              }
+            }
+          } else {
+            if (hasField(field) &&
+                !((Message) getField(field)).isInitialized()) {
+              return false;
+            }
+          }
+        }
+      }
+      return true;
+    }
+
+    @Override
+    public final UnknownFieldSet getUnknownFields() {
+      return unknownFields;
     }
 
     /**
@@ -270,11 +436,68 @@
         final int tag) throws IOException {
       return unknownFields.mergeFieldFrom(tag, input);
     }
+
+    /**
+     * Implementation of {@link BuilderParent} for giving to our children. This
+     * small inner class makes it so we don't publicly expose the BuilderParent
+     * methods.
+     */
+    private class BuilderParentImpl implements BuilderParent {
+
+      @Override
+      public void markDirty() {
+        onChanged();
+      }
+    }
+
+    /**
+     * Gets the {@link BuilderParent} for giving to our children.
+     * @return The builder parent for our children.
+     */
+    protected BuilderParent getParentForChildren() {
+      if (meAsParent == null) {
+        meAsParent = new BuilderParentImpl();
+      }
+      return meAsParent;
+    }
+
+    /**
+     * Called when a the builder or one of its nested children has changed
+     * and any parent should be notified of its invalidation.
+     */
+    protected final void onChanged() {
+      if (isClean && builderParent != null) {
+        builderParent.markDirty();
+
+        // Don't keep dispatching invalidations until build is called again.
+        isClean = false;
+      }
+    }
   }
 
   // =================================================================
   // Extensions-related stuff
 
+  public interface ExtendableMessageOrBuilder<
+      MessageType extends ExtendableMessage> extends MessageOrBuilder {
+
+    /** Check if a singular extension is present. */
+    <Type> boolean hasExtension(
+        GeneratedExtension<MessageType, Type> extension);
+
+    /** Get the number of elements in a repeated extension. */
+    <Type> int getExtensionCount(
+        GeneratedExtension<MessageType, List<Type>> extension);
+
+    /** Get the value of an extension. */
+    <Type> Type getExtension(GeneratedExtension<MessageType, Type> extension);
+
+    /** Get one element of a repeated extension. */
+    <Type> Type getExtension(
+        GeneratedExtension<MessageType, List<Type>> extension,
+        int index);
+  }
+
   /**
    * Generated message classes for message types that contain extension ranges
    * subclass this.
@@ -312,9 +535,20 @@
    */
   public abstract static class ExtendableMessage<
         MessageType extends ExtendableMessage>
-      extends GeneratedMessage {
-    protected ExtendableMessage() {}
-    private final FieldSet<FieldDescriptor> extensions = FieldSet.newFieldSet();
+      extends GeneratedMessage
+      implements ExtendableMessageOrBuilder<MessageType> {
+
+    private final FieldSet<FieldDescriptor> extensions;
+
+    protected ExtendableMessage() {
+      this.extensions = FieldSet.newFieldSet();
+    }
+
+    protected ExtendableMessage(
+        ExtendableBuilder<MessageType, ?> builder) {
+      super(builder);
+      this.extensions = builder.buildExtensions();
+    }
 
     private void verifyExtensionContainingType(
         final GeneratedExtension<MessageType, ?> extension) {
@@ -330,13 +564,15 @@
     }
 
     /** Check if a singular extension is present. */
-    public final boolean hasExtension(
-        final GeneratedExtension<MessageType, ?> extension) {
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
+    public final <Type> boolean hasExtension(
+        final GeneratedExtension<MessageType, Type> extension) {
       verifyExtensionContainingType(extension);
       return extensions.hasField(extension.getDescriptor());
     }
 
     /** Get the number of elements in a repeated extension. */
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
     public final <Type> int getExtensionCount(
         final GeneratedExtension<MessageType, List<Type>> extension) {
       verifyExtensionContainingType(extension);
@@ -345,6 +581,7 @@
     }
 
     /** Get the value of an extension. */
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
     @SuppressWarnings("unchecked")
     public final <Type> Type getExtension(
         final GeneratedExtension<MessageType, Type> extension) {
@@ -367,6 +604,7 @@
     }
 
     /** Get one element of a repeated extension. */
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
     @SuppressWarnings("unchecked")
     public final <Type> Type getExtension(
         final GeneratedExtension<MessageType, List<Type>> extension,
@@ -448,10 +686,14 @@
     // ---------------------------------------------------------------
     // Reflection
 
+    protected Map<FieldDescriptor, Object> getExtensionFields() {
+      return extensions.getAllFields();
+    }
+
     @Override
     public Map<FieldDescriptor, Object> getAllFields() {
       final Map<FieldDescriptor, Object> result = super.getAllFieldsMutable();
-      result.putAll(extensions.getAllFields());
+      result.putAll(getExtensionFields());
       return Collections.unmodifiableMap(result);
     }
 
@@ -556,9 +798,24 @@
   public abstract static class ExtendableBuilder<
         MessageType extends ExtendableMessage,
         BuilderType extends ExtendableBuilder>
-      extends Builder<BuilderType> {
+      extends Builder<BuilderType>
+      implements ExtendableMessageOrBuilder<MessageType> {
+
+    private FieldSet<FieldDescriptor> extensions = FieldSet.emptySet();
+
     protected ExtendableBuilder() {}
 
+    protected ExtendableBuilder(
+        BuilderParent parent) {
+      super(parent);
+    }
+
+    @Override
+    public BuilderType clear() {
+      extensions = FieldSet.emptySet();
+      return super.clear();
+    }
+
     // This is implemented here only to work around an apparent bug in the
     // Java compiler and/or build system.  See bug #1898463.  The mere presence
     // of this dummy clone() implementation makes it go away.
@@ -568,43 +825,84 @@
           "This is supposed to be overridden by subclasses.");
     }
 
-    @Override
-    protected abstract ExtendableMessage<MessageType> internalGetResult();
+    private void ensureExtensionsIsMutable() {
+      if (extensions.isImmutable()) {
+        extensions = extensions.clone();
+      }
+    }
+
+    private void verifyExtensionContainingType(
+        final GeneratedExtension<MessageType, ?> extension) {
+      if (extension.getDescriptor().getContainingType() !=
+          getDescriptorForType()) {
+        // This can only happen if someone uses unchecked operations.
+        throw new IllegalArgumentException(
+          "Extension is for type \"" +
+          extension.getDescriptor().getContainingType().getFullName() +
+          "\" which does not match message type \"" +
+          getDescriptorForType().getFullName() + "\".");
+      }
+    }
 
     /** Check if a singular extension is present. */
-    public final boolean hasExtension(
-        final GeneratedExtension<MessageType, ?> extension) {
-      return internalGetResult().hasExtension(extension);
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
+    public final <Type> boolean hasExtension(
+        final GeneratedExtension<MessageType, Type> extension) {
+      verifyExtensionContainingType(extension);
+      return extensions.hasField(extension.getDescriptor());
     }
 
     /** Get the number of elements in a repeated extension. */
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
     public final <Type> int getExtensionCount(
         final GeneratedExtension<MessageType, List<Type>> extension) {
-      return internalGetResult().getExtensionCount(extension);
+      verifyExtensionContainingType(extension);
+      final FieldDescriptor descriptor = extension.getDescriptor();
+      return extensions.getRepeatedFieldCount(descriptor);
     }
 
     /** Get the value of an extension. */
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
     public final <Type> Type getExtension(
         final GeneratedExtension<MessageType, Type> extension) {
-      return internalGetResult().getExtension(extension);
+      verifyExtensionContainingType(extension);
+      FieldDescriptor descriptor = extension.getDescriptor();
+      final Object value = extensions.getField(descriptor);
+      if (value == null) {
+        if (descriptor.isRepeated()) {
+          return (Type) Collections.emptyList();
+        } else if (descriptor.getJavaType() ==
+                   FieldDescriptor.JavaType.MESSAGE) {
+          return (Type) extension.getMessageDefaultInstance();
+        } else {
+          return (Type) extension.fromReflectionType(
+              descriptor.getDefaultValue());
+        }
+      } else {
+        return (Type) extension.fromReflectionType(value);
+      }
     }
 
     /** Get one element of a repeated extension. */
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
     public final <Type> Type getExtension(
         final GeneratedExtension<MessageType, List<Type>> extension,
         final int index) {
-      return internalGetResult().getExtension(extension, index);
+      verifyExtensionContainingType(extension);
+      FieldDescriptor descriptor = extension.getDescriptor();
+      return (Type) extension.singularFromReflectionType(
+          extensions.getRepeatedField(descriptor, index));
     }
 
     /** Set the value of an extension. */
     public final <Type> BuilderType setExtension(
         final GeneratedExtension<MessageType, Type> extension,
         final Type value) {
-      final ExtendableMessage<MessageType> message = internalGetResult();
-      message.verifyExtensionContainingType(extension);
+      verifyExtensionContainingType(extension);
+      ensureExtensionsIsMutable();
       final FieldDescriptor descriptor = extension.getDescriptor();
-      message.extensions.setField(descriptor,
-                                  extension.toReflectionType(value));
+      extensions.setField(descriptor, extension.toReflectionType(value));
+      onChanged();
       return (BuilderType) this;
     }
 
@@ -612,12 +910,13 @@
     public final <Type> BuilderType setExtension(
         final GeneratedExtension<MessageType, List<Type>> extension,
         final int index, final Type value) {
-      final ExtendableMessage<MessageType> message = internalGetResult();
-      message.verifyExtensionContainingType(extension);
+      verifyExtensionContainingType(extension);
+      ensureExtensionsIsMutable();
       final FieldDescriptor descriptor = extension.getDescriptor();
-      message.extensions.setRepeatedField(
+      extensions.setRepeatedField(
         descriptor, index,
         extension.singularToReflectionType(value));
+      onChanged();
       return (BuilderType) this;
     }
 
@@ -625,23 +924,44 @@
     public final <Type> BuilderType addExtension(
         final GeneratedExtension<MessageType, List<Type>> extension,
         final Type value) {
-      final ExtendableMessage<MessageType> message = internalGetResult();
-      message.verifyExtensionContainingType(extension);
+      verifyExtensionContainingType(extension);
+      ensureExtensionsIsMutable();
       final FieldDescriptor descriptor = extension.getDescriptor();
-      message.extensions.addRepeatedField(
+      extensions.addRepeatedField(
           descriptor, extension.singularToReflectionType(value));
+      onChanged();
       return (BuilderType) this;
     }
 
     /** Clear an extension. */
     public final <Type> BuilderType clearExtension(
         final GeneratedExtension<MessageType, ?> extension) {
-      final ExtendableMessage<MessageType> message = internalGetResult();
-      message.verifyExtensionContainingType(extension);
-      message.extensions.clearField(extension.getDescriptor());
+      verifyExtensionContainingType(extension);
+      ensureExtensionsIsMutable();
+      extensions.clearField(extension.getDescriptor());
+      onChanged();
       return (BuilderType) this;
     }
 
+    /** Called by subclasses to check if all extensions are initialized. */
+    protected boolean extensionsAreInitialized() {
+      return extensions.isInitialized();
+    }
+
+    /**
+     * Called by the build code path to create a copy of the extensions for
+     * building the message.
+     */
+    private FieldSet<FieldDescriptor> buildExtensions() {
+      extensions.makeImmutable();
+      return extensions;
+    }
+
+    @Override
+    public boolean isInitialized() {
+      return super.isInitialized() && extensionsAreInitialized();
+    }
+
     /**
      * Called by subclasses to parse an unknown field or an extension.
      * @return {@code true} unless the tag is an end-group tag.
@@ -659,16 +979,73 @@
     // ---------------------------------------------------------------
     // Reflection
 
-    // We don't have to override the get*() methods here because they already
-    // just forward to the underlying message.
+    @Override
+    public Map<FieldDescriptor, Object> getAllFields() {
+      final Map<FieldDescriptor, Object> result = super.getAllFieldsMutable();
+      result.putAll(extensions.getAllFields());
+      return Collections.unmodifiableMap(result);
+    }
+
+    @Override
+    public Object getField(final FieldDescriptor field) {
+      if (field.isExtension()) {
+        verifyContainingType(field);
+        final Object value = extensions.getField(field);
+        if (value == null) {
+          if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
+            // Lacking an ExtensionRegistry, we have no way to determine the
+            // extension's real type, so we return a DynamicMessage.
+            return DynamicMessage.getDefaultInstance(field.getMessageType());
+          } else {
+            return field.getDefaultValue();
+          }
+        } else {
+          return value;
+        }
+      } else {
+        return super.getField(field);
+      }
+    }
+
+    @Override
+    public int getRepeatedFieldCount(final FieldDescriptor field) {
+      if (field.isExtension()) {
+        verifyContainingType(field);
+        return extensions.getRepeatedFieldCount(field);
+      } else {
+        return super.getRepeatedFieldCount(field);
+      }
+    }
+
+    @Override
+    public Object getRepeatedField(final FieldDescriptor field,
+                                   final int index) {
+      if (field.isExtension()) {
+        verifyContainingType(field);
+        return extensions.getRepeatedField(field, index);
+      } else {
+        return super.getRepeatedField(field, index);
+      }
+    }
+
+    @Override
+    public boolean hasField(final FieldDescriptor field) {
+      if (field.isExtension()) {
+        verifyContainingType(field);
+        return extensions.hasField(field);
+      } else {
+        return super.hasField(field);
+      }
+    }
 
     @Override
     public BuilderType setField(final FieldDescriptor field,
                                 final Object value) {
       if (field.isExtension()) {
-        final ExtendableMessage<MessageType> message = internalGetResult();
-        message.verifyContainingType(field);
-        message.extensions.setField(field, value);
+        verifyContainingType(field);
+        ensureExtensionsIsMutable();
+        extensions.setField(field, value);
+        onChanged();
         return (BuilderType) this;
       } else {
         return super.setField(field, value);
@@ -678,9 +1055,10 @@
     @Override
     public BuilderType clearField(final FieldDescriptor field) {
       if (field.isExtension()) {
-        final ExtendableMessage<MessageType> message = internalGetResult();
-        message.verifyContainingType(field);
-        message.extensions.clearField(field);
+        verifyContainingType(field);
+        ensureExtensionsIsMutable();
+        extensions.clearField(field);
+        onChanged();
         return (BuilderType) this;
       } else {
         return super.clearField(field);
@@ -691,9 +1069,10 @@
     public BuilderType setRepeatedField(final FieldDescriptor field,
                                         final int index, final Object value) {
       if (field.isExtension()) {
-        final ExtendableMessage<MessageType> message = internalGetResult();
-        message.verifyContainingType(field);
-        message.extensions.setRepeatedField(field, index, value);
+        verifyContainingType(field);
+        ensureExtensionsIsMutable();
+        extensions.setRepeatedField(field, index, value);
+        onChanged();
         return (BuilderType) this;
       } else {
         return super.setRepeatedField(field, index, value);
@@ -704,9 +1083,10 @@
     public BuilderType addRepeatedField(final FieldDescriptor field,
                                         final Object value) {
       if (field.isExtension()) {
-        final ExtendableMessage<MessageType> message = internalGetResult();
-        message.verifyContainingType(field);
-        message.extensions.addRepeatedField(field, value);
+        verifyContainingType(field);
+        ensureExtensionsIsMutable();
+        extensions.addRepeatedField(field, value);
+        onChanged();
         return (BuilderType) this;
       } else {
         return super.addRepeatedField(field, value);
@@ -714,17 +1094,63 @@
     }
 
     protected final void mergeExtensionFields(final ExtendableMessage other) {
-      internalGetResult().extensions.mergeFrom(other.extensions);
+      ensureExtensionsIsMutable();
+      extensions.mergeFrom(other.extensions);
+      onChanged();
+    }
+
+    private void verifyContainingType(final FieldDescriptor field) {
+      if (field.getContainingType() != getDescriptorForType()) {
+        throw new IllegalArgumentException(
+          "FieldDescriptor does not match message type.");
+      }
     }
   }
 
   // -----------------------------------------------------------------
 
+  /**
+   * Gets the descriptor for an extension. The implementation depends on whether
+   * the extension is scoped in the top level of a file or scoped in a Message.
+   */
+  private static interface ExtensionDescriptorRetriever {
+    FieldDescriptor getDescriptor();
+  }
+
   /** For use by generated code only. */
   public static <ContainingType extends Message, Type>
       GeneratedExtension<ContainingType, Type>
-      newGeneratedExtension() {
-    return new GeneratedExtension<ContainingType, Type>();
+      newMessageScopedGeneratedExtension(final Message scope,
+                                         final int descriptorIndex,
+                                         final Class singularType,
+                                         final Message defaultInstance) {
+    // For extensions scoped within a Message, we use the Message to resolve
+    // the outer class's descriptor, from which the extension descriptor is
+    // obtained.
+    return new GeneratedExtension<ContainingType, Type>(
+        new ExtensionDescriptorRetriever() {
+          @Override
+          public FieldDescriptor getDescriptor() {
+            return scope.getDescriptorForType().getExtensions()
+                .get(descriptorIndex);
+          }
+        },
+        singularType,
+        defaultInstance);
+  }
+
+  /** For use by generated code only. */
+  public static <ContainingType extends Message, Type>
+     GeneratedExtension<ContainingType, Type>
+     newFileScopedGeneratedExtension(final Class singularType,
+                                     final Message defaultInstance) {
+    // For extensions scoped within a file, we rely on the outer class's
+    // static initializer to call internalInit() on the extension when the
+    // descriptor is available.
+    return new GeneratedExtension<ContainingType, Type>(
+        null,  // ExtensionDescriptorRetriever is initialized in internalInit();
+        singularType,
+        defaultInstance);
   }
 
   /**
@@ -757,64 +1183,67 @@
     // TODO(kenton):  Find ways to avoid using Java reflection within this
     //   class.  Also try to avoid suppressing unchecked warnings.
 
-    // We can't always initialize a GeneratedExtension when we first construct
-    // it due to initialization order difficulties (namely, the descriptor may
-    // not have been constructed yet, since it is often constructed by the
-    // initializer of a separate module).  So, we construct an uninitialized
-    // GeneratedExtension once, then call internalInit() on it later.  Generated
-    // code will always call internalInit() on all extensions as part of the
-    // static initialization code, and internalInit() throws an exception if
-    // called more than once, so this method is useless to users.
-    private GeneratedExtension() {}
-
-    /** For use by generated code only. */
-    public void internalInit(final FieldDescriptor descriptor,
-                             final Class<?> type) {
-      if (this.descriptor != null) {
-        throw new IllegalStateException("Already initialized.");
-      }
-
-      if (!descriptor.isExtension()) {
+    // We can't always initialize the descriptor of a GeneratedExtension when
+    // we first construct it due to initialization order difficulties (namely,
+    // the descriptor may not have been constructed yet, since it is often
+    // constructed by the initializer of a separate module).
+    //
+    // In the case of nested extensions, we initialize the
+    // ExtensionDescriptorRetriever with an instance that uses the scoping
+    // Message's default instance to retrieve the extension's descriptor.
+    //
+    // In the case of non-nested extensions, we initialize the
+    // ExtensionDescriptorRetriever to null and rely on the outer class's static
+    // initializer to call internalInit() after the descriptor has been parsed.
+    private GeneratedExtension(ExtensionDescriptorRetriever descriptorRetriever,
+                               Class singularType,
+                               Message messageDefaultInstance) {
+      if (Message.class.isAssignableFrom(singularType) &&
+          !singularType.isInstance(messageDefaultInstance)) {
         throw new IllegalArgumentException(
-          "GeneratedExtension given a regular (non-extension) field.");
+            "Bad messageDefaultInstance for " + singularType.getName());
       }
+      this.descriptorRetriever = descriptorRetriever;
+      this.singularType = singularType;
+      this.messageDefaultInstance = messageDefaultInstance;
 
-      this.descriptor = descriptor;
-      this.type = type;
-
-      switch (descriptor.getJavaType()) {
-        case MESSAGE:
-          enumValueOf = null;
-          enumGetValueDescriptor = null;
-          messageDefaultInstance =
-            (Message) invokeOrDie(getMethodOrDie(type, "getDefaultInstance"),
-                                  null);
-          if (messageDefaultInstance == null) {
-            throw new IllegalStateException(
-                type.getName() + ".getDefaultInstance() returned null.");
-          }
-          break;
-        case ENUM:
-          enumValueOf = getMethodOrDie(type, "valueOf",
-                                       EnumValueDescriptor.class);
-          enumGetValueDescriptor = getMethodOrDie(type, "getValueDescriptor");
-          messageDefaultInstance = null;
-          break;
-        default:
-          enumValueOf = null;
-          enumGetValueDescriptor = null;
-          messageDefaultInstance = null;
-          break;
+      if (ProtocolMessageEnum.class.isAssignableFrom(singularType)) {
+        this.enumValueOf = getMethodOrDie(singularType, "valueOf",
+                                          EnumValueDescriptor.class);
+        this.enumGetValueDescriptor =
+            getMethodOrDie(singularType, "getValueDescriptor");
+      } else {
+        this.enumValueOf = null;
+        this.enumGetValueDescriptor = null;
       }
     }
 
-    private FieldDescriptor descriptor;
-    private Class<?> type;
-    private Method enumValueOf;
-    private Method enumGetValueDescriptor;
-    private Message messageDefaultInstance;
+    /** For use by generated code only. */
+    public void internalInit(final FieldDescriptor descriptor) {
+      if (descriptorRetriever != null) {
+        throw new IllegalStateException("Already initialized.");
+      }
+      descriptorRetriever = new ExtensionDescriptorRetriever() {
+          @Override
+          public FieldDescriptor getDescriptor() {
+            return descriptor;
+          }
+        };
+    }
 
-    public FieldDescriptor getDescriptor() { return descriptor; }
+    private ExtensionDescriptorRetriever descriptorRetriever;
+    private final Class singularType;
+    private final Message messageDefaultInstance;
+    private final Method enumValueOf;
+    private final Method enumGetValueDescriptor;
+
+    public FieldDescriptor getDescriptor() {
+      if (descriptorRetriever == null) {
+        throw new IllegalStateException(
+            "getDescriptor() called before internalInit()");
+      }
+      return descriptorRetriever.getDescriptor();
+    }
 
     /**
      * If the extension is an embedded message or group, returns the default
@@ -832,6 +1261,7 @@
      */
     @SuppressWarnings("unchecked")
     private Object fromReflectionType(final Object value) {
+      FieldDescriptor descriptor = getDescriptor();
       if (descriptor.isRepeated()) {
         if (descriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE ||
             descriptor.getJavaType() == FieldDescriptor.JavaType.ENUM) {
@@ -854,9 +1284,10 @@
      * type, this converts a single element.
      */
     private Object singularFromReflectionType(final Object value) {
+      FieldDescriptor descriptor = getDescriptor();
       switch (descriptor.getJavaType()) {
         case MESSAGE:
-          if (type.isInstance(value)) {
+          if (singularType.isInstance(value)) {
             return value;
           } else {
             // It seems the copy of the embedded message stored inside the
@@ -883,6 +1314,7 @@
      */
     @SuppressWarnings("unchecked")
     private Object toReflectionType(final Object value) {
+      FieldDescriptor descriptor = getDescriptor();
       if (descriptor.isRepeated()) {
         if (descriptor.getJavaType() == FieldDescriptor.JavaType.ENUM) {
           // Must convert the whole list.
@@ -904,6 +1336,7 @@
      * type, this converts a single element.
      */
     private Object singularToReflectionType(final Object value) {
+      FieldDescriptor descriptor = getDescriptor();
       switch (descriptor.getJavaType()) {
         case ENUM:
           return invokeOrDie(enumGetValueDescriptor, value);
@@ -1025,13 +1458,17 @@
      */
     private interface FieldAccessor {
       Object get(GeneratedMessage message);
+      Object get(GeneratedMessage.Builder builder);
       void set(Builder builder, Object value);
       Object getRepeated(GeneratedMessage message, int index);
+      Object getRepeated(GeneratedMessage.Builder builder, int index);
       void setRepeated(Builder builder,
                        int index, Object value);
       void addRepeated(Builder builder, Object value);
       boolean has(GeneratedMessage message);
+      boolean has(GeneratedMessage.Builder builder);
       int getRepeatedCount(GeneratedMessage message);
+      int getRepeatedCount(GeneratedMessage.Builder builder);
       void clear(Builder builder);
       Message.Builder newBuilder();
     }
@@ -1044,10 +1481,13 @@
           final Class<? extends GeneratedMessage> messageClass,
           final Class<? extends Builder> builderClass) {
         getMethod = getMethodOrDie(messageClass, "get" + camelCaseName);
+        getMethodBuilder = getMethodOrDie(builderClass, "get" + camelCaseName);
         type = getMethod.getReturnType();
         setMethod = getMethodOrDie(builderClass, "set" + camelCaseName, type);
         hasMethod =
-          getMethodOrDie(messageClass, "has" + camelCaseName);
+            getMethodOrDie(messageClass, "has" + camelCaseName);
+        hasMethodBuilder =
+            getMethodOrDie(builderClass, "has" + camelCaseName);
         clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName);
       }
 
@@ -1056,13 +1496,18 @@
       //   checks.
       protected final Class<?> type;
       protected final Method getMethod;
+      protected final Method getMethodBuilder;
       protected final Method setMethod;
       protected final Method hasMethod;
+      protected final Method hasMethodBuilder;
       protected final Method clearMethod;
 
       public Object get(final GeneratedMessage message) {
         return invokeOrDie(getMethod, message);
       }
+      public Object get(GeneratedMessage.Builder builder) {
+        return invokeOrDie(getMethodBuilder, builder);
+      }
       public void set(final Builder builder, final Object value) {
         invokeOrDie(setMethod, builder, value);
       }
@@ -1071,6 +1516,10 @@
         throw new UnsupportedOperationException(
           "getRepeatedField() 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) {
         throw new UnsupportedOperationException(
@@ -1083,10 +1532,17 @@
       public boolean has(final GeneratedMessage message) {
         return (Boolean) invokeOrDie(hasMethod, message);
       }
+      public boolean has(GeneratedMessage.Builder builder) {
+        return (Boolean) invokeOrDie(hasMethodBuilder, builder);
+      }
       public int getRepeatedCount(final GeneratedMessage message) {
         throw new UnsupportedOperationException(
           "getRepeatedFieldSize() called on a singular field.");
       }
+      public int getRepeatedCount(GeneratedMessage.Builder builder) {
+        throw new UnsupportedOperationException(
+          "getRepeatedFieldSize() called on a singular field.");
+      }
       public void clear(final Builder builder) {
         invokeOrDie(clearMethod, builder);
       }
@@ -1097,38 +1553,51 @@
     }
 
     private static class RepeatedFieldAccessor implements FieldAccessor {
+      protected final Class type;
+      protected final Method getMethod;
+      protected final Method getMethodBuilder;
+      protected final Method getRepeatedMethod;
+      protected final Method getRepeatedMethodBuilder;
+      protected final Method setRepeatedMethod;
+      protected final Method addRepeatedMethod;
+      protected final Method getCountMethod;
+      protected final Method getCountMethodBuilder;
+      protected final Method clearMethod;
+
       RepeatedFieldAccessor(
           final FieldDescriptor descriptor, final String camelCaseName,
           final Class<? extends GeneratedMessage> messageClass,
           final Class<? extends Builder> builderClass) {
         getMethod = getMethodOrDie(messageClass,
                                    "get" + camelCaseName + "List");
+        getMethodBuilder = getMethodOrDie(builderClass,
+                                   "get" + camelCaseName + "List");
+
 
         getRepeatedMethod =
-          getMethodOrDie(messageClass, "get" + camelCaseName, Integer.TYPE);
+            getMethodOrDie(messageClass, "get" + camelCaseName, Integer.TYPE);
+        getRepeatedMethodBuilder =
+            getMethodOrDie(builderClass, "get" + camelCaseName, Integer.TYPE);
         type = getRepeatedMethod.getReturnType();
         setRepeatedMethod =
-          getMethodOrDie(builderClass, "set" + camelCaseName,
-                         Integer.TYPE, type);
+            getMethodOrDie(builderClass, "set" + camelCaseName,
+                           Integer.TYPE, type);
         addRepeatedMethod =
-          getMethodOrDie(builderClass, "add" + camelCaseName, type);
+            getMethodOrDie(builderClass, "add" + camelCaseName, type);
         getCountMethod =
-          getMethodOrDie(messageClass, "get" + camelCaseName + "Count");
+            getMethodOrDie(messageClass, "get" + camelCaseName + "Count");
+        getCountMethodBuilder =
+            getMethodOrDie(builderClass, "get" + camelCaseName + "Count");
 
         clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName);
       }
 
-      protected final Class<?> type;
-      protected final Method getMethod;
-      protected final Method getRepeatedMethod;
-      protected final Method setRepeatedMethod;
-      protected final Method addRepeatedMethod;
-      protected final Method getCountMethod;
-      protected final Method clearMethod;
-
       public Object get(final GeneratedMessage message) {
         return invokeOrDie(getMethod, message);
       }
+      public Object get(GeneratedMessage.Builder builder) {
+        return invokeOrDie(getMethodBuilder, 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.
@@ -1143,6 +1612,9 @@
                                 final int index) {
         return invokeOrDie(getRepeatedMethod, message, index);
       }
+      public Object getRepeated(GeneratedMessage.Builder builder, int index) {
+        return invokeOrDie(getRepeatedMethodBuilder, builder, index);
+      }
       public void setRepeated(final Builder builder,
                               final int index, final Object value) {
         invokeOrDie(setRepeatedMethod, builder, index, value);
@@ -1154,9 +1626,16 @@
         throw new UnsupportedOperationException(
           "hasField() called on a singular field.");
       }
+      public boolean has(GeneratedMessage.Builder builder) {
+        throw new UnsupportedOperationException(
+          "hasField() called on a singular field.");
+      }
       public int getRepeatedCount(final GeneratedMessage message) {
         return (Integer) invokeOrDie(getCountMethod, message);
       }
+      public int getRepeatedCount(GeneratedMessage.Builder builder) {
+        return (Integer) invokeOrDie(getCountMethodBuilder, builder);
+      }
       public void clear(final Builder builder) {
         invokeOrDie(clearMethod, builder);
       }
@@ -1189,6 +1668,12 @@
       public Object get(final GeneratedMessage message) {
         return invokeOrDie(getValueDescriptorMethod, super.get(message));
       }
+
+      @Override
+      public Object get(final GeneratedMessage.Builder builder) {
+        return invokeOrDie(getValueDescriptorMethod, super.get(builder));
+      }
+
       @Override
       public void set(final Builder builder, final Object value) {
         super.set(builder, invokeOrDie(valueOfMethod, null, value));
@@ -1221,6 +1706,17 @@
         }
         return Collections.unmodifiableList(newList);
       }
+
+      @Override
+      @SuppressWarnings("unchecked")
+      public Object get(final GeneratedMessage.Builder builder) {
+        final List newList = new ArrayList();
+        for (final Object element : (List) super.get(builder)) {
+          newList.add(invokeOrDie(getValueDescriptorMethod, element));
+        }
+        return Collections.unmodifiableList(newList);
+      }
+
       @Override
       public Object getRepeated(final GeneratedMessage message,
                                 final int index) {
@@ -1228,6 +1724,12 @@
           super.getRepeated(message, index));
       }
       @Override
+      public Object getRepeated(final GeneratedMessage.Builder builder,
+                                final int index) {
+        return invokeOrDie(getValueDescriptorMethod,
+          super.getRepeated(builder, index));
+      }
+      @Override
       public void setRepeated(final Builder builder,
                               final int index, final Object value) {
         super.setRepeated(builder, index, invokeOrDie(valueOfMethod, null,
@@ -1318,4 +1820,14 @@
       }
     }
   }
+
+  /**
+   * Replaces this object in the output stream with a serialized form.
+   * Part of Java's serialization magic.  Generated sub-classes must override
+   * this method by calling <code>return super.writeReplace();</code>
+   * @return a SerializedForm of this message
+   */
+  protected Object writeReplace() throws ObjectStreamException {
+    return new GeneratedMessageLite.SerializedForm(this);
+  }
 }
diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java
index 9a799b4..30a7571 100644
--- a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java
+++ b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java
@@ -31,6 +31,10 @@
 package com.google.protobuf;
 
 import java.io.IOException;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
@@ -41,8 +45,14 @@
  *
  * @author [email protected] Kenton Varda
  */
-public abstract class GeneratedMessageLite extends AbstractMessageLite {
-  protected GeneratedMessageLite() {}
+public abstract class GeneratedMessageLite extends AbstractMessageLite
+    implements Serializable {
+
+  protected GeneratedMessageLite() {
+  }
+
+  protected GeneratedMessageLite(Builder builder) {
+  }
 
   @SuppressWarnings("unchecked")
   public abstract static class Builder<MessageType extends GeneratedMessageLite,
@@ -50,6 +60,11 @@
       extends AbstractMessageLite.Builder<BuilderType> {
     protected Builder() {}
 
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
+    public BuilderType clear() {
+      return (BuilderType) this;
+    }
+
     // This is implemented here only to work around an apparent bug in the
     // Java compiler and/or build system.  See bug #1898463.  The mere presence
     // of this dummy clone() implementation makes it go away.
@@ -66,12 +81,6 @@
     public abstract MessageType getDefaultInstanceForType();
 
     /**
-     * Get the message being built.  We don't just pass this to the
-     * constructor because it becomes null when build() is called.
-     */
-    protected abstract MessageType internalGetResult();
-
-    /**
      * Called by subclasses to parse an unknown field.
      * @return {@code true} unless the tag is an end-group tag.
      */
@@ -87,14 +96,45 @@
   // Extensions-related stuff
 
   /**
+   * Lite equivalent of {@link com.google.protobuf.GeneratedMessage.ExtendableMessageOrBuilder}.
+   */
+  public interface ExtendableMessageOrBuilder<
+      MessageType extends ExtendableMessage> extends MessageLiteOrBuilder {
+
+    /** Check if a singular extension is present. */
+    <Type> boolean hasExtension(
+        GeneratedExtension<MessageType, Type> extension);
+
+    /** Get the number of elements in a repeated extension. */
+    <Type> int getExtensionCount(
+        GeneratedExtension<MessageType, List<Type>> extension);
+
+    /** Get the value of an extension. */
+    <Type> Type getExtension(GeneratedExtension<MessageType, Type> extension);
+
+    /** Get one element of a repeated extension. */
+    <Type> Type getExtension(
+        GeneratedExtension<MessageType, List<Type>> extension,
+        int index);
+  }
+
+  /**
    * Lite equivalent of {@link GeneratedMessage.ExtendableMessage}.
    */
   public abstract static class ExtendableMessage<
         MessageType extends ExtendableMessage<MessageType>>
-      extends GeneratedMessageLite {
-    protected ExtendableMessage() {}
-    private final FieldSet<ExtensionDescriptor> extensions =
-        FieldSet.newFieldSet();
+      extends GeneratedMessageLite
+      implements ExtendableMessageOrBuilder<MessageType> {
+
+    private final FieldSet<ExtensionDescriptor> extensions;
+
+    protected ExtendableMessage() {
+      this.extensions = FieldSet.newFieldSet();
+    }
+
+    protected ExtendableMessage(ExtendableBuilder<MessageType, ?> builder) {
+      this.extensions = builder.buildExtensions();
+    }
 
     private void verifyExtensionContainingType(
         final GeneratedExtension<MessageType, ?> extension) {
@@ -108,13 +148,15 @@
     }
 
     /** Check if a singular extension is present. */
-    public final boolean hasExtension(
-        final GeneratedExtension<MessageType, ?> extension) {
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
+    public final <Type> boolean hasExtension(
+        final GeneratedExtension<MessageType, Type> extension) {
       verifyExtensionContainingType(extension);
       return extensions.hasField(extension.descriptor);
     }
 
     /** Get the number of elements in a repeated extension. */
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
     public final <Type> int getExtensionCount(
         final GeneratedExtension<MessageType, List<Type>> extension) {
       verifyExtensionContainingType(extension);
@@ -122,6 +164,7 @@
     }
 
     /** Get the value of an extension. */
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
     @SuppressWarnings("unchecked")
     public final <Type> Type getExtension(
         final GeneratedExtension<MessageType, Type> extension) {
@@ -135,6 +178,7 @@
     }
 
     /** Get one element of a repeated extension. */
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
     @SuppressWarnings("unchecked")
     public final <Type> Type getExtension(
         final GeneratedExtension<MessageType, List<Type>> extension,
@@ -214,9 +258,88 @@
   public abstract static class ExtendableBuilder<
         MessageType extends ExtendableMessage<MessageType>,
         BuilderType extends ExtendableBuilder<MessageType, BuilderType>>
-      extends Builder<MessageType, BuilderType> {
+      extends Builder<MessageType, BuilderType>
+      implements ExtendableMessageOrBuilder<MessageType> {
     protected ExtendableBuilder() {}
 
+    private FieldSet<ExtensionDescriptor> extensions = FieldSet.emptySet();
+    private boolean extensionsIsMutable;
+
+    @Override
+    public BuilderType clear() {
+      extensions.clear();
+      extensionsIsMutable = false;
+      return super.clear();
+    }
+
+    private void ensureExtensionsIsMutable() {
+      if (!extensionsIsMutable) {
+        extensions = extensions.clone();
+        extensionsIsMutable = true;
+      }
+    }
+
+    /**
+     * Called by the build code path to create a copy of the extensions for
+     * building the message.
+     */
+    private FieldSet<ExtensionDescriptor> buildExtensions() {
+      extensions.makeImmutable();
+      extensionsIsMutable = false;
+      return extensions;
+    }
+
+    private void verifyExtensionContainingType(
+        final GeneratedExtension<MessageType, ?> extension) {
+      if (extension.getContainingTypeDefaultInstance() !=
+          getDefaultInstanceForType()) {
+        // This can only happen if someone uses unchecked operations.
+        throw new IllegalArgumentException(
+          "This extension is for a different message type.  Please make " +
+          "sure that you are not suppressing any generics type warnings.");
+      }
+    }
+
+    /** Check if a singular extension is present. */
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
+    public final <Type> boolean hasExtension(
+        final GeneratedExtension<MessageType, Type> extension) {
+      verifyExtensionContainingType(extension);
+      return extensions.hasField(extension.descriptor);
+    }
+
+    /** Get the number of elements in a repeated extension. */
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
+    public final <Type> int getExtensionCount(
+        final GeneratedExtension<MessageType, List<Type>> extension) {
+      verifyExtensionContainingType(extension);
+      return extensions.getRepeatedFieldCount(extension.descriptor);
+    }
+
+    /** Get the value of an extension. */
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
+    @SuppressWarnings("unchecked")
+    public final <Type> Type getExtension(
+        final GeneratedExtension<MessageType, Type> extension) {
+      verifyExtensionContainingType(extension);
+      final Object value = extensions.getField(extension.descriptor);
+      if (value == null) {
+        return extension.defaultValue;
+      } else {
+        return (Type) value;
+      }
+    }
+
+    /** Get one element of a repeated extension. */
+    @SuppressWarnings("unchecked")
+    //@Override (Java 1.6 override semantics, but we must support 1.5)
+    public final <Type> Type getExtension(
+        final GeneratedExtension<MessageType, List<Type>> extension,
+        final int index) {
+      verifyExtensionContainingType(extension);
+      return (Type) extensions.getRepeatedField(extension.descriptor, index);
+    }
+
     // This is implemented here only to work around an apparent bug in the
     // Java compiler and/or build system.  See bug #1898463.  The mere presence
     // of this dummy clone() implementation makes it go away.
@@ -226,41 +349,13 @@
           "This is supposed to be overridden by subclasses.");
     }
 
-    @Override
-    protected abstract MessageType internalGetResult();
-
-    /** Check if a singular extension is present. */
-    public final boolean hasExtension(
-        final GeneratedExtension<MessageType, ?> extension) {
-      return internalGetResult().hasExtension(extension);
-    }
-
-    /** Get the number of elements in a repeated extension. */
-    public final <Type> int getExtensionCount(
-        final GeneratedExtension<MessageType, List<Type>> extension) {
-      return internalGetResult().getExtensionCount(extension);
-    }
-
-    /** Get the value of an extension. */
-    public final <Type> Type getExtension(
-        final GeneratedExtension<MessageType, Type> extension) {
-      return internalGetResult().getExtension(extension);
-    }
-
-    /** Get one element of a repeated extension. */
-    public final <Type> Type getExtension(
-        final GeneratedExtension<MessageType, List<Type>> extension,
-        final int index) {
-      return internalGetResult().getExtension(extension, index);
-    }
-
     /** Set the value of an extension. */
     public final <Type> BuilderType setExtension(
         final GeneratedExtension<MessageType, Type> extension,
         final Type value) {
-      final ExtendableMessage<MessageType> message = internalGetResult();
-      message.verifyExtensionContainingType(extension);
-      message.extensions.setField(extension.descriptor, value);
+      verifyExtensionContainingType(extension);
+      ensureExtensionsIsMutable();
+      extensions.setField(extension.descriptor, value);
       return (BuilderType) this;
     }
 
@@ -268,9 +363,9 @@
     public final <Type> BuilderType setExtension(
         final GeneratedExtension<MessageType, List<Type>> extension,
         final int index, final Type value) {
-      final ExtendableMessage<MessageType> message = internalGetResult();
-      message.verifyExtensionContainingType(extension);
-      message.extensions.setRepeatedField(extension.descriptor, index, value);
+      verifyExtensionContainingType(extension);
+      ensureExtensionsIsMutable();
+      extensions.setRepeatedField(extension.descriptor, index, value);
       return (BuilderType) this;
     }
 
@@ -278,21 +373,26 @@
     public final <Type> BuilderType addExtension(
         final GeneratedExtension<MessageType, List<Type>> extension,
         final Type value) {
-      final ExtendableMessage<MessageType> message = internalGetResult();
-      message.verifyExtensionContainingType(extension);
-      message.extensions.addRepeatedField(extension.descriptor, value);
+      verifyExtensionContainingType(extension);
+      ensureExtensionsIsMutable();
+      extensions.addRepeatedField(extension.descriptor, value);
       return (BuilderType) this;
     }
 
     /** Clear an extension. */
     public final <Type> BuilderType clearExtension(
         final GeneratedExtension<MessageType, ?> extension) {
-      final ExtendableMessage<MessageType> message = internalGetResult();
-      message.verifyExtensionContainingType(extension);
-      message.extensions.clearField(extension.descriptor);
+      verifyExtensionContainingType(extension);
+      ensureExtensionsIsMutable();
+      extensions.clearField(extension.descriptor);
       return (BuilderType) this;
     }
 
+    /** Called by subclasses to check if all extensions are initialized. */
+    protected boolean extensionsAreInitialized() {
+      return extensions.isInitialized();
+    }
+
     /**
      * Called by subclasses to parse an unknown field or an extension.
      * @return {@code true} unless the tag is an end-group tag.
@@ -302,9 +402,6 @@
         final CodedInputStream input,
         final ExtensionRegistryLite extensionRegistry,
         final int tag) throws IOException {
-      final FieldSet<ExtensionDescriptor> extensions =
-          ((ExtendableMessage) internalGetResult()).extensions;
-
       final int wireType = WireFormat.getTagWireType(tag);
       final int fieldNumber = WireFormat.getTagFieldNumber(tag);
 
@@ -347,6 +444,7 @@
               // enum, drop it (don't even add it to unknownFields).
               return true;
             }
+            ensureExtensionsIsMutable();
             extensions.addRepeatedField(extension.descriptor, value);
           }
         } else {
@@ -354,6 +452,7 @@
             final Object value =
               FieldSet.readPrimitiveField(input,
                                           extension.descriptor.getLiteType());
+            ensureExtensionsIsMutable();
             extensions.addRepeatedField(extension.descriptor, value);
           }
         }
@@ -400,8 +499,10 @@
         }
 
         if (extension.descriptor.isRepeated()) {
+          ensureExtensionsIsMutable();
           extensions.addRepeatedField(extension.descriptor, value);
         } else {
+          ensureExtensionsIsMutable();
           extensions.setField(extension.descriptor, value);
         }
       }
@@ -410,8 +511,8 @@
     }
 
     protected final void mergeExtensionFields(final MessageType other) {
-      ((ExtendableMessage) internalGetResult()).extensions.mergeFrom(
-          ((ExtendableMessage) other).extensions);
+      ensureExtensionsIsMutable();
+      extensions.mergeFrom(((ExtendableMessage) other).extensions);
     }
   }
 
@@ -420,8 +521,40 @@
   /** For use by generated code only. */
   public static <ContainingType extends MessageLite, Type>
       GeneratedExtension<ContainingType, Type>
-      newGeneratedExtension() {
-    return new GeneratedExtension<ContainingType, Type>();
+          newSingularGeneratedExtension(
+              final ContainingType containingTypeDefaultInstance,
+              final Type defaultValue,
+              final MessageLite messageDefaultInstance,
+              final Internal.EnumLiteMap<?> enumTypeMap,
+              final int number,
+              final WireFormat.FieldType type) {
+    return new GeneratedExtension<ContainingType, Type>(
+        containingTypeDefaultInstance,
+        defaultValue,
+        messageDefaultInstance,
+        new ExtensionDescriptor(enumTypeMap, number, type,
+                                false /* isRepeated */,
+                                false /* isPacked */));
+  }
+
+  /** For use by generated code only. */
+  public static <ContainingType extends MessageLite, Type>
+      GeneratedExtension<ContainingType, Type>
+          newRepeatedGeneratedExtension(
+              final ContainingType containingTypeDefaultInstance,
+              final MessageLite messageDefaultInstance,
+              final Internal.EnumLiteMap<?> enumTypeMap,
+              final int number,
+              final WireFormat.FieldType type,
+              final boolean isPacked) {
+    @SuppressWarnings("unchecked")  // Subclasses ensure Type is a List
+    Type emptyList = (Type) Collections.emptyList();
+    return new GeneratedExtension<ContainingType, Type>(
+        containingTypeDefaultInstance,
+        emptyList,
+        messageDefaultInstance,
+        new ExtensionDescriptor(
+            enumTypeMap, number, type, true /* isRepeated */, isPacked));
   }
 
   private static final class ExtensionDescriptor
@@ -489,60 +622,33 @@
    */
   public static final class GeneratedExtension<
       ContainingType extends MessageLite, Type> {
-    // We can't always initialize a GeneratedExtension when we first construct
-    // it due to initialization order difficulties (namely, the default
-    // instances may not have been constructed yet).  So, we construct an
-    // uninitialized GeneratedExtension once, then call internalInit() on it
-    // later.  Generated code will always call internalInit() on all extensions
-    // as part of the static initialization code, and internalInit() throws an
-    // exception if called more than once, so this method is useless to users.
-    private GeneratedExtension() {}
 
-    private void internalInit(
+    private GeneratedExtension(
         final ContainingType containingTypeDefaultInstance,
         final Type defaultValue,
         final MessageLite messageDefaultInstance,
         final ExtensionDescriptor descriptor) {
+      // Defensive checks to verify the correct initialization order of
+      // GeneratedExtensions and their related GeneratedMessages.
+      if (containingTypeDefaultInstance == null) {
+        throw new IllegalArgumentException(
+            "Null containingTypeDefaultInstance");
+      }
+      if (descriptor.getLiteType() == WireFormat.FieldType.MESSAGE &&
+          messageDefaultInstance == null) {
+        throw new IllegalArgumentException(
+            "Null messageDefaultInstance");
+      }
       this.containingTypeDefaultInstance = containingTypeDefaultInstance;
       this.defaultValue = defaultValue;
       this.messageDefaultInstance = messageDefaultInstance;
       this.descriptor = descriptor;
     }
 
-    /** For use by generated code only. */
-    public void internalInitSingular(
-        final ContainingType containingTypeDefaultInstance,
-        final Type defaultValue,
-        final MessageLite messageDefaultInstance,
-        final Internal.EnumLiteMap<?> enumTypeMap,
-        final int number,
-        final WireFormat.FieldType type) {
-      internalInit(
-        containingTypeDefaultInstance, defaultValue, messageDefaultInstance,
-        new ExtensionDescriptor(enumTypeMap, number, type,
-          false /* isRepeated */, false /* isPacked */));
-    }
-
-    /** For use by generated code only. */
-    @SuppressWarnings("unchecked")
-    public void internalInitRepeated(
-        final ContainingType containingTypeDefaultInstance,
-        final MessageLite messageDefaultInstance,
-        final Internal.EnumLiteMap<?> enumTypeMap,
-        final int number,
-        final WireFormat.FieldType type,
-        final boolean isPacked) {
-      internalInit(
-        containingTypeDefaultInstance, (Type) Collections.emptyList(),
-        messageDefaultInstance,
-        new ExtensionDescriptor(
-          enumTypeMap, number, type, true /* isRepeated */, isPacked));
-    }
-
-    private ContainingType containingTypeDefaultInstance;
-    private Type defaultValue;
-    private MessageLite messageDefaultInstance;
-    private ExtensionDescriptor descriptor;
+    private final ContainingType containingTypeDefaultInstance;
+    private final Type defaultValue;
+    private final MessageLite messageDefaultInstance;
+    private final ExtensionDescriptor descriptor;
 
     /**
      * Default instance of the type being extended, used to identify that type.
@@ -564,4 +670,61 @@
       return messageDefaultInstance;
     }
   }
+
+  /**
+   * A serialized (serializable) form of the generated message.  Stores the
+   * message as a class name and a byte array.
+   */
+  static final class SerializedForm implements Serializable {
+    private static final long serialVersionUID = 0L;
+
+    private String messageClassName;
+    private byte[] asBytes;
+
+    /**
+     * Creates the serialized form by calling {@link com.google.protobuf.MessageLite#toByteArray}.
+     * @param regularForm the message to serialize
+     */
+    SerializedForm(MessageLite regularForm) {
+      messageClassName = regularForm.getClass().getName();
+      asBytes = regularForm.toByteArray();
+    }
+
+    /**
+     * When read from an ObjectInputStream, this method converts this object
+     * back to the regular form.  Part of Java's serialization magic.
+     * @return a GeneratedMessage of the type that was serialized
+     */
+    @SuppressWarnings("unchecked")
+    protected Object readResolve() throws ObjectStreamException {
+      try {
+        Class messageClass = Class.forName(messageClassName);
+        Method newBuilder = messageClass.getMethod("newBuilder");
+        MessageLite.Builder builder =
+            (MessageLite.Builder) newBuilder.invoke(null);
+        builder.mergeFrom(asBytes);
+        return builder.buildPartial();
+      } catch (ClassNotFoundException e) {
+        throw new RuntimeException("Unable to find proto buffer class", e);
+      } catch (NoSuchMethodException e) {
+        throw new RuntimeException("Unable to find newBuilder method", e);
+      } catch (IllegalAccessException e) {
+        throw new RuntimeException("Unable to call newBuilder method", e);
+      } catch (InvocationTargetException e) {
+        throw new RuntimeException("Error calling newBuilder", e.getCause());
+      } catch (InvalidProtocolBufferException e) {
+        throw new RuntimeException("Unable to understand proto buffer", e);
+      }
+    }
+  }
+
+  /**
+   * Replaces this object in the output stream with a serialized form.
+   * Part of Java's serialization magic.  Generated sub-classes must override
+   * this method by calling <code>return super.writeReplace();</code>
+   * @return a SerializedForm of this message
+   */
+  protected Object writeReplace() throws ObjectStreamException {
+    return new SerializedForm(this);
+  }
 }
diff --git a/java/src/main/java/com/google/protobuf/Internal.java b/java/src/main/java/com/google/protobuf/Internal.java
index 965465e..05eab57 100644
--- a/java/src/main/java/com/google/protobuf/Internal.java
+++ b/java/src/main/java/com/google/protobuf/Internal.java
@@ -100,6 +100,91 @@
   }
 
   /**
+   * Helper called by generated code to determine if a byte array is a valid
+   * UTF-8 encoded string such that the original bytes can be converted to
+   * a String object and then back to a byte array round tripping the bytes
+   * without loss.
+   * <p>
+   * This is inspired by UTF_8.java in sun.nio.cs.
+   *
+   * @param byteString the string to check
+   * @return whether the byte array is round trippable
+   */
+  public static boolean isValidUtf8(ByteString byteString) {
+    int index = 0;
+    int size = byteString.size();
+    // To avoid the masking, we could change this to use bytes;
+    // Then X > 0xC2 gets turned into X < -0xC2; X < 0x80
+    // gets turned into X >= 0, etc.
+
+    while (index < size) {
+      int byte1 = byteString.byteAt(index++) & 0xFF;
+      if (byte1 < 0x80) {
+        // fast loop for single bytes
+        continue;
+
+        // we know from this point on that we have 2-4 byte forms
+      } else if (byte1 < 0xC2 || byte1 > 0xF4) {
+        // catch illegal first bytes: < C2 or > F4
+        return false;
+      }
+      if (index >= size) {
+        // fail if we run out of bytes
+        return false;
+      }
+      int byte2 = byteString.byteAt(index++) & 0xFF;
+      if (byte2 < 0x80 || byte2 > 0xBF) {
+        // general trail-byte test
+        return false;
+      }
+      if (byte1 <= 0xDF) {
+        // two-byte form; general trail-byte test is sufficient
+        continue;
+      }
+
+      // we know from this point on that we have 3 or 4 byte forms
+      if (index >= size) {
+        // fail if we run out of bytes
+        return false;
+      }
+      int byte3 = byteString.byteAt(index++) & 0xFF;
+      if (byte3 < 0x80 || byte3 > 0xBF) {
+        // general trail-byte test
+        return false;
+      }
+      if (byte1 <= 0xEF) {
+        // three-byte form. Vastly more frequent than four-byte forms
+        // The following has an extra test, but not worth restructuring
+        if (byte1 == 0xE0 && byte2 < 0xA0 ||
+            byte1 == 0xED && byte2 > 0x9F) {
+          // check special cases of byte2
+          return false;
+        }
+
+      } else {
+        // four-byte form
+
+        if (index >= size) {
+          // fail if we run out of bytes
+          return false;
+        }
+        int byte4 = byteString.byteAt(index++) & 0xFF;
+        if (byte4 < 0x80 || byte4 > 0xBF) {
+          // general trail-byte test
+          return false;
+        }
+        // The following has an extra test, but not worth restructuring
+        if (byte1 == 0xF0 && byte2 < 0x90 ||
+            byte1 == 0xF4 && byte2 > 0x8F) {
+          // check special cases of byte2
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
+  /**
    * Interface for an enum value or value descriptor, to be used in FieldSet.
    * The lite library stores enum values directly in FieldSets but the full
    * library stores EnumValueDescriptors in order to better support reflection.
diff --git a/java/src/main/java/com/google/protobuf/LazyStringArrayList.java b/java/src/main/java/com/google/protobuf/LazyStringArrayList.java
new file mode 100644
index 0000000..1683a64
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/LazyStringArrayList.java
@@ -0,0 +1,155 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import java.util.List;
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.RandomAccess;
+import java.util.Collection;
+
+/**
+ * An implementation of {@link LazyStringList} that wraps an ArrayList. Each
+ * element is either a ByteString or a String. It caches the last one requested
+ * which is most likely the one needed next. This minimizes memory usage while
+ * satisfying the most common use cases.
+ * <p>
+ * <strong>Note that this implementation is not synchronized.</strong>
+ * If multiple threads access an <tt>ArrayList</tt> instance concurrently,
+ * and at least one of the threads modifies the list structurally, it
+ * <i>must</i> be synchronized externally.  (A structural modification is
+ * any operation that adds or deletes one or more elements, or explicitly
+ * resizes the backing array; merely setting the value of an element is not
+ * a structural modification.)  This is typically accomplished by
+ * synchronizing on some object that naturally encapsulates the list.
+ * <p>
+ * If the implementation is accessed via concurrent reads, this is thread safe.
+ * Conversions are done in a thread safe manner. It's possible that the
+ * conversion may happen more than once if two threads attempt to access the
+ * same element and the modifications were not visible to each other, but this
+ * will not result in any corruption of the list or change in behavior other
+ * than performance.
+ *
+ * @author [email protected] (Jon Perlow)
+ */
+public class LazyStringArrayList extends AbstractList<String>
+    implements LazyStringList, RandomAccess {
+
+  public final static LazyStringList EMPTY = new UnmodifiableLazyStringList(
+      new LazyStringArrayList());
+
+  private final List<Object> list;
+
+  public LazyStringArrayList() {
+    list = new ArrayList<Object>();
+  }
+
+  public LazyStringArrayList(List<String> from) {
+    list = new ArrayList<Object>(from);
+  }
+
+  @Override
+  public String get(int index) {
+    Object o = list.get(index);
+    if (o instanceof String) {
+      return (String) o;
+    } else {
+      ByteString bs = (ByteString) o;
+      String s = bs.toStringUtf8();
+      if (Internal.isValidUtf8(bs)) {
+        list.set(index, s);
+      }
+      return s;
+    }
+  }
+
+  @Override
+  public int size() {
+    return list.size();
+  }
+
+  @Override
+  public String set(int index, String s) {
+    Object o = list.set(index, s);
+    return asString(o);
+  }
+
+  @Override
+  public void add(int index, String element) {
+    list.add(index, element);
+    modCount++;
+  }
+
+  @Override
+  public boolean addAll(int index, Collection<? extends String> c) {
+    boolean ret = list.addAll(index, c);
+    modCount++;
+    return ret;
+  }
+
+  @Override
+  public String remove(int index) {
+    Object o = list.remove(index);
+    modCount++;
+    return asString(o);
+  }
+
+  public void clear() {
+    list.clear();
+    modCount++;
+  }
+
+  // @Override
+  public void add(ByteString element) {
+    list.add(element);
+    modCount++;
+  }
+
+  // @Override
+  public ByteString getByteString(int index) {
+    Object o = list.get(index);
+    if (o instanceof String) {
+      ByteString b = ByteString.copyFromUtf8((String) o);
+      list.set(index, b);
+      return b;
+    } else {
+      return (ByteString) o;
+    }
+  }
+
+  private String asString(Object o) {
+    if (o instanceof String) {
+      return (String) o;
+    } else {
+      return ((ByteString) o).toStringUtf8();
+    }
+  }
+}
diff --git a/java/src/main/java/com/google/protobuf/LazyStringList.java b/java/src/main/java/com/google/protobuf/LazyStringList.java
new file mode 100644
index 0000000..97139ca
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/LazyStringList.java
@@ -0,0 +1,72 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import java.util.List;
+
+/**
+ * An interface extending List&lt;String&gt; that also provides access to the
+ * items of the list as UTF8-encoded ByteString objects. This is used by the
+ * protocol buffer implementation to support lazily converting bytes parsed
+ * over the wire to String objects until needed and also increases the
+ * efficiency of serialization if the String was never requested as the
+ * ByteString is already cached.
+ * <p>
+ * This only adds additional methods that are required for the use in the
+ * protocol buffer code in order to be able successfuly round trip byte arrays
+ * through parsing and serialization without conversion to strings. It's not
+ * attempting to support the functionality of say List&ltByteString&gt, hence
+ * why only these two very specific methods are added.
+ *
+ * @author [email protected] (Jon Perlow)
+ */
+public interface LazyStringList extends List<String> {
+
+  /**
+   * Returns the element at the specified position in this list as a ByteString.
+   *
+   * @param index index of the element to return
+   * @return the element at the specified position in this list
+   * @throws IndexOutOfBoundsException if the index is out of range
+   *         (<tt>index &lt; 0 || index &gt;= size()</tt>)
+   */
+  ByteString getByteString(int index);
+
+  /**
+   * Appends the specified element to the end of this list (optional
+   * operation).
+   *
+   * @param element element to be appended to this list
+   * @throws UnsupportedOperationException if the <tt>add</tt> operation
+   *         is not supported by this list
+   */
+  void add(ByteString element);
+}
diff --git a/java/src/main/java/com/google/protobuf/Message.java b/java/src/main/java/com/google/protobuf/Message.java
index 8c29e21..67c4148 100644
--- a/java/src/main/java/com/google/protobuf/Message.java
+++ b/java/src/main/java/com/google/protobuf/Message.java
@@ -48,69 +48,7 @@
  *
  * @author [email protected] Kenton Varda
  */
-public interface Message extends MessageLite {
-  /**
-   * Get the message's type's descriptor.  This differs from the
-   * {@code getDescriptor()} method of generated message classes in that
-   * this method is an abstract method of the {@code Message} interface
-   * whereas {@code getDescriptor()} is a static method of a specific class.
-   * They return the same thing.
-   */
-  Descriptors.Descriptor getDescriptorForType();
-
-  // (From MessageLite, re-declared here only for return type covariance.)
-  Message getDefaultInstanceForType();
-
-  /**
-   * Returns a collection of all the fields in this message which are set
-   * and their corresponding values.  A singular ("required" or "optional")
-   * field is set iff hasField() returns true for that field.  A "repeated"
-   * field is set iff getRepeatedFieldSize() is greater than zero.  The
-   * values are exactly what would be returned by calling
-   * {@link #getField(Descriptors.FieldDescriptor)} for each field.  The map
-   * is guaranteed to be a sorted map, so iterating over it will return fields
-   * in order by field number.
-   */
-  Map<Descriptors.FieldDescriptor, Object> getAllFields();
-
-  /**
-   * Returns true if the given field is set.  This is exactly equivalent to
-   * calling the generated "has" accessor method corresponding to the field.
-   * @throws IllegalArgumentException The field is a repeated field, or
-   *           {@code field.getContainingType() != getDescriptorForType()}.
-   */
-  boolean hasField(Descriptors.FieldDescriptor field);
-
-  /**
-   * Obtains the value of the given field, or the default value if it is
-   * not set.  For primitive fields, the boxed primitive value is returned.
-   * For enum fields, the EnumValueDescriptor for the value is returend. For
-   * embedded message fields, the sub-message is returned.  For repeated
-   * fields, a java.util.List is returned.
-   */
-  Object getField(Descriptors.FieldDescriptor field);
-
-  /**
-   * Gets the number of elements of a repeated field.  This is exactly
-   * equivalent to calling the generated "Count" accessor method corresponding
-   * to the field.
-   * @throws IllegalArgumentException The field is not a repeated field, or
-   *           {@code field.getContainingType() != getDescriptorForType()}.
-   */
-  int getRepeatedFieldCount(Descriptors.FieldDescriptor field);
-
-  /**
-   * Gets an element of a repeated field.  For primitive fields, the boxed
-   * primitive value is returned.  For enum fields, the EnumValueDescriptor
-   * for the value is returend. For embedded message fields, the sub-message
-   * is returned.
-   * @throws IllegalArgumentException The field is not a repeated field, or
-   *           {@code field.getContainingType() != getDescriptorForType()}.
-   */
-  Object getRepeatedField(Descriptors.FieldDescriptor field, int index);
-
-  /** Get the {@link UnknownFieldSet} for this message. */
-  UnknownFieldSet getUnknownFields();
+public interface Message extends MessageLite, MessageOrBuilder {
 
   // -----------------------------------------------------------------
   // Comparison and hashing
@@ -119,7 +57,8 @@
    * Compares the specified object with this message for equality.  Returns
    * <tt>true</tt> if the given object is a message of the same type (as
    * defined by {@code getDescriptorForType()}) and has identical values for
-   * all of its fields.
+   * all of its fields.  Subclasses must implement this; inheriting
+   * {@code Object.equals()} is incorrect.
    *
    * @param other object to be compared for equality with this message
    * @return <tt>true</tt> if the specified object is equal to this message
@@ -129,8 +68,9 @@
 
   /**
    * Returns the hash code value for this message.  The hash code of a message
-   * is defined to be <tt>getDescriptor().hashCode() ^ map.hashCode()</tt>,
-   * where <tt>map</tt> is a map of field numbers to field values.
+   * should mix the message's type (object identity of the decsriptor) with its
+   * contents (known and unknown field values).  Subclasses must implement this;
+   * inheriting {@code Object.hashCode()} is incorrect.
    *
    * @return the hash code value for this message
    * @see Map#hashCode()
@@ -158,7 +98,7 @@
   /**
    * Abstract interface implemented by Protocol Message builders.
    */
-  interface Builder extends MessageLite.Builder {
+  interface Builder extends MessageLite.Builder, MessageOrBuilder {
     // (From MessageLite.Builder, re-declared here only for return type
     // covariance.)
     Builder clear();
@@ -197,17 +137,6 @@
      */
     Descriptors.Descriptor getDescriptorForType();
 
-    // (From MessageLite.Builder, re-declared here only for return type
-    // covariance.)
-    Message getDefaultInstanceForType();
-
-    /**
-     * Like {@link Message#getAllFields()}.  The returned map may or may not
-     * reflect future changes to the builder.  Either way, the returned map is
-     * itself unmodifiable.
-     */
-    Map<Descriptors.FieldDescriptor, Object> getAllFields();
-
     /**
      * Create a Builder for messages of the appropriate type for the given
      * field.  Messages built with this can then be passed to setField(),
@@ -215,12 +144,6 @@
      */
     Builder newBuilderForField(Descriptors.FieldDescriptor field);
 
-    /** Like {@link Message#hasField(Descriptors.FieldDescriptor)} */
-    boolean hasField(Descriptors.FieldDescriptor field);
-
-    /** Like {@link Message#getField(Descriptors.FieldDescriptor)} */
-    Object getField(Descriptors.FieldDescriptor field);
-
     /**
      * Sets a field to the given value.  The value must be of the correct type
      * for this field, i.e. the same type that
@@ -235,16 +158,6 @@
     Builder clearField(Descriptors.FieldDescriptor field);
 
     /**
-     * Like {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}
-     */
-    int getRepeatedFieldCount(Descriptors.FieldDescriptor field);
-
-    /**
-     * Like {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)}
-     */
-    Object getRepeatedField(Descriptors.FieldDescriptor field, int index);
-
-    /**
      * Sets an element of a repeated field to the given value.  The value must
      * be of the correct type for this field, i.e. the same type that
      * {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)} would
@@ -262,9 +175,6 @@
      */
     Builder addRepeatedField(Descriptors.FieldDescriptor field, Object value);
 
-    /** Get the {@link UnknownFieldSet} for this message. */
-    UnknownFieldSet getUnknownFields();
-
     /** Set the {@link UnknownFieldSet} for this message. */
     Builder setUnknownFields(UnknownFieldSet unknownFields);
 
diff --git a/java/src/main/java/com/google/protobuf/MessageLite.java b/java/src/main/java/com/google/protobuf/MessageLite.java
index cf7f39e..31b8256 100644
--- a/java/src/main/java/com/google/protobuf/MessageLite.java
+++ b/java/src/main/java/com/google/protobuf/MessageLite.java
@@ -64,22 +64,8 @@
  *
  * @author [email protected] Kenton Varda
  */
-public interface MessageLite {
-  /**
-   * Get an instance of the type with all fields set to their default values.
-   * This may or may not be a singleton.  This differs from the
-   * {@code getDefaultInstance()} method of generated message classes in that
-   * this method is an abstract method of the {@code MessageLite} interface
-   * whereas {@code getDefaultInstance()} is a static method of a specific
-   * class.  They return the same thing.
-   */
-  MessageLite getDefaultInstanceForType();
+public interface MessageLite extends MessageLiteOrBuilder {
 
-  /**
-   * Returns true if all required fields in the message and all embedded
-   * messages are set, false otherwise.
-   */
-  boolean isInitialized();
 
   /**
    * Serializes the message and writes it to {@code output}.  This does not
@@ -153,7 +139,7 @@
   /**
    * Abstract interface implemented by Protocol Message builders.
    */
-  interface Builder extends Cloneable {
+  interface Builder extends MessageLiteOrBuilder, Cloneable {
     /** Resets all fields to their default values. */
     Builder clear();
 
@@ -187,12 +173,6 @@
     Builder clone();
 
     /**
-     * Returns true if all required fields in the message and all embedded
-     * messages are set, false otherwise.
-     */
-    boolean isInitialized();
-
-    /**
      * Parses a message of this type from the input and merges it with this
      * message, as if using {@link Builder#mergeFrom(MessageLite)}.
      *
@@ -230,12 +210,6 @@
                       ExtensionRegistryLite extensionRegistry)
                       throws IOException;
 
-    /**
-     * Get the message's type's default instance.
-     * See {@link MessageLite#getDefaultInstanceForType()}.
-     */
-    MessageLite getDefaultInstanceForType();
-
     // ---------------------------------------------------------------
     // Convenience methods.
 
@@ -243,6 +217,8 @@
      * Parse {@code data} as a message of this type and merge it with the
      * message being built.  This is just a small wrapper around
      * {@link #mergeFrom(CodedInputStream)}.
+     *
+     * @return this
      */
     Builder mergeFrom(ByteString data) throws InvalidProtocolBufferException;
 
@@ -250,6 +226,8 @@
      * Parse {@code data} as a message of this type and merge it with the
      * message being built.  This is just a small wrapper around
      * {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}.
+     *
+     * @return this
      */
     Builder mergeFrom(ByteString data,
                       ExtensionRegistryLite extensionRegistry)
@@ -259,6 +237,8 @@
      * Parse {@code data} as a message of this type and merge it with the
      * message being built.  This is just a small wrapper around
      * {@link #mergeFrom(CodedInputStream)}.
+     *
+     * @return this
      */
     Builder mergeFrom(byte[] data) throws InvalidProtocolBufferException;
 
@@ -266,6 +246,8 @@
      * Parse {@code data} as a message of this type and merge it with the
      * message being built.  This is just a small wrapper around
      * {@link #mergeFrom(CodedInputStream)}.
+     *
+     * @return this
      */
     Builder mergeFrom(byte[] data, int off, int len)
                       throws InvalidProtocolBufferException;
@@ -274,6 +256,8 @@
      * Parse {@code data} as a message of this type and merge it with the
      * message being built.  This is just a small wrapper around
      * {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}.
+     *
+     * @return this
      */
     Builder mergeFrom(byte[] data,
                       ExtensionRegistryLite extensionRegistry)
@@ -283,6 +267,8 @@
      * Parse {@code data} as a message of this type and merge it with the
      * message being built.  This is just a small wrapper around
      * {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}.
+     *
+     * @return this
      */
     Builder mergeFrom(byte[] data, int off, int len,
                       ExtensionRegistryLite extensionRegistry)
@@ -299,6 +285,8 @@
      * and {@link #mergeDelimitedFrom(InputStream)} to read it.
      * <p>
      * Despite usually reading the entire input, this does not close the stream.
+     *
+     * @return this
      */
     Builder mergeFrom(InputStream input) throws IOException;
 
@@ -306,6 +294,8 @@
      * Parse a message of this type from {@code input} and merge it with the
      * message being built.  This is just a small wrapper around
      * {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}.
+     *
+     * @return this
      */
     Builder mergeFrom(InputStream input,
                       ExtensionRegistryLite extensionRegistry)
diff --git a/java/src/main/java/com/google/protobuf/MessageLiteOrBuilder.java b/java/src/main/java/com/google/protobuf/MessageLiteOrBuilder.java
new file mode 100644
index 0000000..a2a7448
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/MessageLiteOrBuilder.java
@@ -0,0 +1,56 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+/**
+ * Base interface for methods common to {@link MessageLite}
+ * and {@link MessageLite.Builder} to provide type equivalency.
+ *
+ * @author [email protected] (Jon Perlow)
+ */
+public interface MessageLiteOrBuilder {
+  /**
+   * Get an instance of the type with all fields set to their default values.
+   * This may or may not be a singleton.  This differs from the
+   * {@code getDefaultInstance()} method of generated message classes in that
+   * this method is an abstract method of the {@code MessageLite} interface
+   * whereas {@code getDefaultInstance()} is a static method of a specific
+   * class.  They return the same thing.
+   */
+  MessageLite getDefaultInstanceForType();
+
+  /**
+   * Returns true if all required fields in the message and all embedded
+   * messages are set, false otherwise.
+   */
+  boolean isInitialized();
+
+}
diff --git a/java/src/main/java/com/google/protobuf/MessageOrBuilder.java b/java/src/main/java/com/google/protobuf/MessageOrBuilder.java
new file mode 100644
index 0000000..0132e7c
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/MessageOrBuilder.java
@@ -0,0 +1,110 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import java.util.Map;
+
+/**
+ * Base interface for methods common to {@link Message} and
+ * {@link Message.Builder} to provide type equivalency.
+ *
+ * @author [email protected] (Jon Perlow)
+ */
+public interface MessageOrBuilder extends MessageLiteOrBuilder {
+
+  // (From MessageLite, re-declared here only for return type covariance.)
+  //@Override (Java 1.6 override semantics, but we must support 1.5)
+  Message getDefaultInstanceForType();
+
+  /**
+   * Get the message's type's descriptor.  This differs from the
+   * {@code getDescriptor()} method of generated message classes in that
+   * this method is an abstract method of the {@code Message} interface
+   * whereas {@code getDescriptor()} is a static method of a specific class.
+   * They return the same thing.
+   */
+  Descriptors.Descriptor getDescriptorForType();
+
+  /**
+   * Returns a collection of all the fields in this message which are set
+   * and their corresponding values.  A singular ("required" or "optional")
+   * field is set iff hasField() returns true for that field.  A "repeated"
+   * field is set iff getRepeatedFieldSize() is greater than zero.  The
+   * values are exactly what would be returned by calling
+   * {@link #getField(Descriptors.FieldDescriptor)} for each field.  The map
+   * is guaranteed to be a sorted map, so iterating over it will return fields
+   * in order by field number.
+   * <br>
+   * If this is for a builder, the returned map may or may not reflect future
+   * changes to the builder.  Either way, the returned map is itself
+   * unmodifiable.
+   */
+  Map<Descriptors.FieldDescriptor, Object> getAllFields();
+
+  /**
+   * Returns true if the given field is set.  This is exactly equivalent to
+   * calling the generated "has" accessor method corresponding to the field.
+   * @throws IllegalArgumentException The field is a repeated field, or
+   *           {@code field.getContainingType() != getDescriptorForType()}.
+   */
+  boolean hasField(Descriptors.FieldDescriptor field);
+
+  /**
+   * Obtains the value of the given field, or the default value if it is
+   * not set.  For primitive fields, the boxed primitive value is returned.
+   * For enum fields, the EnumValueDescriptor for the value is returend. For
+   * embedded message fields, the sub-message is returned.  For repeated
+   * fields, a java.util.List is returned.
+   */
+  Object getField(Descriptors.FieldDescriptor field);
+
+  /**
+   * Gets the number of elements of a repeated field.  This is exactly
+   * equivalent to calling the generated "Count" accessor method corresponding
+   * to the field.
+   * @throws IllegalArgumentException The field is not a repeated field, or
+   *           {@code field.getContainingType() != getDescriptorForType()}.
+   */
+  int getRepeatedFieldCount(Descriptors.FieldDescriptor field);
+
+  /**
+   * Gets an element of a repeated field.  For primitive fields, the boxed
+   * primitive value is returned.  For enum fields, the EnumValueDescriptor
+   * for the value is returend. For embedded message fields, the sub-message
+   * is returned.
+   * @throws IllegalArgumentException The field is not a repeated field, or
+   *           {@code field.getContainingType() != getDescriptorForType()}.
+   */
+  Object getRepeatedField(Descriptors.FieldDescriptor field, int index);
+
+  /** Get the {@link UnknownFieldSet} for this message. */
+  UnknownFieldSet getUnknownFields();
+}
diff --git a/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java b/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java
new file mode 100644
index 0000000..0772eac
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java
@@ -0,0 +1,696 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * <code>RepeatedFieldBuilder</code> implements a structure that a protocol
+ * message uses to hold a repeated field of other protocol messages. It supports
+ * the classical use case of adding immutable {@link Message}'s to the
+ * repeated field and is highly optimized around this (no extra memory
+ * allocations and sharing of immutable arrays).
+ * <br>
+ * It also supports the additional use case of adding a {@link Message.Builder}
+ * to the repeated field and deferring conversion of that <code>Builder</code>
+ * to an immutable <code>Message</code>. In this way, it's possible to maintain
+ * a tree of <code>Builder</code>'s that acts as a fully read/write data
+ * structure.
+ * <br>
+ * Logically, one can think of a tree of builders as converting the entire tree
+ * to messages when build is called on the root or when any method is called
+ * that desires a Message instead of a Builder. In terms of the implementation,
+ * the <code>SingleFieldBuilder</code> and <code>RepeatedFieldBuilder</code>
+ * classes cache messages that were created so that messages only need to be
+ * created when some change occured in its builder or a builder for one of its
+ * descendants.
+ *
+ * @param <MType> the type of message for the field
+ * @param <BType> the type of builder for the field
+ * @param <IType> the common interface for the message and the builder
+ *
+ * @author [email protected] (Jon Perlow)
+ */
+public class RepeatedFieldBuilder
+    <MType extends GeneratedMessage,
+     BType extends GeneratedMessage.Builder,
+     IType extends MessageOrBuilder>
+    implements GeneratedMessage.BuilderParent {
+
+  // Parent to send changes to.
+  private GeneratedMessage.BuilderParent parent;
+
+  // List of messages. Never null. It may be immutable, in which case
+  // isMessagesListImmutable will be true. See note below.
+  private List<MType> messages;
+
+  // Whether messages is an mutable array that can be modified.
+  private boolean isMessagesListMutable;
+
+  // List of builders. May be null, in which case, no nested builders were
+  // created. If not null, entries represent the builder for that index.
+  private List<SingleFieldBuilder<MType, BType, IType>> builders;
+
+  // Here are the invariants for messages and builders:
+  // 1. messages is never null and its count corresponds to the number of items
+  //    in the repeated field.
+  // 2. If builders is non-null, messages and builders MUST always
+  //    contain the same number of items.
+  // 3. Entries in either array can be null, but for any index, there MUST be
+  //    either a Message in messages or a builder in builders.
+  // 4. If the builder at an index is non-null, the builder is
+  //    authoritative. This is the case where a Builder was set on the index.
+  //    Any message in the messages array MUST be ignored.
+  // t. If the builder at an index is null, the message in the messages
+  //    list is authoritative. This is the case where a Message (not a Builder)
+  //    was set directly for an index.
+
+  // Indicates that we've built a message and so we are now obligated
+  // to dispatch dirty invalidations. See GeneratedMessage.BuilderListener.
+  private boolean isClean;
+
+  // A view of this builder that exposes a List interface of messages. This is
+  // initialized on demand. This is fully backed by this object and all changes
+  // are reflected in it. Access to any item converts it to a message if it
+  // was a builder.
+  private MessageExternalList<MType, BType, IType> externalMessageList;
+
+  // A view of this builder that exposes a List interface of builders. This is
+  // initialized on demand. This is fully backed by this object and all changes
+  // are reflected in it. Access to any item converts it to a builder if it
+  // was a message.
+  private BuilderExternalList<MType, BType, IType> externalBuilderList;
+
+  // A view of this builder that exposes a List interface of the interface
+  // implemented by messages and builders. This is initialized on demand. This
+  // is fully backed by this object and all changes are reflected in it.
+  // Access to any item returns either a builder or message depending on
+  // what is most efficient.
+  private MessageOrBuilderExternalList<MType, BType, IType>
+      externalMessageOrBuilderList;
+
+  /**
+   * Constructs a new builder with an empty list of messages.
+   *
+   * @param messages the current list of messages
+   * @param isMessagesListMutable Whether the messages list is mutable
+   * @param parent a listener to notify of changes
+   * @param isClean whether the builder is initially marked clean
+   */
+  public RepeatedFieldBuilder(
+      List<MType> messages,
+      boolean isMessagesListMutable,
+      GeneratedMessage.BuilderParent parent,
+      boolean isClean) {
+    this.messages = messages;
+    this.isMessagesListMutable = isMessagesListMutable;
+    this.parent = parent;
+    this.isClean = isClean;
+  }
+
+  public void dispose() {
+    // Null out parent so we stop sending it invalidations.
+    parent = null;
+  }
+
+  /**
+   * Ensures that the list of messages is mutable so it can be updated. If it's
+   * immutable, a copy is made.
+   */
+  private void ensureMutableMessageList() {
+    if (!isMessagesListMutable) {
+      messages = new ArrayList<MType>(messages);
+      isMessagesListMutable = true;
+    }
+  }
+
+  /**
+   * Ensures that the list of builders is not null. If it's null, the list is
+   * created and initialized to be the same size as the messages list with
+   * null entries.
+   */
+  private void ensureBuilders() {
+    if (this.builders == null) {
+      this.builders =
+          new ArrayList<SingleFieldBuilder<MType, BType, IType>>(
+              messages.size());
+      for (int i = 0; i < messages.size(); i++) {
+        builders.add(null);
+      }
+    }
+  }
+
+  /**
+   * Gets the count of items in the list.
+   *
+   * @return the count of items in the list.
+   */
+  public int getCount() {
+    return messages.size();
+  }
+
+  /**
+   * Gets whether the list is empty.
+   *
+   * @return whether the list is empty
+   */
+  public boolean isEmpty() {
+    return messages.isEmpty();
+  }
+
+  /**
+   * Get the message at the specified index. If the message is currently stored
+   * as a <code>Builder</code>, it is converted to a <code>Message</code> by
+   * calling {@link Message.Builder#buildPartial} on it.
+   *
+   * @param index the index of the message to get
+   * @return the message for the specified index
+   */
+  public MType getMessage(int index) {
+    return getMessage(index, false);
+  }
+
+  /**
+   * Get the message at the specified index. If the message is currently stored
+   * as a <code>Builder</code>, it is converted to a <code>Message</code> by
+   * calling {@link Message.Builder#buildPartial} on it.
+   *
+   * @param index the index of the message to get
+   * @param forBuild this is being called for build so we want to make sure
+   *     we SingleFieldBuilder.build to send dirty invalidations
+   * @return the message for the specified index
+   */
+  private MType getMessage(int index, boolean forBuild) {
+    if (this.builders == null) {
+      // We don't have any builders -- return the current Message.
+      // This is the case where no builder was created, so we MUST have a
+      // Message.
+      return messages.get(index);
+    }
+
+    SingleFieldBuilder<MType, BType, IType> builder = builders.get(index);
+    if (builder == null) {
+      // We don't have a builder -- return the current message.
+      // This is the case where no builder was created for the entry at index,
+      // so we MUST have a message.
+      return messages.get(index);
+
+    } else {
+      return forBuild ? builder.build() : builder.getMessage();
+    }
+  }
+
+  /**
+   * Gets a builder for the specified index. If no builder has been created for
+   * that index, a builder is created on demand by calling
+   * {@link Message#toBuilder}.
+   *
+   * @param index the index of the message to get
+   * @return The builder for that index
+   */
+  public BType getBuilder(int index) {
+    ensureBuilders();
+    SingleFieldBuilder<MType, BType, IType> builder = builders.get(index);
+    if (builder == null) {
+      MType message = messages.get(index);
+      builder = new SingleFieldBuilder<MType, BType, IType>(
+          message, this, isClean);
+      builders.set(index, builder);
+    }
+    return builder.getBuilder();
+  }
+
+  /**
+   * Gets the base class interface for the specified index. This may either be
+   * a builder or a message. It will return whatever is more efficient.
+   *
+   * @param index the index of the message to get
+   * @return the message or builder for the index as the base class interface
+   */
+  @SuppressWarnings("unchecked")
+  public IType getMessageOrBuilder(int index) {
+    if (this.builders == null) {
+      // We don't have any builders -- return the current Message.
+      // This is the case where no builder was created, so we MUST have a
+      // Message.
+      return (IType) messages.get(index);
+    }
+
+    SingleFieldBuilder<MType, BType, IType> builder = builders.get(index);
+    if (builder == null) {
+      // We don't have a builder -- return the current message.
+      // This is the case where no builder was created for the entry at index,
+      // so we MUST have a message.
+      return (IType) messages.get(index);
+
+    } else {
+      return builder.getMessageOrBuilder();
+    }
+  }
+
+  /**
+   * Sets a  message at the specified index replacing the existing item at
+   * that index.
+   *
+   * @param index the index to set.
+   * @param message the message to set
+   * @return the builder
+   */
+  public RepeatedFieldBuilder<MType, BType, IType> setMessage(
+      int index, MType message) {
+    if (message == null) {
+      throw new NullPointerException();
+    }
+    ensureMutableMessageList();
+    messages.set(index, message);
+    if (builders != null) {
+      SingleFieldBuilder<MType, BType, IType> entry =
+          builders.set(index, null);
+      if (entry != null) {
+        entry.dispose();
+      }
+    }
+    onChanged();
+    incrementModCounts();
+    return this;
+  }
+
+  /**
+   * Appends the specified element to the end of this list.
+   *
+   * @param message the message to add
+   * @return the builder
+   */
+  public RepeatedFieldBuilder<MType, BType, IType> addMessage(
+      MType message) {
+    if (message == null) {
+      throw new NullPointerException();
+    }
+    ensureMutableMessageList();
+    messages.add(message);
+    if (builders != null) {
+      builders.add(null);
+    }
+    onChanged();
+    incrementModCounts();
+    return this;
+  }
+
+  /**
+   * Inserts the specified message at the specified position in this list.
+   * Shifts the element currently at that position (if any) and any subsequent
+   * elements to the right (adds one to their indices).
+   *
+   * @param index the index at which to insert the message
+   * @param message the message to add
+   * @return the builder
+   */
+  public RepeatedFieldBuilder<MType, BType, IType> addMessage(
+      int index, MType message) {
+    if (message == null) {
+      throw new NullPointerException();
+    }
+    ensureMutableMessageList();
+    messages.add(index, message);
+    if (builders != null) {
+      builders.add(index, null);
+    }
+    onChanged();
+    incrementModCounts();
+    return this;
+  }
+
+  /**
+   * Appends all of the messages in the specified collection to the end of
+   * this list, in the order that they are returned by the specified
+   * collection's iterator.
+   *
+   * @param values the messages to add
+   * @return the builder
+   */
+  public RepeatedFieldBuilder<MType, BType, IType> addAllMessages(
+      Iterable<? extends MType> values) {
+    for (final MType value : values) {
+      if (value == null) {
+        throw new NullPointerException();
+      }
+    }
+    if (values instanceof Collection) {
+      @SuppressWarnings("unchecked") final
+      Collection<MType> collection = (Collection<MType>) values;
+      if (collection.size() == 0) {
+        return this;
+      }
+      ensureMutableMessageList();
+      for (MType value : values) {
+        addMessage(value);
+      }
+    } else {
+      ensureMutableMessageList();
+      for (MType value : values) {
+        addMessage(value);
+      }
+    }
+    onChanged();
+    incrementModCounts();
+    return this;
+  }
+
+  /**
+   * Appends a new builder to the end of this list and returns the builder.
+   *
+   * @param message the message to add which is the basis of the builder
+   * @return the new builder
+   */
+  public BType addBuilder(MType message) {
+    ensureMutableMessageList();
+    ensureBuilders();
+    SingleFieldBuilder<MType, BType, IType> builder =
+        new SingleFieldBuilder<MType, BType, IType>(
+            message, this, isClean);
+    messages.add(null);
+    builders.add(builder);
+    onChanged();
+    incrementModCounts();
+    return builder.getBuilder();
+  }
+
+  /**
+   * Inserts a new builder at the specified position in this list.
+   * Shifts the element currently at that position (if any) and any subsequent
+   * elements to the right (adds one to their indices).
+   *
+   * @param index the index at which to insert the builder
+   * @param message the message to add which is the basis of the builder
+   * @return the builder
+   */
+  public BType addBuilder(int index, MType message) {
+    ensureMutableMessageList();
+    ensureBuilders();
+    SingleFieldBuilder<MType, BType, IType> builder =
+        new SingleFieldBuilder<MType, BType, IType>(
+            message, this, isClean);
+    messages.add(index, null);
+    builders.add(index, builder);
+    onChanged();
+    incrementModCounts();
+    return builder.getBuilder();
+  }
+
+  /**
+   * Removes the element at the specified position in this list. Shifts any
+   * subsequent elements to the left (subtracts one from their indices).
+   * Returns the element that was removed from the list.
+   *
+   * @param index the index at which to remove the message
+   */
+  public void remove(int index) {
+    ensureMutableMessageList();
+    messages.remove(index);
+    if (builders != null) {
+      SingleFieldBuilder<MType, BType, IType> entry =
+          builders.remove(index);
+      if (entry != null) {
+        entry.dispose();
+      }
+    }
+    onChanged();
+    incrementModCounts();
+  }
+
+  /**
+   * Removes all of the elements from this list.
+   * The list will be empty after this call returns.
+   */
+  public void clear() {
+    messages = Collections.emptyList();
+    isMessagesListMutable = false;
+    if (builders != null) {
+      for (SingleFieldBuilder<MType, BType, IType> entry :
+          builders) {
+        if (entry != null) {
+          entry.dispose();
+        }
+      }
+      builders = null;
+    }
+    onChanged();
+    incrementModCounts();
+  }
+
+  /**
+   * Builds the list of messages from the builder and returns them.
+   *
+   * @return an immutable list of messages
+   */
+  public List<MType> build() {
+    // Now that build has been called, we are required to dispatch
+    // invalidations.
+    isClean = true;
+
+    if (!isMessagesListMutable && builders == null) {
+      // We still have an immutable list and we never created a builder.
+      return messages;
+    }
+
+    boolean allMessagesInSync = true;
+    if (!isMessagesListMutable) {
+      // We still have an immutable list. Let's see if any of them are out
+      // of sync with their builders.
+      for (int i = 0; i < messages.size(); i++) {
+        Message message = messages.get(i);
+        SingleFieldBuilder<MType, BType, IType> builder = builders.get(i);
+        if (builder != null) {
+          if (builder.build() != message) {
+            allMessagesInSync = false;
+            break;
+          }
+        }
+      }
+      if (allMessagesInSync) {
+        // Immutable list is still in sync.
+        return messages;
+      }
+    }
+
+    // Need to make sure messages is up to date
+    ensureMutableMessageList();
+    for (int i = 0; i < messages.size(); i++) {
+      messages.set(i, getMessage(i, true));
+    }
+
+    // We're going to return our list as immutable so we mark that we can
+    // no longer update it.
+    messages = Collections.unmodifiableList(messages);
+    isMessagesListMutable = false;
+    return messages;
+  }
+
+  /**
+   * Gets a view of the builder as a list of messages. The returned list is live
+   * and will reflect any changes to the underlying builder.
+   *
+   * @return the messages in the list
+   */
+  public List<MType> getMessageList() {
+    if (externalMessageList == null) {
+      externalMessageList =
+          new MessageExternalList<MType, BType, IType>(this);
+    }
+    return externalMessageList;
+  }
+
+  /**
+   * Gets a view of the builder as a list of builders. This returned list is
+   * live and will reflect any changes to the underlying builder.
+   *
+   * @return the builders in the list
+   */
+  public List<BType> getBuilderList() {
+    if (externalBuilderList == null) {
+      externalBuilderList =
+          new BuilderExternalList<MType, BType, IType>(this);
+    }
+    return externalBuilderList;
+  }
+
+  /**
+   * Gets a view of the builder as a list of MessageOrBuilders. This returned
+   * list is live and will reflect any changes to the underlying builder.
+   *
+   * @return the builders in the list
+   */
+  public List<IType> getMessageOrBuilderList() {
+    if (externalMessageOrBuilderList == null) {
+      externalMessageOrBuilderList =
+          new MessageOrBuilderExternalList<MType, BType, IType>(this);
+    }
+    return externalMessageOrBuilderList;
+  }
+
+  /**
+   * Called when a the builder or one of its nested children has changed
+   * and any parent should be notified of its invalidation.
+   */
+  private void onChanged() {
+    if (isClean && parent != null) {
+      parent.markDirty();
+
+      // Don't keep dispatching invalidations until build is called again.
+      isClean = false;
+    }
+  }
+
+  @Override
+  public void markDirty() {
+    onChanged();
+  }
+
+  /**
+   * Increments the mod counts so that an ConcurrentModificationException can
+   * be thrown if calling code tries to modify the builder while its iterating
+   * the list.
+   */
+  private void incrementModCounts() {
+    if (externalMessageList != null) {
+      externalMessageList.incrementModCount();
+    }
+    if (externalBuilderList != null) {
+      externalBuilderList.incrementModCount();
+    }
+    if (externalMessageOrBuilderList != null) {
+      externalMessageOrBuilderList.incrementModCount();
+    }
+  }
+
+  /**
+   * Provides a live view of the builder as a list of messages.
+   *
+   * @param <MType> the type of message for the field
+   * @param <BType> the type of builder for the field
+   * @param <IType> the common interface for the message and the builder
+   */
+  private static class MessageExternalList<
+      MType extends GeneratedMessage,
+      BType extends GeneratedMessage.Builder,
+      IType extends MessageOrBuilder>
+      extends AbstractList<MType> implements List<MType> {
+
+    RepeatedFieldBuilder<MType, BType, IType> builder;
+
+    MessageExternalList(
+        RepeatedFieldBuilder<MType, BType, IType> builder) {
+      this.builder = builder;
+    }
+
+    public int size() {
+      return this.builder.getCount();
+    }
+
+    public MType get(int index) {
+      return builder.getMessage(index);
+    }
+
+    void incrementModCount() {
+      modCount++;
+    }
+  }
+
+  /**
+   * Provides a live view of the builder as a list of builders.
+   *
+   * @param <MType> the type of message for the field
+   * @param <BType> the type of builder for the field
+   * @param <IType> the common interface for the message and the builder
+   */
+  private static class BuilderExternalList<
+      MType extends GeneratedMessage,
+      BType extends GeneratedMessage.Builder,
+      IType extends MessageOrBuilder>
+      extends AbstractList<BType> implements List<BType> {
+
+    RepeatedFieldBuilder<MType, BType, IType> builder;
+
+    BuilderExternalList(
+        RepeatedFieldBuilder<MType, BType, IType> builder) {
+      this.builder = builder;
+    }
+
+    public int size() {
+      return this.builder.getCount();
+    }
+
+    public BType get(int index) {
+      return builder.getBuilder(index);
+    }
+
+    void incrementModCount() {
+      modCount++;
+    }
+  }
+
+  /**
+   * Provides a live view of the builder as a list of builders.
+   *
+   * @param <MType> the type of message for the field
+   * @param <BType> the type of builder for the field
+   * @param <IType> the common interface for the message and the builder
+   */
+  private static class MessageOrBuilderExternalList<
+      MType extends GeneratedMessage,
+      BType extends GeneratedMessage.Builder,
+      IType extends MessageOrBuilder>
+      extends AbstractList<IType> implements List<IType> {
+
+    RepeatedFieldBuilder<MType, BType, IType> builder;
+
+    MessageOrBuilderExternalList(
+        RepeatedFieldBuilder<MType, BType, IType> builder) {
+      this.builder = builder;
+    }
+
+    public int size() {
+      return this.builder.getCount();
+    }
+
+    public IType get(int index) {
+      return builder.getMessageOrBuilder(index);
+    }
+
+    void incrementModCount() {
+      modCount++;
+    }
+  }
+}
diff --git a/java/src/main/java/com/google/protobuf/ServiceException.java b/java/src/main/java/com/google/protobuf/ServiceException.java
index c043a77..cde669d 100644
--- a/java/src/main/java/com/google/protobuf/ServiceException.java
+++ b/java/src/main/java/com/google/protobuf/ServiceException.java
@@ -35,10 +35,18 @@
  *
  * @author [email protected] (Chris Povirk)
  */
-public final class ServiceException extends Exception {
+public class ServiceException extends Exception {
   private static final long serialVersionUID = -1219262335729891920L;
 
   public ServiceException(final String message) {
     super(message);
   }
+
+  public ServiceException(final Throwable cause) {
+    super(cause);
+  }
+
+  public ServiceException(final String message, final Throwable cause) {
+    super(message, cause);
+  }
 }
diff --git a/java/src/main/java/com/google/protobuf/SingleFieldBuilder.java b/java/src/main/java/com/google/protobuf/SingleFieldBuilder.java
new file mode 100644
index 0000000..a92042e
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/SingleFieldBuilder.java
@@ -0,0 +1,241 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+/**
+ * <code>SingleFieldBuilder</code> implements a structure that a protocol
+ * message uses to hold a single field of another protocol message. It supports
+ * the classical use case of setting an immutable {@link Message} as the value
+ * of the field and is highly optimized around this.
+ * <br>
+ * It also supports the additional use case of setting a {@link Message.Builder}
+ * as the field and deferring conversion of that <code>Builder</code>
+ * to an immutable <code>Message</code>. In this way, it's possible to maintain
+ * a tree of <code>Builder</code>'s that acts as a fully read/write data
+ * structure.
+ * <br>
+ * Logically, one can think of a tree of builders as converting the entire tree
+ * to messages when build is called on the root or when any method is called
+ * that desires a Message instead of a Builder. In terms of the implementation,
+ * the <code>SingleFieldBuilder</code> and <code>RepeatedFieldBuilder</code>
+ * classes cache messages that were created so that messages only need to be
+ * created when some change occured in its builder or a builder for one of its
+ * descendants.
+ *
+ * @param <MType> the type of message for the field
+ * @param <BType> the type of builder for the field
+ * @param <IType> the common interface for the message and the builder
+ *
+ * @author [email protected] (Jon Perlow)
+ */
+public class SingleFieldBuilder
+    <MType extends GeneratedMessage,
+     BType extends GeneratedMessage.Builder,
+     IType extends MessageOrBuilder>
+    implements GeneratedMessage.BuilderParent {
+
+  // Parent to send changes to.
+  private GeneratedMessage.BuilderParent parent;
+
+  // Invariant: one of builder or message fields must be non-null.
+
+  // If set, this is the case where we are backed by a builder. In this case,
+  // message field represents a cached message for the builder (or null if
+  // there is no cached message).
+  private BType builder;
+
+  // If builder is non-null, this represents a cached message from the builder.
+  // If builder is null, this is the authoritative message for the field.
+  private MType message;
+
+  // Indicates that we've built a message and so we are now obligated
+  // to dispatch dirty invalidations. See GeneratedMessage.BuilderListener.
+  private boolean isClean;
+
+  public SingleFieldBuilder(
+      MType message,
+      GeneratedMessage.BuilderParent parent,
+      boolean isClean) {
+    if (message == null) {
+      throw new NullPointerException();
+    }
+    this.message = message;
+    this.parent = parent;
+    this.isClean = isClean;
+  }
+
+  public void dispose() {
+    // Null out parent so we stop sending it invalidations.
+    parent = null;
+  }
+
+  /**
+   * Get the message for the field. If the message is currently stored
+   * as a <code>Builder</code>, it is converted to a <code>Message</code> by
+   * calling {@link Message.Builder#buildPartial} on it. If no message has
+   * been set, returns the default instance of the message.
+   *
+   * @return the message for the field
+   */
+  @SuppressWarnings("unchecked")
+  public MType getMessage() {
+    if (message == null) {
+      // If message is null, the invariant is that we must be have a builder.
+      message = (MType) builder.buildPartial();
+    }
+    return message;
+  }
+
+  /**
+   * Builds the message and returns it.
+   *
+   * @return the message
+   */
+  public MType build() {
+    // Now that build has been called, we are required to dispatch
+    // invalidations.
+    isClean = true;
+    return getMessage();
+  }
+
+  /**
+   * Gets a builder for the field. If no builder has been created yet, a
+   * builder is created on demand by calling {@link Message#toBuilder}.
+   *
+   * @return The builder for the field
+   */
+  @SuppressWarnings("unchecked")
+  public BType getBuilder() {
+    if (builder == null) {
+      // builder.mergeFrom() on a fresh builder
+      // does not create any sub-objects with independent clean/dirty states,
+      // therefore setting the builder itself to clean without actually calling
+      // build() cannot break any invariants.
+      builder = (BType) message.newBuilderForType(this);
+      builder.mergeFrom(message); // no-op if message is the default message
+      builder.markClean();
+    }
+    return builder;
+  }
+
+  /**
+   * Gets the base class interface for the field. This may either be a builder
+   * or a message. It will return whatever is more efficient.
+   *
+   * @return the message or builder for the field as the base class interface
+   */
+  @SuppressWarnings("unchecked")
+  public IType getMessageOrBuilder() {
+    if (builder != null) {
+      return  (IType) builder;
+    } else {
+      return (IType) message;
+    }
+  }
+
+  /**
+   * Sets a  message for the field replacing any existing value.
+   *
+   * @param message the message to set
+   * @return the builder
+   */
+  public SingleFieldBuilder<MType, BType, IType> setMessage(
+      MType message) {
+    if (message == null) {
+      throw new NullPointerException();
+    }
+    this.message = message;
+    if (builder != null) {
+      builder.dispose();
+      builder = null;
+    }
+    onChanged();
+    return this;
+  }
+
+  /**
+   * Merges the field from another field.
+   *
+   * @param value the value to merge from
+   * @return the builder
+   */
+  public SingleFieldBuilder<MType, BType, IType> mergeFrom(
+      MType value) {
+    if (builder == null && message == message.getDefaultInstanceForType()) {
+      message = value;
+    } else {
+      getBuilder().mergeFrom(value);
+    }
+    onChanged();
+    return this;
+  }
+
+  /**
+   * Clears the value of the field.
+   *
+   * @return the builder
+   */
+  @SuppressWarnings("unchecked")
+  public SingleFieldBuilder<MType, BType, IType> clear() {
+    message = (MType) (message != null ?
+        message.getDefaultInstanceForType() :
+        builder.getDefaultInstanceForType());
+    if (builder != null) {
+      builder.dispose();
+      builder = null;
+    }
+    onChanged();
+    return this;
+  }
+
+  /**
+   * Called when a the builder or one of its nested children has changed
+   * and any parent should be notified of its invalidation.
+   */
+  private void onChanged() {
+    // If builder is null, this is the case where onChanged is being called
+    // from setMessage or clear.
+    if (builder != null) {
+      message = null;
+    }
+    if (isClean && parent != null) {
+      parent.markDirty();
+
+      // Don't keep dispatching invalidations until build is called again.
+      isClean = false;
+    }
+  }
+
+  @Override
+  public void markDirty() {
+    onChanged();
+  }
+}
diff --git a/java/src/main/java/com/google/protobuf/SmallSortedMap.java b/java/src/main/java/com/google/protobuf/SmallSortedMap.java
new file mode 100644
index 0000000..ccc2016
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/SmallSortedMap.java
@@ -0,0 +1,618 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.TreeMap;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.SortedMap;
+
+/**
+ * A custom map implementation from FieldDescriptor to Object optimized to
+ * minimize the number of memory allocations for instances with a small number
+ * of mappings. The implementation stores the first {@code k} mappings in an
+ * array for a configurable value of {@code k}, allowing direct access to the
+ * corresponding {@code Entry}s without the need to create an Iterator. The
+ * remaining entries are stored in an overflow map. Iteration over the entries
+ * in the map should be done as follows:
+ *
+ * <pre>
+ * for (int i = 0; i &lt; fieldMap.getNumArrayEntries(); i++) {
+ *   process(fieldMap.getArrayEntryAt(i));
+ * }
+ * for (Map.Entry&lt;K, V&gt; entry : fieldMap.getOverflowEntries()) {
+ *   process(entry);
+ * }
+ * </pre>
+ *
+ * The resulting iteration is in order of ascending field tag number. The
+ * object returned by {@link #entrySet()} adheres to the same contract but is
+ * less efficient as it necessarily involves creating an object for iteration.
+ * <p>
+ * The tradeoff for this memory efficiency is that the worst case running time
+ * of the {@code put()} operation is {@code O(k + lg n)}, which happens when
+ * entries are added in descending order. {@code k} should be chosen such that
+ * it covers enough common cases without adversely affecting larger maps. In
+ * practice, the worst case scenario does not happen for extensions because
+ * extension fields are serialized and deserialized in order of ascending tag
+ * number, but the worst case scenario can happen for DynamicMessages.
+ * <p>
+ * The running time for all other operations is similar to that of
+ * {@code TreeMap}.
+ * <p>
+ * Instances are not thread-safe until {@link #makeImmutable()} is called,
+ * after which any modifying operation will result in an
+ * {@link UnsupportedOperationException}.
+ *
+ * @author [email protected] Darick Tong
+ */
+// This class is final for all intents and purposes because the constructor is
+// private. However, the FieldDescriptor-specific logic is encapsulated in
+// a subclass to aid testability of the core logic.
+class SmallSortedMap<K extends Comparable<K>, V> extends AbstractMap<K, V> {
+
+  /**
+   * Creates a new instance for mapping FieldDescriptors to their values.
+   * The {@link #makeImmutable()} implementation will convert the List values
+   * of any repeated fields to unmodifiable lists.
+   *
+   * @param arraySize The size of the entry array containing the
+   *        lexicographically smallest mappings.
+   */
+  static <FieldDescriptorType extends
+      FieldSet.FieldDescriptorLite<FieldDescriptorType>>
+      SmallSortedMap<FieldDescriptorType, Object> newFieldMap(int arraySize) {
+    return new SmallSortedMap<FieldDescriptorType, Object>(arraySize) {
+      @Override
+      @SuppressWarnings("unchecked")
+      public void makeImmutable() {
+        if (!isImmutable()) {
+          for (int i = 0; i < getNumArrayEntries(); i++) {
+            final Map.Entry<FieldDescriptorType, Object> entry =
+                getArrayEntryAt(i);
+            if (entry.getKey().isRepeated()) {
+              final List value = (List) entry.getValue();
+              entry.setValue(Collections.unmodifiableList(value));
+            }
+          }
+          for (Map.Entry<FieldDescriptorType, Object> entry :
+                   getOverflowEntries()) {
+            if (entry.getKey().isRepeated()) {
+              final List value = (List) entry.getValue();
+              entry.setValue(Collections.unmodifiableList(value));
+            }
+          }
+        }
+        super.makeImmutable();
+      }
+    };
+  }
+
+  /**
+   * Creates a new instance for testing.
+   *
+   * @param arraySize The size of the entry array containing the
+   *        lexicographically smallest mappings.
+   */
+  static <K extends Comparable<K>, V> SmallSortedMap<K, V> newInstanceForTest(
+      int arraySize) {
+    return new SmallSortedMap<K, V>(arraySize);
+  }
+
+  private final int maxArraySize;
+  // The "entry array" is actually a List because generic arrays are not
+  // allowed. ArrayList also nicely handles the entry shifting on inserts and
+  // removes.
+  private List<Entry> entryList;
+  private Map<K, V> overflowEntries;
+  private boolean isImmutable;
+  // The EntrySet is a stateless view of the Map. It's initialized the first
+  // time it is requested and reused henceforth.
+  private volatile EntrySet lazyEntrySet;
+
+  /**
+   * @code arraySize Size of the array in which the lexicographically smallest
+   *       mappings are stored. (i.e. the {@code k} referred to in the class
+   *       documentation).
+   */
+  private SmallSortedMap(int arraySize) {
+    this.maxArraySize = arraySize;
+    this.entryList = Collections.emptyList();
+    this.overflowEntries = Collections.emptyMap();
+  }
+
+  /** Make this map immutable from this point forward. */
+  public void makeImmutable() {
+    if (!isImmutable) {
+      // Note: There's no need to wrap the entryList in an unmodifiableList
+      // because none of the list's accessors are exposed. The iterator() of
+      // overflowEntries, on the other hand, is exposed so it must be made
+      // unmodifiable.
+      overflowEntries = overflowEntries.isEmpty() ?
+          Collections.<K, V>emptyMap() :
+          Collections.unmodifiableMap(overflowEntries);
+      isImmutable = true;
+    }
+  }
+
+  /** @return Whether {@link #makeImmutable()} has been called. */
+  public boolean isImmutable() {
+    return isImmutable;
+  }
+
+  /** @return The number of entries in the entry array. */
+  public int getNumArrayEntries() {
+    return entryList.size();
+  }
+
+  /** @return The array entry at the given {@code index}. */
+  public Map.Entry<K, V> getArrayEntryAt(int index) {
+    return entryList.get(index);
+  }
+
+  /** @return There number of overflow entries. */
+  public int getNumOverflowEntries() {
+    return overflowEntries.size();
+  }
+
+  /** @return An iterable over the overflow entries. */
+  public Iterable<Map.Entry<K, V>> getOverflowEntries() {
+    return overflowEntries.isEmpty() ?
+        EmptySet.<Map.Entry<K, V>>iterable() :
+        overflowEntries.entrySet();
+  }
+
+  @Override
+  public int size() {
+    return entryList.size() + overflowEntries.size();
+  }
+
+  /**
+   * The implementation throws a {@code ClassCastException} if o is not an
+   * object of type {@code K}.
+   *
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean containsKey(Object o) {
+    @SuppressWarnings("unchecked")
+    final K key = (K) o;
+    return binarySearchInArray(key) >= 0 || overflowEntries.containsKey(key);
+  }
+
+  /**
+   * The implementation throws a {@code ClassCastException} if o is not an
+   * object of type {@code K}.
+   *
+   * {@inheritDoc}
+   */
+  @Override
+  public V get(Object o) {
+    @SuppressWarnings("unchecked")
+    final K key = (K) o;
+    final int index = binarySearchInArray(key);
+    if (index >= 0) {
+      return entryList.get(index).getValue();
+    }
+    return overflowEntries.get(key);
+  }
+
+  @Override
+  public V put(K key, V value) {
+    checkMutable();
+    final int index = binarySearchInArray(key);
+    if (index >= 0) {
+      // Replace existing array entry.
+      return entryList.get(index).setValue(value);
+    }
+    ensureEntryArrayMutable();
+    final int insertionPoint = -(index + 1);
+    if (insertionPoint >= maxArraySize) {
+      // Put directly in overflow.
+      return getOverflowEntriesMutable().put(key, value);
+    }
+    // Insert new Entry in array.
+    if (entryList.size() == maxArraySize) {
+      // Shift the last array entry into overflow.
+      final Entry lastEntryInArray = entryList.remove(maxArraySize - 1);
+      getOverflowEntriesMutable().put(lastEntryInArray.getKey(),
+                                      lastEntryInArray.getValue());
+    }
+    entryList.add(insertionPoint, new Entry(key, value));
+    return null;
+  }
+
+  @Override
+  public void clear() {
+    checkMutable();
+    if (!entryList.isEmpty()) {
+      entryList.clear();
+    }
+    if (!overflowEntries.isEmpty()) {
+      overflowEntries.clear();
+    }
+  }
+
+  /**
+   * The implementation throws a {@code ClassCastException} if o is not an
+   * object of type {@code K}.
+   *
+   * {@inheritDoc}
+   */
+  @Override
+  public V remove(Object o) {
+    checkMutable();
+    @SuppressWarnings("unchecked")
+    final K key = (K) o;
+    final int index = binarySearchInArray(key);
+    if (index >= 0) {
+      return removeArrayEntryAt(index);
+    }
+    // overflowEntries might be Collections.unmodifiableMap(), so only
+    // call remove() if it is non-empty.
+    if (overflowEntries.isEmpty()) {
+      return null;
+    } else {
+      return overflowEntries.remove(key);
+    }
+  }
+
+  private V removeArrayEntryAt(int index) {
+    checkMutable();
+    final V removed = entryList.remove(index).getValue();
+    if (!overflowEntries.isEmpty()) {
+      // Shift the first entry in the overflow to be the last entry in the
+      // array.
+      final Iterator<Map.Entry<K, V>> iterator =
+          getOverflowEntriesMutable().entrySet().iterator();
+      entryList.add(new Entry(iterator.next()));
+      iterator.remove();
+    }
+    return removed;
+  }
+
+  /**
+   * @param key The key to find in the entry array.
+   * @return The returned integer position follows the same semantics as the
+   *     value returned by {@link java.util.Arrays#binarySearch()}.
+   */
+  private int binarySearchInArray(K key) {
+    int left = 0;
+    int right = entryList.size() - 1;
+
+    // Optimization: For the common case in which entries are added in
+    // ascending tag order, check the largest element in the array before
+    // doing a full binary search.
+    if (right >= 0) {
+      int cmp = key.compareTo(entryList.get(right).getKey());
+      if (cmp > 0) {
+        return -(right + 2);  // Insert point is after "right".
+      } else if (cmp == 0) {
+        return right;
+      }
+    }
+
+    while (left <= right) {
+      int mid = (left + right) / 2;
+      int cmp = key.compareTo(entryList.get(mid).getKey());
+      if (cmp < 0) {
+        right = mid - 1;
+      } else if (cmp > 0) {
+        left = mid + 1;
+      } else {
+        return mid;
+      }
+    }
+    return -(left + 1);
+  }
+
+  /**
+   * Similar to the AbstractMap implementation of {@code keySet()} and
+   * {@code values()}, the entry set is created the first time this method is
+   * called, and returned in response to all subsequent calls.
+   *
+   * {@inheritDoc}
+   */
+  @Override
+  public Set<Map.Entry<K, V>> entrySet() {
+    if (lazyEntrySet == null) {
+      lazyEntrySet = new EntrySet();
+    }
+    return lazyEntrySet;
+  }
+
+  /**
+   * @throws UnsupportedOperationException if {@link #makeImmutable()} has
+   *         has been called.
+   */
+  private void checkMutable() {
+    if (isImmutable) {
+      throw new UnsupportedOperationException();
+    }
+  }
+
+  /**
+   * @return a {@link SortedMap} to which overflow entries mappings can be
+   *         added or removed.
+   * @throws UnsupportedOperationException if {@link #makeImmutable()} has been
+   *         called.
+   */
+  @SuppressWarnings("unchecked")
+  private SortedMap<K, V> getOverflowEntriesMutable() {
+    checkMutable();
+    if (overflowEntries.isEmpty() && !(overflowEntries instanceof TreeMap)) {
+      overflowEntries = new TreeMap<K, V>();
+    }
+    return (SortedMap<K, V>) overflowEntries;
+  }
+
+  /**
+   * Lazily creates the entry list. Any code that adds to the list must first
+   * call this method.
+   */
+  private void ensureEntryArrayMutable() {
+    checkMutable();
+    if (entryList.isEmpty() && !(entryList instanceof ArrayList)) {
+      entryList = new ArrayList<Entry>(maxArraySize);
+    }
+  }
+
+  /**
+   * Entry implementation that implements Comparable in order to support
+   * binary search witin the entry array. Also checks mutability in
+   * {@link #setValue()}.
+   */
+  private class Entry implements Map.Entry<K, V>, Comparable<Entry> {
+
+    private final K key;
+    private V value;
+
+    Entry(Map.Entry<K, V> copy) {
+      this(copy.getKey(), copy.getValue());
+    }
+
+    Entry(K key, V value) {
+      this.key = key;
+      this.value = value;
+    }
+
+    @Override
+    public K getKey() {
+      return key;
+    }
+
+    @Override
+    public V getValue() {
+      return value;
+    }
+
+    @Override
+    public int compareTo(Entry other) {
+      return getKey().compareTo(other.getKey());
+    }
+
+    @Override
+    public V setValue(V newValue) {
+      checkMutable();
+      final V oldValue = this.value;
+      this.value = newValue;
+      return oldValue;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      if (!(o instanceof Map.Entry)) {
+        return false;
+      }
+      @SuppressWarnings("unchecked")
+      Map.Entry<?, ?> other = (Map.Entry<?, ?>) o;
+      return equals(key, other.getKey()) && equals(value, other.getValue());
+    }
+
+    @Override
+    public int hashCode() {
+      return (key == null ? 0 : key.hashCode()) ^
+          (value == null ? 0 : value.hashCode());
+    }
+
+    @Override
+    public String toString() {
+      return key + "=" + value;
+    }
+
+    /** equals() that handles null values. */
+    private boolean equals(Object o1, Object o2) {
+      return o1 == null ? o2 == null : o1.equals(o2);
+    }
+  }
+
+  /**
+   * Stateless view of the entries in the field map.
+   */
+  private class EntrySet extends AbstractSet<Map.Entry<K, V>> {
+
+    @Override
+    public Iterator<Map.Entry<K, V>> iterator() {
+      return new EntryIterator();
+    }
+
+    @Override
+    public int size() {
+      return SmallSortedMap.this.size();
+    }
+
+    /**
+     * Throws a {@link ClassCastException} if o is not of the expected type.
+     *
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean contains(Object o) {
+      @SuppressWarnings("unchecked")
+      final Map.Entry<K, V> entry = (Map.Entry<K, V>) o;
+      final V existing = get(entry.getKey());
+      final V value = entry.getValue();
+      return existing == value ||
+          (existing != null && existing.equals(value));
+    }
+
+    @Override
+    public boolean add(Map.Entry<K, V> entry) {
+      if (!contains(entry)) {
+        put(entry.getKey(), entry.getValue());
+        return true;
+      }
+      return false;
+    }
+
+    /**
+     * Throws a {@link ClassCastException} if o is not of the expected type.
+     *
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean remove(Object o) {
+      @SuppressWarnings("unchecked")
+      final Map.Entry<K, V> entry = (Map.Entry<K, V>) o;
+      if (contains(entry)) {
+        SmallSortedMap.this.remove(entry.getKey());
+        return true;
+      }
+      return false;
+    }
+
+    @Override
+    public void clear() {
+      SmallSortedMap.this.clear();
+    }
+  }
+
+  /**
+   * Iterator implementation that switches from the entry array to the overflow
+   * entries appropriately.
+   */
+  private class EntryIterator implements Iterator<Map.Entry<K, V>> {
+
+    private int pos = -1;
+    private boolean nextCalledBeforeRemove;
+    private Iterator<Map.Entry<K, V>> lazyOverflowIterator;
+
+    @Override
+    public boolean hasNext() {
+      return (pos + 1) < entryList.size() ||
+          getOverflowIterator().hasNext();
+    }
+
+    @Override
+    public Map.Entry<K, V> next() {
+      nextCalledBeforeRemove = true;
+      // Always increment pos so that we know whether the last returned value
+      // was from the array or from overflow.
+      if (++pos < entryList.size()) {
+        return entryList.get(pos);
+      }
+      return getOverflowIterator().next();
+    }
+
+    @Override
+    public void remove() {
+      if (!nextCalledBeforeRemove) {
+        throw new IllegalStateException("remove() was called before next()");
+      }
+      nextCalledBeforeRemove = false;
+      checkMutable();
+
+      if (pos < entryList.size()) {
+        removeArrayEntryAt(pos--);
+      } else {
+        getOverflowIterator().remove();
+      }
+    }
+
+    /**
+     * It is important to create the overflow iterator only after the array
+     * entries have been iterated over because the overflow entry set changes
+     * when the client calls remove() on the array entries, which invalidates
+     * any existing iterators.
+     */
+    private Iterator<Map.Entry<K, V>> getOverflowIterator() {
+      if (lazyOverflowIterator == null) {
+        lazyOverflowIterator = overflowEntries.entrySet().iterator();
+      }
+      return lazyOverflowIterator;
+    }
+  }
+
+  /**
+   * Helper class that holds immutable instances of an Iterable/Iterator that
+   * we return when the overflow entries is empty. This eliminates the creation
+   * of an Iterator object when there is nothing to iterate over.
+   */
+  private static class EmptySet {
+
+    private static final Iterator<Object> ITERATOR = new Iterator<Object>() {
+      @Override
+      public boolean hasNext() {
+        return false;
+      }
+      @Override
+      public Object next() {
+        throw new NoSuchElementException();
+      }
+      @Override
+      public void remove() {
+        throw new UnsupportedOperationException();
+      }
+    };
+
+    private static final Iterable<Object> ITERABLE = new Iterable<Object>() {
+      @Override
+      public Iterator<Object> iterator() {
+        return ITERATOR;
+      }
+    };
+
+    @SuppressWarnings("unchecked")
+    static <T> Iterable<T> iterable() {
+      return (Iterable<T>) ITERABLE;
+    }
+  }
+}
diff --git a/java/src/main/java/com/google/protobuf/TextFormat.java b/java/src/main/java/com/google/protobuf/TextFormat.java
index 7ca2b4b..d5fbdab 100644
--- a/java/src/main/java/com/google/protobuf/TextFormat.java
+++ b/java/src/main/java/com/google/protobuf/TextFormat.java
@@ -46,15 +46,17 @@
 import java.util.regex.Pattern;
 
 /**
- * Provide ascii text parsing and formatting support for proto2 instances.
+ * Provide text parsing and formatting support for proto2 instances.
  * The implementation largely follows google/protobuf/text_format.cc.
  *
  * @author [email protected] Wenbo Zhu
  * @author [email protected] Kenton Varda
  */
 public final class TextFormat {
-  private TextFormat() {
-  }
+  private TextFormat() {}
+
+  private static final Printer DEFAULT_PRINTER = new Printer(false);
+  private static final Printer SINGLE_LINE_PRINTER = new Printer(true);
 
   /**
    * Outputs a textual representation of the Protocol Message supplied into
@@ -63,16 +65,44 @@
    */
   public static void print(final Message message, final Appendable output)
                            throws IOException {
-    final TextGenerator generator = new TextGenerator(output);
-    print(message, generator);
+    DEFAULT_PRINTER.print(message, new TextGenerator(output));
   }
 
   /** Outputs a textual representation of {@code fields} to {@code output}. */
   public static void print(final UnknownFieldSet fields,
                            final Appendable output)
                            throws IOException {
-    final TextGenerator generator = new TextGenerator(output);
-    printUnknownFields(fields, generator);
+    DEFAULT_PRINTER.printUnknownFields(fields, new TextGenerator(output));
+  }
+
+  /**
+   * Generates a human readable form of this message, useful for debugging and
+   * other purposes, with no newline characters.
+   */
+  public static String shortDebugString(final Message message) {
+    try {
+      final StringBuilder sb = new StringBuilder();
+      SINGLE_LINE_PRINTER.print(message, new TextGenerator(sb));
+      // Single line mode currently might have an extra space at the end.
+      return sb.toString().trim();
+    } catch (IOException e) {
+      throw new IllegalStateException(e);
+    }
+  }
+
+  /**
+   * Generates a human readable form of the unknown fields, useful for debugging
+   * and other purposes, with no newline characters.
+   */
+  public static String shortDebugString(final UnknownFieldSet fields) {
+    try {
+      final StringBuilder sb = new StringBuilder();
+      SINGLE_LINE_PRINTER.printUnknownFields(fields, new TextGenerator(sb));
+      // Single line mode currently might have an extra space at the end.
+      return sb.toString().trim();
+    } catch (IOException e) {
+      throw new IllegalStateException(e);
+    }
   }
 
   /**
@@ -85,9 +115,7 @@
       print(message, text);
       return text.toString();
     } catch (IOException e) {
-      throw new RuntimeException(
-        "Writing to a StringBuilder threw an IOException (should never " +
-        "happen).", e);
+      throw new IllegalStateException(e);
     }
   }
 
@@ -101,28 +129,15 @@
       print(fields, text);
       return text.toString();
     } catch (IOException e) {
-      throw new RuntimeException(
-        "Writing to a StringBuilder threw an IOException (should never " +
-        "happen).", e);
+      throw new IllegalStateException(e);
     }
   }
 
-  private static void print(final Message message,
-                            final TextGenerator generator)
-      throws IOException {
-    for (final Map.Entry<FieldDescriptor, Object> field :
-         message.getAllFields().entrySet()) {
-      printField(field.getKey(), field.getValue(), generator);
-    }
-    printUnknownFields(message.getUnknownFields(), generator);
-  }
-
   public static void printField(final FieldDescriptor field,
                                 final Object value,
                                 final Appendable output)
                                 throws IOException {
-    final TextGenerator generator = new TextGenerator(output);
-    printField(field, value, generator);
+    DEFAULT_PRINTER.printField(field, value, new TextGenerator(output));
   }
 
   public static String printFieldToString(final FieldDescriptor field,
@@ -132,157 +147,263 @@
       printField(field, value, text);
       return text.toString();
     } catch (IOException e) {
-      throw new RuntimeException(
-        "Writing to a StringBuilder threw an IOException (should never " +
-        "happen).", e);
+      throw new IllegalStateException(e);
     }
   }
 
-  private static void printField(final FieldDescriptor field,
-                                final Object value,
-                                final TextGenerator generator)
-                                throws IOException {
-    if (field.isRepeated()) {
-      // Repeated field.  Print each element.
-      for (final Object element : (List<?>) value) {
-        printSingleField(field, element, generator);
-      }
-    } else {
-      printSingleField(field, value, generator);
-    }
+  /**
+   * Outputs a textual representation of the value of given field value.
+   *
+   * @param field the descriptor of the field
+   * @param value the value of the field
+   * @param output the output to which to append the formatted value
+   * @throws ClassCastException if the value is not appropriate for the
+   *     given field descriptor
+   * @throws IOException if there is an exception writing to the output
+   */
+  public static void printFieldValue(final FieldDescriptor field,
+                                     final Object value,
+                                     final Appendable output)
+                                     throws IOException {
+    DEFAULT_PRINTER.printFieldValue(field, value, new TextGenerator(output));
   }
 
-  private static void printSingleField(final FieldDescriptor field,
-                                       final Object value,
-                                       final TextGenerator generator)
-                                       throws IOException {
-    if (field.isExtension()) {
-      generator.print("[");
-      // We special-case MessageSet elements for compatibility with proto1.
-      if (field.getContainingType().getOptions().getMessageSetWireFormat()
-          && (field.getType() == FieldDescriptor.Type.MESSAGE)
-          && (field.isOptional())
-          // object equality
-          && (field.getExtensionScope() == field.getMessageType())) {
-        generator.print(field.getMessageType().getFullName());
-      } else {
-        generator.print(field.getFullName());
-      }
-      generator.print("]");
-    } else {
-      if (field.getType() == FieldDescriptor.Type.GROUP) {
-        // Groups must be serialized with their original capitalization.
-        generator.print(field.getMessageType().getName());
-      } else {
-        generator.print(field.getName());
-      }
-    }
-
-    if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
-      generator.print(" {\n");
-      generator.indent();
-    } else {
-      generator.print(": ");
-    }
-
-    printFieldValue(field, value, generator);
-
-    if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
-      generator.outdent();
-      generator.print("}");
-    }
-    generator.print("\n");
+  /**
+   * Outputs a textual representation of the value of an unknown field.
+   *
+   * @param tag the field's tag number
+   * @param value the value of the field
+   * @param output the output to which to append the formatted value
+   * @throws ClassCastException if the value is not appropriate for the
+   *     given field descriptor
+   * @throws IOException if there is an exception writing to the output
+   */
+  public static void printUnknownFieldValue(final int tag,
+                                            final Object value,
+                                            final Appendable output)
+                                            throws IOException {
+    printUnknownFieldValue(tag, value, new TextGenerator(output));
   }
 
-  private static void printFieldValue(final FieldDescriptor field,
-                                      final Object value,
-                                      final TextGenerator generator)
-                                      throws IOException {
-    switch (field.getType()) {
-      case INT32:
-      case INT64:
-      case SINT32:
-      case SINT64:
-      case SFIXED32:
-      case SFIXED64:
-      case FLOAT:
-      case DOUBLE:
-      case BOOL:
-        // Good old toString() does what we want for these types.
-        generator.print(value.toString());
-        break;
-
-      case UINT32:
-      case FIXED32:
-        generator.print(unsignedToString((Integer) value));
-        break;
-
-      case UINT64:
-      case FIXED64:
+  private static void printUnknownFieldValue(final int tag,
+                                             final Object value,
+                                             final TextGenerator generator)
+                                             throws IOException {
+    switch (WireFormat.getTagWireType(tag)) {
+      case WireFormat.WIRETYPE_VARINT:
         generator.print(unsignedToString((Long) value));
         break;
-
-      case STRING:
-        generator.print("\"");
-        generator.print(escapeText((String) value));
-        generator.print("\"");
+      case WireFormat.WIRETYPE_FIXED32:
+        generator.print(
+            String.format((Locale) null, "0x%08x", (Integer) value));
         break;
-
-      case BYTES:
+      case WireFormat.WIRETYPE_FIXED64:
+        generator.print(String.format((Locale) null, "0x%016x", (Long) value));
+        break;
+      case WireFormat.WIRETYPE_LENGTH_DELIMITED:
         generator.print("\"");
         generator.print(escapeBytes((ByteString) value));
         generator.print("\"");
         break;
-
-      case ENUM:
-        generator.print(((EnumValueDescriptor) value).getName());
+      case WireFormat.WIRETYPE_START_GROUP:
+        DEFAULT_PRINTER.printUnknownFields((UnknownFieldSet) value, generator);
         break;
-
-      case MESSAGE:
-      case GROUP:
-        print((Message) value, generator);
-        break;
+      default:
+        throw new IllegalArgumentException("Bad tag: " + tag);
     }
   }
 
-  private static void printUnknownFields(final UnknownFieldSet unknownFields,
-                                         final TextGenerator generator)
-                                         throws IOException {
-    for (final Map.Entry<Integer, UnknownFieldSet.Field> entry :
-         unknownFields.asMap().entrySet()) {
-      final UnknownFieldSet.Field field = entry.getValue();
+  /** Helper class for converting protobufs to text. */
+  private static final class Printer {
+    /** Whether to omit newlines from the output. */
+    final boolean singleLineMode;
 
-      for (final long value : field.getVarintList()) {
-        generator.print(entry.getKey().toString());
+    private Printer(final boolean singleLineMode) {
+      this.singleLineMode = singleLineMode;
+    }
+
+    private void print(final Message message, final TextGenerator generator)
+        throws IOException {
+      for (Map.Entry<FieldDescriptor, Object> field
+          : message.getAllFields().entrySet()) {
+        printField(field.getKey(), field.getValue(), generator);
+      }
+      printUnknownFields(message.getUnknownFields(), generator);
+    }
+
+    private void printField(final FieldDescriptor field, final Object value,
+        final TextGenerator generator) throws IOException {
+      if (field.isRepeated()) {
+        // Repeated field.  Print each element.
+        for (Object element : (List<?>) value) {
+          printSingleField(field, element, generator);
+        }
+      } else {
+        printSingleField(field, value, generator);
+      }
+    }
+
+    private void printSingleField(final FieldDescriptor field,
+                                  final Object value,
+                                  final TextGenerator generator)
+                                  throws IOException {
+      if (field.isExtension()) {
+        generator.print("[");
+        // We special-case MessageSet elements for compatibility with proto1.
+        if (field.getContainingType().getOptions().getMessageSetWireFormat()
+            && (field.getType() == FieldDescriptor.Type.MESSAGE)
+            && (field.isOptional())
+            // object equality
+            && (field.getExtensionScope() == field.getMessageType())) {
+          generator.print(field.getMessageType().getFullName());
+        } else {
+          generator.print(field.getFullName());
+        }
+        generator.print("]");
+      } else {
+        if (field.getType() == FieldDescriptor.Type.GROUP) {
+          // Groups must be serialized with their original capitalization.
+          generator.print(field.getMessageType().getName());
+        } else {
+          generator.print(field.getName());
+        }
+      }
+
+      if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
+        if (singleLineMode) {
+          generator.print(" { ");
+        } else {
+          generator.print(" {\n");
+          generator.indent();
+        }
+      } else {
         generator.print(": ");
-        generator.print(unsignedToString(value));
-        generator.print("\n");
       }
-      for (final int value : field.getFixed32List()) {
-        generator.print(entry.getKey().toString());
+
+      printFieldValue(field, value, generator);
+
+      if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
+        if (singleLineMode) {
+          generator.print("} ");
+        } else {
+          generator.outdent();
+          generator.print("}\n");
+        }
+      } else {
+        if (singleLineMode) {
+          generator.print(" ");
+        } else {
+          generator.print("\n");
+        }
+      }
+    }
+
+    private void printFieldValue(final FieldDescriptor field,
+                                 final Object value,
+                                 final TextGenerator generator)
+                                 throws IOException {
+      switch (field.getType()) {
+        case INT32:
+        case SINT32:
+        case SFIXED32:
+          generator.print(((Integer) value).toString());
+          break;
+
+        case INT64:
+        case SINT64:
+        case SFIXED64:
+          generator.print(((Long) value).toString());
+          break;
+
+        case BOOL:
+          generator.print(((Boolean) value).toString());
+          break;
+
+        case FLOAT:
+          generator.print(((Float) value).toString());
+          break;
+
+        case DOUBLE:
+          generator.print(((Double) value).toString());
+          break;
+
+        case UINT32:
+        case FIXED32:
+          generator.print(unsignedToString((Integer) value));
+          break;
+
+        case UINT64:
+        case FIXED64:
+          generator.print(unsignedToString((Long) value));
+          break;
+
+        case STRING:
+          generator.print("\"");
+          generator.print(escapeText((String) value));
+          generator.print("\"");
+          break;
+
+        case BYTES:
+          generator.print("\"");
+          generator.print(escapeBytes((ByteString) value));
+          generator.print("\"");
+          break;
+
+        case ENUM:
+          generator.print(((EnumValueDescriptor) value).getName());
+          break;
+
+        case MESSAGE:
+        case GROUP:
+          print((Message) value, generator);
+          break;
+      }
+    }
+
+    private void printUnknownFields(final UnknownFieldSet unknownFields,
+                                    final TextGenerator generator)
+                                    throws IOException {
+      for (Map.Entry<Integer, UnknownFieldSet.Field> entry :
+               unknownFields.asMap().entrySet()) {
+        final int number = entry.getKey();
+        final UnknownFieldSet.Field field = entry.getValue();
+        printUnknownField(number, WireFormat.WIRETYPE_VARINT,
+            field.getVarintList(), generator);
+        printUnknownField(number, WireFormat.WIRETYPE_FIXED32,
+            field.getFixed32List(), generator);
+        printUnknownField(number, WireFormat.WIRETYPE_FIXED64,
+            field.getFixed64List(), generator);
+        printUnknownField(number, WireFormat.WIRETYPE_LENGTH_DELIMITED,
+            field.getLengthDelimitedList(), generator);
+        for (final UnknownFieldSet value : field.getGroupList()) {
+          generator.print(entry.getKey().toString());
+          if (singleLineMode) {
+            generator.print(" { ");
+          } else {
+            generator.print(" {\n");
+            generator.indent();
+          }
+          printUnknownFields(value, generator);
+          if (singleLineMode) {
+            generator.print("} ");
+          } else {
+            generator.outdent();
+            generator.print("}\n");
+          }
+        }
+      }
+    }
+
+    private void printUnknownField(final int number,
+                                   final int wireType,
+                                   final List<?> values,
+                                   final TextGenerator generator)
+                                   throws IOException {
+      for (final Object value : values) {
+        generator.print(String.valueOf(number));
         generator.print(": ");
-        generator.print(String.format((Locale) null, "0x%08x", value));
-        generator.print("\n");
-      }
-      for (final long value : field.getFixed64List()) {
-        generator.print(entry.getKey().toString());
-        generator.print(": ");
-        generator.print(String.format((Locale) null, "0x%016x", value));
-        generator.print("\n");
-      }
-      for (final ByteString value : field.getLengthDelimitedList()) {
-        generator.print(entry.getKey().toString());
-        generator.print(": \"");
-        generator.print(escapeBytes(value));
-        generator.print("\"\n");
-      }
-      for (final UnknownFieldSet value : field.getGroupList()) {
-        generator.print(entry.getKey().toString());
-        generator.print(" {\n");
-        generator.indent();
-        printUnknownFields(value, generator);
-        generator.outdent();
-        generator.print("}\n");
+        printUnknownFieldValue(wireType, value, generator);
+        generator.print(singleLineMode ? " " : "\n");
       }
     }
   }
@@ -312,9 +433,9 @@
    * An inner class for writing text to the output stream.
    */
   private static final class TextGenerator {
-    private Appendable output;
-    private boolean atStartOfLine = true;
+    private final Appendable output;
     private final StringBuilder indent = new StringBuilder();
+    private boolean atStartOfLine = true;
 
     private TextGenerator(final Appendable output) {
       this.output = output;
@@ -670,10 +791,14 @@
      * Otherwise, throw a {@link ParseException}.
      */
     public boolean consumeBoolean() throws ParseException {
-      if (currentToken.equals("true")) {
+      if (currentToken.equals("true") ||
+          currentToken.equals("t") ||
+          currentToken.equals("1")) {
         nextToken();
         return true;
-      } else if (currentToken.equals("false")) {
+      } else if (currentToken.equals("false") ||
+                 currentToken.equals("f") ||
+                 currentToken.equals("0")) {
         nextToken();
         return false;
       } else {
@@ -1063,6 +1188,9 @@
         case '\'': builder.append("\\\'"); break;
         case '"' : builder.append("\\\""); break;
         default:
+          // Note:  Bytes with the high-order bit set should be escaped.  Since
+          //   bytes are signed, such bytes will compare less than 0x20, hence
+          //   the following line is correct.
           if (b >= 0x20) {
             builder.append((char) b);
           } else {
@@ -1082,27 +1210,37 @@
    * {@link #escapeBytes(ByteString)}.  Two-digit hex escapes (starting with
    * "\x") are also recognized.
    */
-  static ByteString unescapeBytes(final CharSequence input)
+  static ByteString unescapeBytes(final CharSequence charString)
       throws InvalidEscapeSequenceException {
-    final byte[] result = new byte[input.length()];
+    // First convert the Java characater sequence to UTF-8 bytes.
+    ByteString input = ByteString.copyFromUtf8(charString.toString());
+    // Then unescape certain byte sequences introduced by ASCII '\\'.  The valid
+    // escapes can all be expressed with ASCII characters, so it is safe to
+    // operate on bytes here.
+    //
+    // Unescaping the input byte array will result in a byte sequence that's no
+    // longer than the input.  That's because each escape sequence is between
+    // two and four bytes long and stands for a single byte.
+    final byte[] result = new byte[input.size()];
     int pos = 0;
-    for (int i = 0; i < input.length(); i++) {
-      char c = input.charAt(i);
+    for (int i = 0; i < input.size(); i++) {
+      byte c = input.byteAt(i);
       if (c == '\\') {
-        if (i + 1 < input.length()) {
+        if (i + 1 < input.size()) {
           ++i;
-          c = input.charAt(i);
+          c = input.byteAt(i);
           if (isOctal(c)) {
             // Octal escape.
             int code = digitValue(c);
-            if (i + 1 < input.length() && isOctal(input.charAt(i + 1))) {
+            if (i + 1 < input.size() && isOctal(input.byteAt(i + 1))) {
               ++i;
-              code = code * 8 + digitValue(input.charAt(i));
+              code = code * 8 + digitValue(input.byteAt(i));
             }
-            if (i + 1 < input.length() && isOctal(input.charAt(i + 1))) {
+            if (i + 1 < input.size() && isOctal(input.byteAt(i + 1))) {
               ++i;
-              code = code * 8 + digitValue(input.charAt(i));
+              code = code * 8 + digitValue(input.byteAt(i));
             }
+            // TODO: Check that 0 <= code && code <= 0xFF.
             result[pos++] = (byte)code;
           } else {
             switch (c) {
@@ -1120,31 +1258,31 @@
               case 'x':
                 // hex escape
                 int code = 0;
-                if (i + 1 < input.length() && isHex(input.charAt(i + 1))) {
+                if (i + 1 < input.size() && isHex(input.byteAt(i + 1))) {
                   ++i;
-                  code = digitValue(input.charAt(i));
+                  code = digitValue(input.byteAt(i));
                 } else {
                   throw new InvalidEscapeSequenceException(
-                    "Invalid escape sequence: '\\x' with no digits");
+                      "Invalid escape sequence: '\\x' with no digits");
                 }
-                if (i + 1 < input.length() && isHex(input.charAt(i + 1))) {
+                if (i + 1 < input.size() && isHex(input.byteAt(i + 1))) {
                   ++i;
-                  code = code * 16 + digitValue(input.charAt(i));
+                  code = code * 16 + digitValue(input.byteAt(i));
                 }
                 result[pos++] = (byte)code;
                 break;
 
               default:
                 throw new InvalidEscapeSequenceException(
-                  "Invalid escape sequence: '\\" + c + '\'');
+                    "Invalid escape sequence: '\\" + (char)c + '\'');
             }
           }
         } else {
           throw new InvalidEscapeSequenceException(
-            "Invalid escape sequence: '\\' at end of string.");
+              "Invalid escape sequence: '\\' at end of string.");
         }
       } else {
-        result[pos++] = (byte)c;
+        result[pos++] = c;
       }
     }
 
@@ -1182,12 +1320,12 @@
   }
 
   /** Is this an octal digit? */
-  private static boolean isOctal(final char c) {
+  private static boolean isOctal(final byte c) {
     return '0' <= c && c <= '7';
   }
 
   /** Is this a hex digit? */
-  private static boolean isHex(final char c) {
+  private static boolean isHex(final byte c) {
     return ('0' <= c && c <= '9') ||
            ('a' <= c && c <= 'f') ||
            ('A' <= c && c <= 'F');
@@ -1198,7 +1336,7 @@
    * numeric value.  This is like {@code Character.digit()} but we don't accept
    * non-ASCII digits.
    */
-  private static int digitValue(final char c) {
+  private static int digitValue(final byte c) {
     if ('0' <= c && c <= '9') {
       return c - '0';
     } else if ('a' <= c && c <= 'z') {
diff --git a/java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java b/java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java
new file mode 100644
index 0000000..ee8fe19
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java
@@ -0,0 +1,146 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import java.util.AbstractList;
+import java.util.RandomAccess;
+import java.util.ListIterator;
+import java.util.Iterator;
+
+/**
+ * An implementation of {@link LazyStringList} that wraps another
+ * {@link LazyStringList} such that it cannot be modified via the wrapper.
+ *
+ * @author [email protected] (Jon Perlow)
+ */
+public class UnmodifiableLazyStringList extends AbstractList<String>
+    implements LazyStringList, RandomAccess {
+
+  private final LazyStringList list;
+
+  public UnmodifiableLazyStringList(LazyStringList list) {
+    this.list = list;
+  }
+
+  @Override
+  public String get(int index) {
+    return list.get(index);
+  }
+
+  @Override
+  public int size() {
+    return list.size();
+  }
+
+  @Override
+  public ByteString getByteString(int index) {
+    return list.getByteString(index);
+  }
+
+  @Override
+  public void add(ByteString element) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public ListIterator<String> listIterator(final int index) {
+    return new ListIterator<String>() {
+      ListIterator<String> iter = list.listIterator(index);
+
+      @Override
+      public boolean hasNext() {
+        return iter.hasNext();
+      }
+
+      @Override
+      public String next() {
+        return iter.next();
+      }
+
+      @Override
+      public boolean hasPrevious() {
+        return iter.hasPrevious();
+      }
+
+      @Override
+      public String previous() {
+        return iter.previous();
+      }
+
+      @Override
+      public int nextIndex() {
+        return iter.nextIndex();
+      }
+
+      @Override
+      public int previousIndex() {
+        return iter.previousIndex();
+      }
+
+      @Override
+      public void remove() {
+        throw new UnsupportedOperationException();
+      }
+
+      @Override
+      public void set(String o) {
+        throw new UnsupportedOperationException();
+      }
+
+      @Override
+      public void add(String o) {
+        throw new UnsupportedOperationException();
+      }
+    };
+  }
+
+  @Override
+  public Iterator<String> iterator() {
+    return new Iterator<String>() {
+      Iterator<String> iter = list.iterator();
+
+      @Override
+      public boolean hasNext() {
+        return iter.hasNext();
+      }
+
+      @Override
+      public String next() {
+        return iter.next();
+      }
+
+      @Override
+      public void remove() {
+        throw new UnsupportedOperationException();
+      }
+    };
+  }
+}
diff --git a/java/src/main/java/com/google/protobuf/WireFormat.java b/java/src/main/java/com/google/protobuf/WireFormat.java
index c46f7b0..a30f2a3 100644
--- a/java/src/main/java/com/google/protobuf/WireFormat.java
+++ b/java/src/main/java/com/google/protobuf/WireFormat.java
@@ -45,12 +45,12 @@
   // Do not allow instantiation.
   private WireFormat() {}
 
-  static final int WIRETYPE_VARINT           = 0;
-  static final int WIRETYPE_FIXED64          = 1;
-  static final int WIRETYPE_LENGTH_DELIMITED = 2;
-  static final int WIRETYPE_START_GROUP      = 3;
-  static final int WIRETYPE_END_GROUP        = 4;
-  static final int WIRETYPE_FIXED32          = 5;
+  public static final int WIRETYPE_VARINT           = 0;
+  public static final int WIRETYPE_FIXED64          = 1;
+  public static final int WIRETYPE_LENGTH_DELIMITED = 2;
+  public static final int WIRETYPE_START_GROUP      = 3;
+  public static final int WIRETYPE_END_GROUP        = 4;
+  public static final int WIRETYPE_FIXED32          = 5;
 
   static final int TAG_TYPE_BITS = 3;
   static final int TAG_TYPE_MASK = (1 << TAG_TYPE_BITS) - 1;
diff --git a/java/src/test/java/com/google/protobuf/AbstractMessageTest.java b/java/src/test/java/com/google/protobuf/AbstractMessageTest.java
index c44d660..d53ce8d 100644
--- a/java/src/test/java/com/google/protobuf/AbstractMessageTest.java
+++ b/java/src/test/java/com/google/protobuf/AbstractMessageTest.java
@@ -366,7 +366,7 @@
 
   // -----------------------------------------------------------------
   // Tests for equals and hashCode
-  
+
   public void testEqualsAndHashCode() throws Exception {
     TestAllTypes a = TestUtil.getAllSet();
     TestAllTypes b = TestAllTypes.newBuilder().build();
@@ -382,7 +382,7 @@
     checkEqualsIsConsistent(d);
     checkEqualsIsConsistent(e);
     checkEqualsIsConsistent(f);
-    
+
     checkNotEqual(a, b);
     checkNotEqual(a, c);
     checkNotEqual(a, d);
@@ -413,19 +413,20 @@
     checkEqualsIsConsistent(eUnknownFields);
     checkEqualsIsConsistent(fUnknownFields);
 
-    // Subseqent reconstitutions should be identical
+    // Subsequent reconstitutions should be identical
     UnittestProto.TestEmptyMessage eUnknownFields2 =
         UnittestProto.TestEmptyMessage.parseFrom(e.toByteArray());
     checkEqualsIsConsistent(eUnknownFields, eUnknownFields2);
   }
-  
+
+
   /**
    * Asserts that the given proto has symetric equals and hashCode methods.
    */
   private void checkEqualsIsConsistent(Message message) {
     // Object should be equal to itself.
     assertEquals(message, message);
-    
+
     // Object should be equal to a dynamic copy of itself.
     DynamicMessage dynamic = DynamicMessage.newBuilder(message).build();
     checkEqualsIsConsistent(message, dynamic);
@@ -442,7 +443,7 @@
 
   /**
    * Asserts that the given protos are not equal and have different hash codes.
-   * 
+   *
    * @warning It's valid for non-equal objects to have the same hash code, so
    *   this test is stricter than it needs to be. However, this should happen
    *   relatively rarely.
diff --git a/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java b/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java
index 6acd322..83f7f8d 100644
--- a/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java
+++ b/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java
@@ -325,6 +325,27 @@
     assertEquals(2, input.readRawByte());
   }
 
+  /**
+   * Test that a bug in skipRawBytes() has been fixed:  if the skip skips
+   * past the end of a buffer with a limit that has been set past the end of
+   * that buffer, this should not break things.
+   */
+  public void testSkipRawBytesPastEndOfBufferWithLimit() throws Exception {
+    byte[] rawBytes = new byte[] { 1, 2, 3, 4, 5 };
+    CodedInputStream input = CodedInputStream.newInstance(
+        new SmallBlockInputStream(rawBytes, 3));
+
+    int limit = input.pushLimit(4);
+    // In order to expose the bug we need to read at least one byte to prime the
+    // buffer inside the CodedInputStream.
+    assertEquals(1, input.readRawByte());
+    // Skip to the end of the limit.
+    input.skipRawBytes(3);
+    assertTrue(input.isAtEnd());
+    input.popLimit(limit);
+    assertEquals(5, input.readRawByte());
+  }
+
   public void testReadHugeBlob() throws Exception {
     // Allocate and initialize a 1MB blob.
     byte[] blob = new byte[1 << 20];
diff --git a/java/src/test/java/com/google/protobuf/DeprecatedFieldTest.java b/java/src/test/java/com/google/protobuf/DeprecatedFieldTest.java
new file mode 100644
index 0000000..1f8bb44
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/DeprecatedFieldTest.java
@@ -0,0 +1,80 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import protobuf_unittest.UnittestProto.TestDeprecatedFields;
+
+import junit.framework.TestCase;
+
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Method;
+/**
+ * Test field deprecation
+ * 
+ * @author [email protected] (Roberto Scaramuzzi)
+ */
+public class DeprecatedFieldTest extends TestCase {
+  private String[] deprecatedGetterNames = {
+      "hasDeprecatedInt32",
+      "getDeprecatedInt32"};
+  
+  private String[] deprecatedBuilderGetterNames = {
+      "hasDeprecatedInt32",
+      "getDeprecatedInt32",
+      "clearDeprecatedInt32"};
+  
+  private String[] deprecatedBuilderSetterNames = {
+      "setDeprecatedInt32"}; 
+  
+  public void testDeprecatedField() throws Exception {
+    Class<?> deprecatedFields = TestDeprecatedFields.class;
+    Class<?> deprecatedFieldsBuilder = TestDeprecatedFields.Builder.class;
+    for (String name : deprecatedGetterNames) {
+      Method method = deprecatedFields.getMethod(name);
+      assertTrue("Method " + name + " should be deprecated",
+          isDeprecated(method));
+    }
+    for (String name : deprecatedBuilderGetterNames) {
+      Method method = deprecatedFieldsBuilder.getMethod(name);
+      assertTrue("Method " + name + " should be deprecated",
+          isDeprecated(method));
+    }
+    for (String name : deprecatedBuilderSetterNames) {
+      Method method = deprecatedFieldsBuilder.getMethod(name, int.class);
+      assertTrue("Method " + name + " should be deprecated",
+          isDeprecated(method));
+    }
+  }
+  
+  private boolean isDeprecated(AnnotatedElement annotated) {
+    return annotated.isAnnotationPresent(Deprecated.class);
+  }
+}
diff --git a/java/src/test/java/com/google/protobuf/DescriptorsTest.java b/java/src/test/java/com/google/protobuf/DescriptorsTest.java
index c77af5d..65d06e3 100644
--- a/java/src/test/java/com/google/protobuf/DescriptorsTest.java
+++ b/java/src/test/java/com/google/protobuf/DescriptorsTest.java
@@ -44,6 +44,7 @@
 
 import com.google.protobuf.test.UnittestImport;
 import com.google.protobuf.test.UnittestImport.ImportEnum;
+import com.google.protobuf.test.UnittestImport.ImportMessage;
 import protobuf_unittest.UnittestProto;
 import protobuf_unittest.UnittestProto.ForeignEnum;
 import protobuf_unittest.UnittestProto.ForeignMessage;
diff --git a/java/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java b/java/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java
new file mode 100644
index 0000000..108a28e
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java
@@ -0,0 +1,48 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+/**
+ * A prerun for a test suite that allows running the full protocol buffer
+ * tests in a mode that disables the optimization for not using
+ * {@link RepeatedFieldBuilder} and {@link SingleFieldBuilder} until they are
+ * requested. This allows us to run all the tests through both code paths
+ * and ensures that both code paths produce identical results.
+ *
+ * @author [email protected] (Jon Perlow)
+ */
+public class ForceFieldBuildersPreRun implements Runnable {
+
+  @Override
+  public void run() {
+    GeneratedMessage.enableAlwaysUseFieldBuildersForTesting();
+  }
+}
diff --git a/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java b/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java
index 73c71f3..3675e00 100644
--- a/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java
+++ b/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java
@@ -30,26 +30,43 @@
 
 package com.google.protobuf;
 
+import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
+import com.google.protobuf.test.UnittestImport;
+import protobuf_unittest.EnumWithNoOuter;
+import protobuf_unittest.MessageWithNoOuter;
+import protobuf_unittest.MultipleFilesTestProto;
+import protobuf_unittest.NestedExtension.MyNestedExtension;
+import protobuf_unittest.NestedExtensionLite.MyNestedExtensionLite;
+import protobuf_unittest.NonNestedExtension;
+import protobuf_unittest.NonNestedExtension.MessageToBeExtended;
+import protobuf_unittest.NonNestedExtension.MyNonNestedExtension;
+import protobuf_unittest.NonNestedExtensionLite;
+import protobuf_unittest.NonNestedExtensionLite.MessageLiteToBeExtended;
+import protobuf_unittest.NonNestedExtensionLite.MyNonNestedExtensionLite;
+import protobuf_unittest.ServiceWithNoOuter;
 import protobuf_unittest.UnittestOptimizeFor.TestOptimizedForSize;
 import protobuf_unittest.UnittestOptimizeFor.TestOptionalOptimizedForSize;
 import protobuf_unittest.UnittestOptimizeFor.TestRequiredOptimizedForSize;
 import protobuf_unittest.UnittestProto;
-import protobuf_unittest.UnittestProto.ForeignMessage;
 import protobuf_unittest.UnittestProto.ForeignEnum;
-import protobuf_unittest.UnittestProto.TestAllTypes;
+import protobuf_unittest.UnittestProto.ForeignMessage;
+import protobuf_unittest.UnittestProto.ForeignMessageOrBuilder;
 import protobuf_unittest.UnittestProto.TestAllExtensions;
+import protobuf_unittest.UnittestProto.TestAllTypes;
+import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder;
 import protobuf_unittest.UnittestProto.TestExtremeDefaultValues;
 import protobuf_unittest.UnittestProto.TestPackedTypes;
 import protobuf_unittest.UnittestProto.TestUnpackedTypes;
-import protobuf_unittest.MultipleFilesTestProto;
-import protobuf_unittest.MessageWithNoOuter;
-import protobuf_unittest.EnumWithNoOuter;
-import protobuf_unittest.ServiceWithNoOuter;
-import com.google.protobuf.UnittestLite;
-import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
 
 import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Unit test for generated messages and generated code.  See also
@@ -68,32 +85,111 @@
                TestAllTypes.newBuilder().getDefaultInstanceForType());
   }
 
-  public void testAccessors() throws Exception {
+  public void testMessageOrBuilder() throws Exception {
     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
     TestUtil.setAllFields(builder);
     TestAllTypes message = builder.build();
     TestUtil.assertAllFieldsSet(message);
   }
 
-  public void testDoubleBuildError() throws Exception {
+  public void testUsingBuilderMultipleTimes() throws Exception {
     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
-    builder.build();
-    try {
-      builder.build();
-      fail("Should have thrown exception.");
-    } catch (IllegalStateException e) {
-      // Success.
-    }
+    // primitive field scalar and repeated
+    builder.setOptionalSfixed64(100);
+    builder.addRepeatedInt32(100);
+    // enum field scalar and repeated
+    builder.setOptionalImportEnum(UnittestImport.ImportEnum.IMPORT_BAR);
+    builder.addRepeatedImportEnum(UnittestImport.ImportEnum.IMPORT_BAR);
+    // proto field scalar and repeated
+    builder.setOptionalForeignMessage(ForeignMessage.newBuilder().setC(1));
+    builder.addRepeatedForeignMessage(ForeignMessage.newBuilder().setC(1));
+
+    TestAllTypes value1 = builder.build();
+
+    assertEquals(100, value1.getOptionalSfixed64());
+    assertEquals(100, value1.getRepeatedInt32(0));
+    assertEquals(UnittestImport.ImportEnum.IMPORT_BAR,
+        value1.getOptionalImportEnum());
+    assertEquals(UnittestImport.ImportEnum.IMPORT_BAR,
+        value1.getRepeatedImportEnum(0));
+    assertEquals(1, value1.getOptionalForeignMessage().getC());
+    assertEquals(1, value1.getRepeatedForeignMessage(0).getC());
+
+    // Make sure that builder didn't update previously created values
+    builder.setOptionalSfixed64(200);
+    builder.setRepeatedInt32(0, 200);
+    builder.setOptionalImportEnum(UnittestImport.ImportEnum.IMPORT_FOO);
+    builder.setRepeatedImportEnum(0, UnittestImport.ImportEnum.IMPORT_FOO);
+    builder.setOptionalForeignMessage(ForeignMessage.newBuilder().setC(2));
+    builder.setRepeatedForeignMessage(0, ForeignMessage.newBuilder().setC(2));
+
+    TestAllTypes value2 = builder.build();
+
+    // Make sure value1 didn't change.
+    assertEquals(100, value1.getOptionalSfixed64());
+    assertEquals(100, value1.getRepeatedInt32(0));
+    assertEquals(UnittestImport.ImportEnum.IMPORT_BAR,
+        value1.getOptionalImportEnum());
+    assertEquals(UnittestImport.ImportEnum.IMPORT_BAR,
+        value1.getRepeatedImportEnum(0));
+    assertEquals(1, value1.getOptionalForeignMessage().getC());
+    assertEquals(1, value1.getRepeatedForeignMessage(0).getC());
+
+    // Make sure value2 is correct
+    assertEquals(200, value2.getOptionalSfixed64());
+    assertEquals(200, value2.getRepeatedInt32(0));
+    assertEquals(UnittestImport.ImportEnum.IMPORT_FOO,
+        value2.getOptionalImportEnum());
+    assertEquals(UnittestImport.ImportEnum.IMPORT_FOO,
+        value2.getRepeatedImportEnum(0));
+    assertEquals(2, value2.getOptionalForeignMessage().getC());
+    assertEquals(2, value2.getRepeatedForeignMessage(0).getC());
   }
 
-  public void testClearAfterBuildError() throws Exception {
+  public void testProtosShareRepeatedArraysIfDidntChange() throws Exception {
     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
-    builder.build();
-    try {
-      builder.clear();
-      fail("Should have thrown exception.");
-    } catch (IllegalStateException e) {
-      // Success.
+    builder.addRepeatedInt32(100);
+    builder.addRepeatedImportEnum(UnittestImport.ImportEnum.IMPORT_BAR);
+    builder.addRepeatedForeignMessage(ForeignMessage.getDefaultInstance());
+
+    TestAllTypes value1 = builder.build();
+    TestAllTypes value2 = value1.toBuilder().build();
+
+    assertSame(value1.getRepeatedInt32List(), value2.getRepeatedInt32List());
+    assertSame(value1.getRepeatedImportEnumList(),
+        value2.getRepeatedImportEnumList());
+    assertSame(value1.getRepeatedForeignMessageList(),
+        value2.getRepeatedForeignMessageList());
+  }
+
+  public void testRepeatedArraysAreImmutable() throws Exception {
+    TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+    builder.addRepeatedInt32(100);
+    builder.addRepeatedImportEnum(UnittestImport.ImportEnum.IMPORT_BAR);
+    builder.addRepeatedForeignMessage(ForeignMessage.getDefaultInstance());
+    assertIsUnmodifiable(builder.getRepeatedInt32List());
+    assertIsUnmodifiable(builder.getRepeatedImportEnumList());
+    assertIsUnmodifiable(builder.getRepeatedForeignMessageList());
+    assertIsUnmodifiable(builder.getRepeatedFloatList());
+
+
+    TestAllTypes value = builder.build();
+    assertIsUnmodifiable(value.getRepeatedInt32List());
+    assertIsUnmodifiable(value.getRepeatedImportEnumList());
+    assertIsUnmodifiable(value.getRepeatedForeignMessageList());
+    assertIsUnmodifiable(value.getRepeatedFloatList());
+  }
+
+  private void assertIsUnmodifiable(List<?> list) {
+    if (list == Collections.emptyList()) {
+      // OKAY -- Need to check this b/c EmptyList allows you to call clear.
+    } else {
+      try {
+        list.clear();
+        fail("List wasn't immutable");
+      } catch (UnsupportedOperationException e) {
+        // good
+      }
     }
   }
 
@@ -316,9 +412,19 @@
     assertTrue(Float.isNaN(message.getNanFloat()));
   }
 
+  public void testClear() throws Exception {
+    TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+    TestUtil.assertClear(builder);
+    TestUtil.setAllFields(builder);
+    builder.clear();
+    TestUtil.assertClear(builder);
+  }
+
   public void testReflectionGetters() throws Exception {
     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
     TestUtil.setAllFields(builder);
+    reflectionTester.assertAllFieldsSetViaReflection(builder);
+
     TestAllTypes message = builder.build();
     reflectionTester.assertAllFieldsSetViaReflection(message);
   }
@@ -326,6 +432,8 @@
   public void testReflectionSetters() throws Exception {
     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
     reflectionTester.setAllFieldsViaReflection(builder);
+    TestUtil.assertAllFieldsSet(builder);
+
     TestAllTypes message = builder.build();
     TestUtil.assertAllFieldsSet(message);
   }
@@ -339,6 +447,8 @@
     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
     reflectionTester.setAllFieldsViaReflection(builder);
     reflectionTester.modifyRepeatedFieldsViaReflection(builder);
+    TestUtil.assertRepeatedFieldsModified(builder);
+
     TestAllTypes message = builder.build();
     TestUtil.assertRepeatedFieldsModified(message);
   }
@@ -391,7 +501,7 @@
     new TestUtil.ReflectionTester(TestAllExtensions.getDescriptor(),
                                   TestUtil.getExtensionRegistry());
 
-  public void testExtensionAccessors() throws Exception {
+  public void testExtensionMessageOrBuilder() throws Exception {
     TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
     TestUtil.setAllExtensions(builder);
     TestAllExtensions message = builder.build();
@@ -414,6 +524,8 @@
   public void testExtensionReflectionGetters() throws Exception {
     TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
     TestUtil.setAllExtensions(builder);
+    extensionsReflectionTester.assertAllFieldsSetViaReflection(builder);
+
     TestAllExtensions message = builder.build();
     extensionsReflectionTester.assertAllFieldsSetViaReflection(message);
   }
@@ -421,6 +533,8 @@
   public void testExtensionReflectionSetters() throws Exception {
     TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
     extensionsReflectionTester.setAllFieldsViaReflection(builder);
+    TestUtil.assertAllExtensionsSet(builder);
+
     TestAllExtensions message = builder.build();
     TestUtil.assertAllExtensionsSet(message);
   }
@@ -434,6 +548,8 @@
     TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
     extensionsReflectionTester.setAllFieldsViaReflection(builder);
     extensionsReflectionTester.modifyRepeatedFieldsViaReflection(builder);
+    TestUtil.assertRepeatedExtensionsModified(builder);
+
     TestAllExtensions message = builder.build();
     TestUtil.assertRepeatedExtensionsModified(message);
   }
@@ -491,9 +607,11 @@
   // lite fields directly since they are implemented exactly the same as
   // regular fields.
 
-  public void testLiteExtensionAccessors() throws Exception {
+  public void testLiteExtensionMessageOrBuilder() throws Exception {
     TestAllExtensionsLite.Builder builder = TestAllExtensionsLite.newBuilder();
     TestUtil.setAllExtensions(builder);
+    TestUtil.assertAllExtensionsSet(builder);
+
     TestAllExtensionsLite message = builder.build();
     TestUtil.assertAllExtensionsSet(message);
   }
@@ -502,6 +620,8 @@
     TestAllExtensionsLite.Builder builder = TestAllExtensionsLite.newBuilder();
     TestUtil.setAllExtensions(builder);
     TestUtil.modifyRepeatedExtensions(builder);
+    TestUtil.assertRepeatedExtensionsModified(builder);
+
     TestAllExtensionsLite message = builder.build();
     TestUtil.assertRepeatedExtensionsModified(message);
   }
@@ -609,6 +729,7 @@
     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
     TestUtil.setAllFields(builder);
     TestAllTypes message = builder.build();
+    TestUtil.assertAllFieldsSet(message);
     TestUtil.assertAllFieldsSet(message.toBuilder().build());
   }
 
@@ -646,4 +767,207 @@
     assertTrue(message.getA() != null);
     assertTrue(message.getA() == message);
   }
+
+  public void testSerialize() throws Exception {
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+    TestUtil.setAllFields(builder);
+    TestAllTypes expected = builder.build();
+    ObjectOutputStream out = new ObjectOutputStream(baos);
+    out.writeObject(expected);
+    out.close();
+    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+    ObjectInputStream in = new ObjectInputStream(bais);
+    TestAllTypes actual = (TestAllTypes) in.readObject();
+    assertEquals(expected, actual);
+  }
+
+  public void testSerializePartial() throws Exception {
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+    TestAllTypes expected = builder.buildPartial();
+    ObjectOutputStream out = new ObjectOutputStream(baos);
+    out.writeObject(expected);
+    out.close();
+    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+    ObjectInputStream in = new ObjectInputStream(bais);
+    TestAllTypes actual = (TestAllTypes) in.readObject();
+    assertEquals(expected, actual);
+  }
+
+  public void testEnumValues() {
+     assertEquals(
+         TestAllTypes.NestedEnum.BAR.getNumber(),
+         TestAllTypes.NestedEnum.BAR_VALUE);
+    assertEquals(
+        TestAllTypes.NestedEnum.BAZ.getNumber(),
+        TestAllTypes.NestedEnum.BAZ_VALUE);
+    assertEquals(
+        TestAllTypes.NestedEnum.FOO.getNumber(),
+        TestAllTypes.NestedEnum.FOO_VALUE);
+  }
+
+  public void testNonNestedExtensionInitialization() {
+    assertTrue(NonNestedExtension.nonNestedExtension
+               .getMessageDefaultInstance() instanceof MyNonNestedExtension);
+    assertEquals("nonNestedExtension",
+                 NonNestedExtension.nonNestedExtension.getDescriptor().getName());
+  }
+
+  public void testNestedExtensionInitialization() {
+    assertTrue(MyNestedExtension.recursiveExtension.getMessageDefaultInstance()
+               instanceof MessageToBeExtended);
+    assertEquals("recursiveExtension",
+                 MyNestedExtension.recursiveExtension.getDescriptor().getName());
+  }
+
+  public void testNonNestedExtensionLiteInitialization() {
+    assertTrue(NonNestedExtensionLite.nonNestedExtensionLite
+               .getMessageDefaultInstance() instanceof MyNonNestedExtensionLite);
+  }
+
+  public void testNestedExtensionLiteInitialization() {
+    assertTrue(MyNestedExtensionLite.recursiveExtensionLite
+               .getMessageDefaultInstance() instanceof MessageLiteToBeExtended);
+  }
+
+  public void testInvalidations() throws Exception {
+    GeneratedMessage.enableAlwaysUseFieldBuildersForTesting();
+    TestAllTypes.NestedMessage nestedMessage1 =
+        TestAllTypes.NestedMessage.newBuilder().build();
+    TestAllTypes.NestedMessage nestedMessage2 =
+        TestAllTypes.NestedMessage.newBuilder().build();
+
+    // Set all three flavors (enum, primitive, message and singular/repeated)
+    // and verify no invalidations fired
+    TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
+
+    TestAllTypes.Builder builder = (TestAllTypes.Builder)
+        ((GeneratedMessage) TestAllTypes.getDefaultInstance()).
+            newBuilderForType(mockParent);
+    builder.setOptionalInt32(1);
+    builder.setOptionalNestedEnum(TestAllTypes.NestedEnum.BAR);
+    builder.setOptionalNestedMessage(nestedMessage1);
+    builder.addRepeatedInt32(1);
+    builder.addRepeatedNestedEnum(TestAllTypes.NestedEnum.BAR);
+    builder.addRepeatedNestedMessage(nestedMessage1);
+    assertEquals(0, mockParent.getInvalidationCount());
+
+    // Now tell it we want changes and make sure it's only fired once
+    // And do this for each flavor
+
+    // primitive single
+    builder.buildPartial();
+    builder.setOptionalInt32(2);
+    builder.setOptionalInt32(3);
+    assertEquals(1, mockParent.getInvalidationCount());
+
+    // enum single
+    builder.buildPartial();
+    builder.setOptionalNestedEnum(TestAllTypes.NestedEnum.BAZ);
+    builder.setOptionalNestedEnum(TestAllTypes.NestedEnum.BAR);
+    assertEquals(2, mockParent.getInvalidationCount());
+
+    // message single
+    builder.buildPartial();
+    builder.setOptionalNestedMessage(nestedMessage2);
+    builder.setOptionalNestedMessage(nestedMessage1);
+    assertEquals(3, mockParent.getInvalidationCount());
+
+    // primitive repated
+    builder.buildPartial();
+    builder.addRepeatedInt32(2);
+    builder.addRepeatedInt32(3);
+    assertEquals(4, mockParent.getInvalidationCount());
+
+    // enum repeated
+    builder.buildPartial();
+    builder.addRepeatedNestedEnum(TestAllTypes.NestedEnum.BAZ);
+    builder.addRepeatedNestedEnum(TestAllTypes.NestedEnum.BAZ);
+    assertEquals(5, mockParent.getInvalidationCount());
+
+    // message repeated
+    builder.buildPartial();
+    builder.addRepeatedNestedMessage(nestedMessage2);
+    builder.addRepeatedNestedMessage(nestedMessage1);
+    assertEquals(6, mockParent.getInvalidationCount());
+
+  }
+
+  public void testInvalidations_Extensions() throws Exception {
+    TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
+
+    TestAllExtensions.Builder builder = (TestAllExtensions.Builder)
+        ((GeneratedMessage) TestAllExtensions.getDefaultInstance()).
+            newBuilderForType(mockParent);
+
+    builder.addExtension(UnittestProto.repeatedInt32Extension, 1);
+    builder.setExtension(UnittestProto.repeatedInt32Extension, 0, 2);
+    builder.clearExtension(UnittestProto.repeatedInt32Extension);
+    assertEquals(0, mockParent.getInvalidationCount());
+
+    // Now tell it we want changes and make sure it's only fired once
+    builder.buildPartial();
+    builder.addExtension(UnittestProto.repeatedInt32Extension, 2);
+    builder.addExtension(UnittestProto.repeatedInt32Extension, 3);
+    assertEquals(1, mockParent.getInvalidationCount());
+
+    builder.buildPartial();
+    builder.setExtension(UnittestProto.repeatedInt32Extension, 0, 4);
+    builder.setExtension(UnittestProto.repeatedInt32Extension, 1, 5);
+    assertEquals(2, mockParent.getInvalidationCount());
+
+    builder.buildPartial();
+    builder.clearExtension(UnittestProto.repeatedInt32Extension);
+    builder.clearExtension(UnittestProto.repeatedInt32Extension);
+    assertEquals(3, mockParent.getInvalidationCount());
+  }
+
+  public void testBaseMessageOrBuilder() {
+    // Mostly just makes sure the base interface exists and has some methods.
+    TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+    TestAllTypes message = builder.buildPartial();
+    TestAllTypesOrBuilder builderAsInterface = (TestAllTypesOrBuilder) builder;
+    TestAllTypesOrBuilder messageAsInterface = (TestAllTypesOrBuilder) message;
+
+    assertEquals(
+        messageAsInterface.getDefaultBool(),
+        messageAsInterface.getDefaultBool());
+    assertEquals(
+        messageAsInterface.getOptionalDouble(),
+        messageAsInterface.getOptionalDouble());
+  }
+
+  public void testMessageOrBuilderGetters() {
+    TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+
+    // single fields
+    assertSame(ForeignMessage.getDefaultInstance(),
+        builder.getOptionalForeignMessageOrBuilder());
+    ForeignMessage.Builder subBuilder =
+        builder.getOptionalForeignMessageBuilder();
+    assertSame(subBuilder, builder.getOptionalForeignMessageOrBuilder());
+
+    // repeated fields
+    ForeignMessage m0 = ForeignMessage.newBuilder().buildPartial();
+    ForeignMessage m1 = ForeignMessage.newBuilder().buildPartial();
+    ForeignMessage m2 = ForeignMessage.newBuilder().buildPartial();
+    builder.addRepeatedForeignMessage(m0);
+    builder.addRepeatedForeignMessage(m1);
+    builder.addRepeatedForeignMessage(m2);
+    assertSame(m0, builder.getRepeatedForeignMessageOrBuilder(0));
+    assertSame(m1, builder.getRepeatedForeignMessageOrBuilder(1));
+    assertSame(m2, builder.getRepeatedForeignMessageOrBuilder(2));
+    ForeignMessage.Builder b0 = builder.getRepeatedForeignMessageBuilder(0);
+    ForeignMessage.Builder b1 = builder.getRepeatedForeignMessageBuilder(1);
+    assertSame(b0, builder.getRepeatedForeignMessageOrBuilder(0));
+    assertSame(b1, builder.getRepeatedForeignMessageOrBuilder(1));
+    assertSame(m2, builder.getRepeatedForeignMessageOrBuilder(2));
+
+    List<? extends ForeignMessageOrBuilder> messageOrBuilderList =
+        builder.getRepeatedForeignMessageOrBuilderList();
+    assertSame(b0, messageOrBuilderList.get(0));
+    assertSame(b1, messageOrBuilderList.get(1));
+    assertSame(m2, messageOrBuilderList.get(2));
+  }
 }
diff --git a/java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java b/java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java
new file mode 100644
index 0000000..4dcdc74
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java
@@ -0,0 +1,118 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link LazyStringArrayList}.
+ *
+ * @author [email protected] (Jon Perlow)
+ */
+public class LazyStringArrayListTest extends TestCase {
+
+  private static String STRING_A = "A";
+  private static String STRING_B = "B";
+  private static String STRING_C = "C";
+
+  private static ByteString BYTE_STRING_A = ByteString.copyFromUtf8("A");
+  private static ByteString BYTE_STRING_B = ByteString.copyFromUtf8("B");
+  private static ByteString BYTE_STRING_C = ByteString.copyFromUtf8("C");
+
+  public void testJustStrings() {
+    LazyStringArrayList list = new LazyStringArrayList();
+    list.add(STRING_A);
+    list.add(STRING_B);
+    list.add(STRING_C);
+
+    assertEquals(3, list.size());
+    assertSame(STRING_A, list.get(0));
+    assertSame(STRING_B, list.get(1));
+    assertSame(STRING_C, list.get(2));
+
+    list.set(1, STRING_C);
+    assertSame(STRING_C, list.get(1));
+
+    list.remove(1);
+    assertSame(STRING_A, list.get(0));
+    assertSame(STRING_C, list.get(1));
+  }
+
+  public void testJustByteString() {
+    LazyStringArrayList list = new LazyStringArrayList();
+    list.add(BYTE_STRING_A);
+    list.add(BYTE_STRING_B);
+    list.add(BYTE_STRING_C);
+
+    assertEquals(3, list.size());
+    assertSame(BYTE_STRING_A, list.getByteString(0));
+    assertSame(BYTE_STRING_B, list.getByteString(1));
+    assertSame(BYTE_STRING_C, list.getByteString(2));
+
+    list.remove(1);
+    assertSame(BYTE_STRING_A, list.getByteString(0));
+    assertSame(BYTE_STRING_C, list.getByteString(1));
+  }
+
+  public void testConversionBackAndForth() {
+    LazyStringArrayList list = new LazyStringArrayList();
+    list.add(STRING_A);
+    list.add(BYTE_STRING_B);
+    list.add(BYTE_STRING_C);
+
+    // String a should be the same because it was originally a string
+    assertSame(STRING_A, list.get(0));
+
+    // String b and c should be different because the string has to be computed
+    // from the ByteString
+    String bPrime = list.get(1);
+    assertNotSame(STRING_B, bPrime);
+    assertEquals(STRING_B, bPrime);
+    String cPrime = list.get(2);
+    assertNotSame(STRING_C, cPrime);
+    assertEquals(STRING_C, cPrime);
+
+    // String c and c should stay the same once cached.
+    assertSame(bPrime, list.get(1));
+    assertSame(cPrime, list.get(2));
+
+    // ByteString needs to be computed from string for both a and b
+    ByteString aPrimeByteString = list.getByteString(0);
+    assertEquals(BYTE_STRING_A, aPrimeByteString);
+    ByteString bPrimeByteString = list.getByteString(1);
+    assertNotSame(BYTE_STRING_B, bPrimeByteString);
+    assertEquals(BYTE_STRING_B, list.getByteString(1));
+
+    // Once cached, ByteString should stay cached.
+    assertSame(aPrimeByteString, list.getByteString(0));
+    assertSame(bPrimeByteString, list.getByteString(1));
+  }
+}
diff --git a/java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java b/java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java
new file mode 100644
index 0000000..e6870b5
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java
@@ -0,0 +1,115 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+
+import protobuf_unittest.UnittestProto;
+
+import junit.framework.TestCase;
+
+import java.io.IOException;
+
+/**
+ * Tests to make sure the lazy conversion of UTF8-encoded byte arrays to
+ * strings works correctly.
+ *
+ * @author [email protected] (Jon Perlow)
+ */
+public class LazyStringEndToEndTest extends TestCase {
+
+  private static ByteString TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8 =
+      ByteString.copyFrom(new byte[] {
+          114, 4, -1, 0, -1, 0, -30, 2, 4, -1,
+          0, -1, 0, -30, 2, 4, -1, 0, -1, 0, });
+
+  /**
+   * Tests that an invalid UTF8 string will roundtrip through a parse
+   * and serialization.
+   */
+  public void testParseAndSerialize() throws InvalidProtocolBufferException {
+    UnittestProto.TestAllTypes tV2 = UnittestProto.TestAllTypes.parseFrom(
+        TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8);
+    ByteString bytes = tV2.toByteString();
+    assertEquals(TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8, bytes);
+
+    tV2.getOptionalString();
+    bytes = tV2.toByteString();
+    assertEquals(TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8, bytes);
+  }
+
+  public void testParseAndWrite() throws IOException {
+    UnittestProto.TestAllTypes tV2 = UnittestProto.TestAllTypes.parseFrom(
+        TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8);
+    byte[] sink = new byte[TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8.size()];
+    CodedOutputStream outputStream = CodedOutputStream.newInstance(sink);
+    tV2.writeTo(outputStream);
+    outputStream.flush();
+    assertEquals(
+        TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8,
+        ByteString.copyFrom(sink));
+  }
+
+  public void testCaching() {
+    String a = "a";
+    String b = "b";
+    String c = "c";
+    UnittestProto.TestAllTypes proto = UnittestProto.TestAllTypes.newBuilder()
+        .setOptionalString(a)
+        .addRepeatedString(b)
+        .addRepeatedString(c)
+        .build();
+
+    // String should be the one we passed it.
+    assertSame(a, proto.getOptionalString());
+    assertSame(b, proto.getRepeatedString(0));
+    assertSame(c, proto.getRepeatedString(1));
+
+
+    // There's no way to directly observe that the ByteString is cached
+    // correctly on serialization, but we can observe that it had to recompute
+    // the string after serialization.
+    proto.toByteString();
+    String aPrime = proto.getOptionalString();
+    assertNotSame(a, aPrime);
+    assertEquals(a, aPrime);
+    String bPrime = proto.getRepeatedString(0);
+    assertNotSame(b, bPrime);
+    assertEquals(b, bPrime);
+    String cPrime = proto.getRepeatedString(1);
+    assertNotSame(c, cPrime);
+    assertEquals(c, cPrime);
+
+    // And now the string should stay cached.
+    assertSame(aPrime, proto.getOptionalString());
+    assertSame(bPrime, proto.getRepeatedString(0));
+    assertSame(cPrime, proto.getRepeatedString(1));
+  }
+}
diff --git a/java/src/test/java/com/google/protobuf/LiteTest.java b/java/src/test/java/com/google/protobuf/LiteTest.java
index 728bad9..4e1003d 100644
--- a/java/src/test/java/com/google/protobuf/LiteTest.java
+++ b/java/src/test/java/com/google/protobuf/LiteTest.java
@@ -37,6 +37,11 @@
 
 import junit.framework.TestCase;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
 /**
  * Test lite runtime.
  *
@@ -113,4 +118,28 @@
     assertEquals(7, message2.getExtension(
         UnittestLite.optionalNestedMessageExtensionLite).getBb());
   }
+
+  public void testSerialize() throws Exception {
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    TestAllTypesLite expected =
+      TestAllTypesLite.newBuilder()
+                      .setOptionalInt32(123)
+                      .addRepeatedString("hello")
+                      .setOptionalNestedMessage(
+                          TestAllTypesLite.NestedMessage.newBuilder().setBb(7))
+                      .build();
+    ObjectOutputStream out = new ObjectOutputStream(baos);
+    out.writeObject(expected);
+    out.close();
+    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+    ObjectInputStream in = new ObjectInputStream(bais);
+    TestAllTypesLite actual = (TestAllTypesLite) in.readObject();
+    assertEquals(expected.getOptionalInt32(), actual.getOptionalInt32());
+    assertEquals(expected.getRepeatedStringCount(),
+        actual.getRepeatedStringCount());
+    assertEquals(expected.getRepeatedString(0),
+        actual.getRepeatedString(0));
+    assertEquals(expected.getOptionalNestedMessage().getBb(),
+        actual.getOptionalNestedMessage().getBb());
+  }
 }
diff --git a/java/src/test/java/com/google/protobuf/NestedBuildersTest.java b/java/src/test/java/com/google/protobuf/NestedBuildersTest.java
new file mode 100644
index 0000000..f537580
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/NestedBuildersTest.java
@@ -0,0 +1,185 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import protobuf_unittest.Vehicle;
+import protobuf_unittest.Wheel;
+
+import junit.framework.TestCase;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Test cases that exercise end-to-end use cases involving
+ * {@link SingleFieldBuilder} and {@link RepeatedFieldBuilder}.
+ *
+ * @author [email protected] (Jon Perlow)
+ */
+public class NestedBuildersTest extends TestCase {
+
+  public void testMessagesAndBuilders() {
+    Vehicle.Builder vehicleBuilder = Vehicle.newBuilder();
+    vehicleBuilder.addWheelBuilder()
+        .setRadius(4)
+        .setWidth(1);
+    vehicleBuilder.addWheelBuilder()
+        .setRadius(4)
+        .setWidth(2);
+    vehicleBuilder.addWheelBuilder()
+        .setRadius(4)
+        .setWidth(3);
+    vehicleBuilder.addWheelBuilder()
+        .setRadius(4)
+        .setWidth(4);
+    vehicleBuilder.getEngineBuilder()
+        .setLiters(10);
+
+    Vehicle vehicle = vehicleBuilder.build();
+    assertEquals(4, vehicle.getWheelCount());
+    for (int i = 0; i < 4; i++) {
+      Wheel wheel = vehicle.getWheel(i);
+      assertEquals(4, wheel.getRadius());
+      assertEquals(i + 1, wheel.getWidth());
+    }
+    assertEquals(10, vehicle.getEngine().getLiters());
+
+    for (int i = 0; i < 4; i++) {
+      vehicleBuilder.getWheelBuilder(i)
+          .setRadius(5)
+          .setWidth(i + 10);
+    }
+    vehicleBuilder.getEngineBuilder().setLiters(20);
+
+    vehicle = vehicleBuilder.build();
+    for (int i = 0; i < 4; i++) {
+      Wheel wheel = vehicle.getWheel(i);
+      assertEquals(5, wheel.getRadius());
+      assertEquals(i + 10, wheel.getWidth());
+    }
+    assertEquals(20, vehicle.getEngine().getLiters());
+    assertTrue(vehicle.hasEngine());
+  }
+
+  public void testMessagesAreCached() {
+    Vehicle.Builder vehicleBuilder = Vehicle.newBuilder();
+    vehicleBuilder.addWheelBuilder()
+        .setRadius(1)
+        .setWidth(2);
+    vehicleBuilder.addWheelBuilder()
+        .setRadius(3)
+        .setWidth(4);
+    vehicleBuilder.addWheelBuilder()
+        .setRadius(5)
+        .setWidth(6);
+    vehicleBuilder.addWheelBuilder()
+        .setRadius(7)
+        .setWidth(8);
+
+    // Make sure messages are cached.
+    List<Wheel> wheels = new ArrayList<Wheel>(vehicleBuilder.getWheelList());
+    for (int i = 0; i < wheels.size(); i++) {
+      assertSame(wheels.get(i), vehicleBuilder.getWheel(i));
+    }
+
+    // Now get builders and check they didn't change.
+    for (int i = 0; i < wheels.size(); i++) {
+      vehicleBuilder.getWheel(i);
+    }
+    for (int i = 0; i < wheels.size(); i++) {
+      assertSame(wheels.get(i), vehicleBuilder.getWheel(i));
+    }
+
+    // Change just one
+    vehicleBuilder.getWheelBuilder(3)
+        .setRadius(20).setWidth(20);
+
+    // Now get wheels and check that only that one changed
+    for (int i = 0; i < wheels.size(); i++) {
+      if (i < 3) {
+        assertSame(wheels.get(i), vehicleBuilder.getWheel(i));
+      } else {
+        assertNotSame(wheels.get(i), vehicleBuilder.getWheel(i));
+      }
+    }
+  }
+
+  public void testRemove_WithNestedBuilders() {
+    Vehicle.Builder vehicleBuilder = Vehicle.newBuilder();
+    vehicleBuilder.addWheelBuilder()
+        .setRadius(1)
+        .setWidth(1);
+    vehicleBuilder.addWheelBuilder()
+        .setRadius(2)
+        .setWidth(2);
+    vehicleBuilder.removeWheel(0);
+
+    assertEquals(1, vehicleBuilder.getWheelCount());
+    assertEquals(2, vehicleBuilder.getWheel(0).getRadius());
+  }
+
+  public void testRemove_WithNestedMessages() {
+    Vehicle.Builder vehicleBuilder = Vehicle.newBuilder();
+    vehicleBuilder.addWheel(Wheel.newBuilder()
+        .setRadius(1)
+        .setWidth(1));
+    vehicleBuilder.addWheel(Wheel.newBuilder()
+        .setRadius(2)
+        .setWidth(2));
+    vehicleBuilder.removeWheel(0);
+
+    assertEquals(1, vehicleBuilder.getWheelCount());
+    assertEquals(2, vehicleBuilder.getWheel(0).getRadius());
+  }
+
+  public void testMerge() {
+    Vehicle vehicle1 = Vehicle.newBuilder()
+        .addWheel(Wheel.newBuilder().setRadius(1).build())
+        .addWheel(Wheel.newBuilder().setRadius(2).build())
+        .build();
+
+    Vehicle vehicle2 = Vehicle.newBuilder()
+        .mergeFrom(vehicle1)
+        .build();
+    // List should be the same -- no allocation
+    assertSame(vehicle1.getWheelList(), vehicle2.getWheelList());
+
+    Vehicle vehicle3 = vehicle1.toBuilder().build();
+    assertSame(vehicle1.getWheelList(), vehicle3.getWheelList());
+  }
+
+  public void testGettingBuilderMarksFieldAsHaving() {
+    Vehicle.Builder vehicleBuilder = Vehicle.newBuilder();
+    vehicleBuilder.getEngineBuilder();
+    Vehicle vehicle = vehicleBuilder.buildPartial();
+    assertTrue(vehicle.hasEngine());
+  }
+}
diff --git a/java/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java b/java/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java
new file mode 100644
index 0000000..cdcdcb2
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java
@@ -0,0 +1,190 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import protobuf_unittest.UnittestProto.TestAllTypes;
+import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder;
+
+import junit.framework.TestCase;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Tests for {@link RepeatedFieldBuilder}. This tests basic functionality.
+ * More extensive testing is provided via other tests that exercise the
+ * builder.
+ *
+ * @author [email protected] (Jon Perlow)
+ */
+public class RepeatedFieldBuilderTest extends TestCase {
+
+  public void testBasicUse() {
+    TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
+    RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+        TestAllTypesOrBuilder> builder = newRepeatedFieldBuilder(mockParent);
+    builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build());
+    builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build());
+    assertEquals(0, builder.getMessage(0).getOptionalInt32());
+    assertEquals(1, builder.getMessage(1).getOptionalInt32());
+
+    List<TestAllTypes> list = builder.build();
+    assertEquals(2, list.size());
+    assertEquals(0, list.get(0).getOptionalInt32());
+    assertEquals(1, list.get(1).getOptionalInt32());
+    assertIsUnmodifiable(list);
+
+    // Make sure it doesn't change.
+    List<TestAllTypes> list2 = builder.build();
+    assertSame(list, list2);
+    assertEquals(0, mockParent.getInvalidationCount());
+  }
+
+  public void testGoingBackAndForth() {
+    TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
+    RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+        TestAllTypesOrBuilder> builder = newRepeatedFieldBuilder(mockParent);
+    builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build());
+    builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build());
+    assertEquals(0, builder.getMessage(0).getOptionalInt32());
+    assertEquals(1, builder.getMessage(1).getOptionalInt32());
+
+    // Convert to list
+    List<TestAllTypes> list = builder.build();
+    assertEquals(2, list.size());
+    assertEquals(0, list.get(0).getOptionalInt32());
+    assertEquals(1, list.get(1).getOptionalInt32());
+    assertIsUnmodifiable(list);
+
+    // Update 0th item
+    assertEquals(0, mockParent.getInvalidationCount());
+    builder.getBuilder(0).setOptionalString("foo");
+    assertEquals(1, mockParent.getInvalidationCount());
+    list = builder.build();
+    assertEquals(2, list.size());
+    assertEquals(0, list.get(0).getOptionalInt32());
+      assertEquals("foo", list.get(0).getOptionalString());
+    assertEquals(1, list.get(1).getOptionalInt32());
+    assertIsUnmodifiable(list);
+    assertEquals(1, mockParent.getInvalidationCount());
+  }
+
+  public void testVariousMethods() {
+    TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
+    RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+        TestAllTypesOrBuilder> builder = newRepeatedFieldBuilder(mockParent);
+    builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build());
+    builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(2).build());
+    builder.addBuilder(0, TestAllTypes.getDefaultInstance())
+        .setOptionalInt32(0);
+    builder.addBuilder(TestAllTypes.getDefaultInstance()).setOptionalInt32(3);
+
+    assertEquals(0, builder.getMessage(0).getOptionalInt32());
+    assertEquals(1, builder.getMessage(1).getOptionalInt32());
+    assertEquals(2, builder.getMessage(2).getOptionalInt32());
+    assertEquals(3, builder.getMessage(3).getOptionalInt32());
+
+    assertEquals(0, mockParent.getInvalidationCount());
+    List<TestAllTypes> messages = builder.build();
+    assertEquals(4, messages.size());
+    assertSame(messages, builder.build()); // expect same list
+
+    // Remove a message.
+    builder.remove(2);
+    assertEquals(1, mockParent.getInvalidationCount());
+    assertEquals(3, builder.getCount());
+    assertEquals(0, builder.getMessage(0).getOptionalInt32());
+    assertEquals(1, builder.getMessage(1).getOptionalInt32());
+    assertEquals(3, builder.getMessage(2).getOptionalInt32());
+
+    // Remove a builder.
+    builder.remove(0);
+    assertEquals(1, mockParent.getInvalidationCount());
+    assertEquals(2, builder.getCount());
+    assertEquals(1, builder.getMessage(0).getOptionalInt32());
+    assertEquals(3, builder.getMessage(1).getOptionalInt32());
+
+    // Test clear.
+    builder.clear();
+    assertEquals(1, mockParent.getInvalidationCount());
+    assertEquals(0, builder.getCount());
+    assertTrue(builder.isEmpty());
+  }
+
+  public void testLists() {
+    TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
+    RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+        TestAllTypesOrBuilder> builder = newRepeatedFieldBuilder(mockParent);
+    builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build());
+    builder.addMessage(0,
+        TestAllTypes.newBuilder().setOptionalInt32(0).build());
+    assertEquals(0, builder.getMessage(0).getOptionalInt32());
+    assertEquals(1, builder.getMessage(1).getOptionalInt32());
+
+    // Use list of builders.
+    List<TestAllTypes.Builder> builders = builder.getBuilderList();
+    assertEquals(0, builders.get(0).getOptionalInt32());
+    assertEquals(1, builders.get(1).getOptionalInt32());
+    builders.get(0).setOptionalInt32(10);
+    builders.get(1).setOptionalInt32(11);
+
+    // Use list of protos
+    List<TestAllTypes> protos = builder.getMessageList();
+    assertEquals(10, protos.get(0).getOptionalInt32());
+    assertEquals(11, protos.get(1).getOptionalInt32());
+
+    // Add an item to the builders and verify it's updated in both
+    builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(12).build());
+    assertEquals(3, builders.size());
+    assertEquals(3, protos.size());
+  }
+
+  private void assertIsUnmodifiable(List<?> list) {
+    if (list == Collections.emptyList()) {
+      // OKAY -- Need to check this b/c EmptyList allows you to call clear.
+    } else {
+      try {
+        list.clear();
+        fail("List wasn't immutable");
+      } catch (UnsupportedOperationException e) {
+        // good
+      }
+    }
+  }
+
+  private RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+      TestAllTypesOrBuilder>
+      newRepeatedFieldBuilder(GeneratedMessage.BuilderParent parent) {
+    return new RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+        TestAllTypesOrBuilder>(Collections.<TestAllTypes>emptyList(), false,
+        parent, false);
+  }
+}
diff --git a/java/src/test/java/com/google/protobuf/ServiceTest.java b/java/src/test/java/com/google/protobuf/ServiceTest.java
index 802eb0e..4be84f5 100644
--- a/java/src/test/java/com/google/protobuf/ServiceTest.java
+++ b/java/src/test/java/com/google/protobuf/ServiceTest.java
@@ -244,6 +244,14 @@
       //   make assumptions, so I'm just going to accept any character as the
       //   separator.
       assertTrue(fullName.startsWith(outerName));
+
+      if (!Service.class.isAssignableFrom(innerClass) &&
+          !Message.class.isAssignableFrom(innerClass) &&
+          !ProtocolMessageEnum.class.isAssignableFrom(innerClass)) {
+        // Ignore any classes not generated by the base code generator.
+        continue;
+      }
+
       innerClassNames.add(fullName.substring(outerName.length() + 1));
     }
 
diff --git a/java/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java b/java/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java
new file mode 100644
index 0000000..5c2f7e7
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java
@@ -0,0 +1,155 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import protobuf_unittest.UnittestProto.TestAllTypes;
+import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link SingleFieldBuilder}. This tests basic functionality.
+ * More extensive testing is provided via other tests that exercise the
+ * builder.
+ *
+ * @author [email protected] (Jon Perlow)
+ */
+public class SingleFieldBuilderTest extends TestCase {
+
+  public void testBasicUseAndInvalidations() {
+    TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
+    SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+        TestAllTypesOrBuilder> builder =
+        new SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+            TestAllTypesOrBuilder>(
+            TestAllTypes.getDefaultInstance(),
+            mockParent,
+            false);
+    assertSame(TestAllTypes.getDefaultInstance(), builder.getMessage());
+    assertEquals(TestAllTypes.getDefaultInstance(),
+        builder.getBuilder().buildPartial());
+    assertEquals(0, mockParent.getInvalidationCount());
+
+    builder.getBuilder().setOptionalInt32(10);
+    assertEquals(0, mockParent.getInvalidationCount());
+    TestAllTypes message = builder.build();
+    assertEquals(10, message.getOptionalInt32());
+
+    // Test that we receive invalidations now that build has been called.
+    assertEquals(0, mockParent.getInvalidationCount());
+    builder.getBuilder().setOptionalInt32(20);
+    assertEquals(1, mockParent.getInvalidationCount());
+
+    // Test that we don't keep getting invalidations on every change
+    builder.getBuilder().setOptionalInt32(30);
+    assertEquals(1, mockParent.getInvalidationCount());
+
+  }
+
+  public void testSetMessage() {
+    TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
+    SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+        TestAllTypesOrBuilder> builder =
+        new SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+            TestAllTypesOrBuilder>(
+            TestAllTypes.getDefaultInstance(),
+            mockParent,
+            false);
+    builder.setMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build());
+    assertEquals(0, builder.getMessage().getOptionalInt32());
+
+    // Update message using the builder
+    builder.getBuilder().setOptionalInt32(1);
+    assertEquals(0, mockParent.getInvalidationCount());
+    assertEquals(1, builder.getBuilder().getOptionalInt32());
+    assertEquals(1, builder.getMessage().getOptionalInt32());
+    builder.build();
+    builder.getBuilder().setOptionalInt32(2);
+    assertEquals(2, builder.getBuilder().getOptionalInt32());
+    assertEquals(2, builder.getMessage().getOptionalInt32());
+
+    // Make sure message stays cached
+    assertSame(builder.getMessage(), builder.getMessage());
+  }
+
+  public void testClear() {
+    TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
+    SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+        TestAllTypesOrBuilder> builder =
+        new SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+            TestAllTypesOrBuilder>(
+            TestAllTypes.getDefaultInstance(),
+            mockParent,
+            false);
+    builder.setMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build());
+    assertNotSame(TestAllTypes.getDefaultInstance(), builder.getMessage());
+    builder.clear();
+    assertSame(TestAllTypes.getDefaultInstance(), builder.getMessage());
+
+    builder.getBuilder().setOptionalInt32(1);
+    assertNotSame(TestAllTypes.getDefaultInstance(), builder.getMessage());
+    builder.clear();
+    assertSame(TestAllTypes.getDefaultInstance(), builder.getMessage());
+  }
+
+  public void testMerge() {
+    TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
+    SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+        TestAllTypesOrBuilder> builder =
+        new SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+            TestAllTypesOrBuilder>(
+            TestAllTypes.getDefaultInstance(),
+            mockParent,
+            false);
+
+    // Merge into default field.
+    builder.mergeFrom(TestAllTypes.getDefaultInstance());
+    assertSame(TestAllTypes.getDefaultInstance(), builder.getMessage());
+
+    // Merge into non-default field on existing builder.
+    builder.getBuilder().setOptionalInt32(2);
+    builder.mergeFrom(TestAllTypes.newBuilder()
+        .setOptionalDouble(4.0)
+        .buildPartial());
+    assertEquals(2, builder.getMessage().getOptionalInt32());
+    assertEquals(4.0, builder.getMessage().getOptionalDouble());
+
+    // Merge into non-default field on existing message
+    builder.setMessage(TestAllTypes.newBuilder()
+        .setOptionalInt32(10)
+        .buildPartial());
+    builder.mergeFrom(TestAllTypes.newBuilder()
+        .setOptionalDouble(5.0)
+        .buildPartial());
+    assertEquals(10, builder.getMessage().getOptionalInt32());
+    assertEquals(5.0, builder.getMessage().getOptionalDouble());
+  }
+}
diff --git a/java/src/test/java/com/google/protobuf/SmallSortedMapTest.java b/java/src/test/java/com/google/protobuf/SmallSortedMapTest.java
new file mode 100644
index 0000000..922115f
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/SmallSortedMapTest.java
@@ -0,0 +1,378 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import junit.framework.TestCase;
+
+import java.util.AbstractMap.SimpleEntry;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * @author [email protected] Darick Tong
+ */
+public class SmallSortedMapTest extends TestCase {
+
+  public void testPutAndGetArrayEntriesOnly() {
+    runPutAndGetTest(3);
+  }
+
+  public void testPutAndGetOverflowEntries() {
+    runPutAndGetTest(6);
+  }
+
+  private void runPutAndGetTest(int numElements) {
+    // Test with even and odd arraySize
+    SmallSortedMap<Integer, Integer> map1 =
+        SmallSortedMap.newInstanceForTest(3);
+    SmallSortedMap<Integer, Integer> map2 =
+        SmallSortedMap.newInstanceForTest(4);
+    SmallSortedMap<Integer, Integer> map3 =
+        SmallSortedMap.newInstanceForTest(3);
+    SmallSortedMap<Integer, Integer> map4 =
+        SmallSortedMap.newInstanceForTest(4);
+
+    // Test with puts in ascending order.
+    for (int i = 0; i < numElements; i++) {
+      assertNull(map1.put(i, i + 1));
+      assertNull(map2.put(i, i + 1));
+    }
+    // Test with puts in descending order.
+    for (int i = numElements - 1; i >= 0; i--) {
+      assertNull(map3.put(i, i + 1));
+      assertNull(map4.put(i, i + 1));
+    }
+
+    assertEquals(Math.min(3, numElements), map1.getNumArrayEntries());
+    assertEquals(Math.min(4, numElements), map2.getNumArrayEntries());
+    assertEquals(Math.min(3, numElements), map3.getNumArrayEntries());
+    assertEquals(Math.min(4, numElements), map4.getNumArrayEntries());
+
+    List<SmallSortedMap<Integer, Integer>> allMaps =
+        new ArrayList<SmallSortedMap<Integer, Integer>>();
+    allMaps.add(map1);
+    allMaps.add(map2);
+    allMaps.add(map3);
+    allMaps.add(map4);
+
+    for (SmallSortedMap<Integer, Integer> map : allMaps) {
+      assertEquals(numElements, map.size());
+      for (int i = 0; i < numElements; i++) {
+        assertEquals(new Integer(i + 1), map.get(i));
+      }
+    }
+
+    assertEquals(map1, map2);
+    assertEquals(map2, map3);
+    assertEquals(map3, map4);
+  }
+
+  public void testReplacingPut() {
+    SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
+    for (int i = 0; i < 6; i++) {
+      assertNull(map.put(i, i + 1));
+      assertNull(map.remove(i + 1));
+    }
+    for (int i = 0; i < 6; i++) {
+      assertEquals(new Integer(i + 1), map.put(i, i + 2));
+    }
+  }
+
+  public void testRemove() {
+    SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
+    for (int i = 0; i < 6; i++) {
+      assertNull(map.put(i, i + 1));
+      assertNull(map.remove(i + 1));
+    }
+
+    assertEquals(3, map.getNumArrayEntries());
+    assertEquals(3, map.getNumOverflowEntries());
+    assertEquals(6,  map.size());
+    assertEquals(makeSortedKeySet(0, 1, 2, 3, 4, 5), map.keySet());
+
+    assertEquals(new Integer(2), map.remove(1));
+    assertEquals(3, map.getNumArrayEntries());
+    assertEquals(2, map.getNumOverflowEntries());
+    assertEquals(5,  map.size());
+    assertEquals(makeSortedKeySet(0, 2, 3, 4, 5), map.keySet());
+
+    assertEquals(new Integer(5), map.remove(4));
+    assertEquals(3, map.getNumArrayEntries());
+    assertEquals(1, map.getNumOverflowEntries());
+    assertEquals(4,  map.size());
+    assertEquals(makeSortedKeySet(0, 2, 3, 5), map.keySet());
+
+    assertEquals(new Integer(4), map.remove(3));
+    assertEquals(3, map.getNumArrayEntries());
+    assertEquals(0, map.getNumOverflowEntries());
+    assertEquals(3, map.size());
+    assertEquals(makeSortedKeySet(0, 2, 5), map.keySet());
+
+    assertNull(map.remove(3));
+    assertEquals(3, map.getNumArrayEntries());
+    assertEquals(0, map.getNumOverflowEntries());
+    assertEquals(3, map.size());
+
+    assertEquals(new Integer(1), map.remove(0));
+    assertEquals(2, map.getNumArrayEntries());
+    assertEquals(0, map.getNumOverflowEntries());
+    assertEquals(2, map.size());
+  }
+
+  public void testClear() {
+    SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
+    for (int i = 0; i < 6; i++) {
+      assertNull(map.put(i, i + 1));
+    }
+    map.clear();
+    assertEquals(0, map.getNumArrayEntries());
+    assertEquals(0, map.getNumOverflowEntries());
+    assertEquals(0, map.size());
+  }
+
+  public void testGetArrayEntryAndOverflowEntries() {
+    SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
+    for (int i = 0; i < 6; i++) {
+      assertNull(map.put(i, i + 1));
+    }
+    assertEquals(3, map.getNumArrayEntries());
+    for (int i = 0; i < 3; i++) {
+      Map.Entry<Integer, Integer> entry = map.getArrayEntryAt(i);
+      assertEquals(new Integer(i), entry.getKey());
+      assertEquals(new Integer(i + 1), entry.getValue());
+    }
+    Iterator<Map.Entry<Integer, Integer>> it =
+        map.getOverflowEntries().iterator();
+    for (int i = 3; i < 6; i++) {
+      assertTrue(it.hasNext());
+      Map.Entry<Integer, Integer> entry = it.next();
+      assertEquals(new Integer(i), entry.getKey());
+      assertEquals(new Integer(i + 1), entry.getValue());
+    }
+    assertFalse(it.hasNext());
+  }
+
+  public void testEntrySetContains() {
+    SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
+    for (int i = 0; i < 6; i++) {
+      assertNull(map.put(i, i + 1));
+    }
+    Set<Map.Entry<Integer, Integer>> entrySet = map.entrySet();
+    for (int i = 0; i < 6; i++) {
+      assertTrue(
+          entrySet.contains(new SimpleEntry<Integer, Integer>(i, i + 1)));
+      assertFalse(
+          entrySet.contains(new SimpleEntry<Integer, Integer>(i, i)));
+    }
+  }
+
+  public void testEntrySetAdd() {
+    SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
+    Set<Map.Entry<Integer, Integer>> entrySet = map.entrySet();
+    for (int i = 0; i < 6; i++) {
+      Map.Entry<Integer, Integer> entry =
+          new SimpleEntry<Integer, Integer>(i, i + 1);
+      assertTrue(entrySet.add(entry));
+      assertFalse(entrySet.add(entry));
+    }
+    for (int i = 0; i < 6; i++) {
+      assertEquals(new Integer(i + 1), map.get(i));
+    }
+    assertEquals(3, map.getNumArrayEntries());
+    assertEquals(3, map.getNumOverflowEntries());
+    assertEquals(6, map.size());
+  }
+
+  public void testEntrySetRemove() {
+    SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
+    Set<Map.Entry<Integer, Integer>> entrySet = map.entrySet();
+    for (int i = 0; i < 6; i++) {
+      assertNull(map.put(i, i + 1));
+    }
+    for (int i = 0; i < 6; i++) {
+      Map.Entry<Integer, Integer> entry =
+          new SimpleEntry<Integer, Integer>(i, i + 1);
+      assertTrue(entrySet.remove(entry));
+      assertFalse(entrySet.remove(entry));
+    }
+    assertTrue(map.isEmpty());
+    assertEquals(0, map.getNumArrayEntries());
+    assertEquals(0, map.getNumOverflowEntries());
+    assertEquals(0, map.size());
+  }
+
+  public void testEntrySetClear() {
+    SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
+    for (int i = 0; i < 6; i++) {
+      assertNull(map.put(i, i + 1));
+    }
+    map.entrySet().clear();
+    assertTrue(map.isEmpty());
+    assertEquals(0, map.getNumArrayEntries());
+    assertEquals(0, map.getNumOverflowEntries());
+    assertEquals(0, map.size());
+  }
+
+  public void testEntrySetIteratorNext() {
+    SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
+    for (int i = 0; i < 6; i++) {
+      assertNull(map.put(i, i + 1));
+    }
+    Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
+    for (int i = 0; i < 6; i++) {
+      assertTrue(it.hasNext());
+      Map.Entry<Integer, Integer> entry = it.next();
+      assertEquals(new Integer(i), entry.getKey());
+      assertEquals(new Integer(i + 1), entry.getValue());
+    }
+    assertFalse(it.hasNext());
+  }
+
+  public void testEntrySetIteratorRemove() {
+    SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
+    for (int i = 0; i < 6; i++) {
+      assertNull(map.put(i, i + 1));
+    }
+    Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
+    for (int i = 0; i < 6; i++) {
+      assertTrue(map.containsKey(i));
+      it.next();
+      it.remove();
+      assertFalse(map.containsKey(i));
+      assertEquals(6 - i - 1, map.size());
+    }
+  }
+
+  public void testMapEntryModification() {
+    SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
+    for (int i = 0; i < 6; i++) {
+      assertNull(map.put(i, i + 1));
+    }
+    Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
+    for (int i = 0; i < 6; i++) {
+      Map.Entry<Integer, Integer> entry = it.next();
+      entry.setValue(i + 23);
+    }
+    for (int i = 0; i < 6; i++) {
+      assertEquals(new Integer(i + 23), map.get(i));
+    }
+  }
+
+  public void testMakeImmutable() {
+    SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
+    for (int i = 0; i < 6; i++) {
+      assertNull(map.put(i, i + 1));
+    }
+    map.makeImmutable();
+    assertEquals(new Integer(1), map.get(0));
+    assertEquals(6, map.size());
+
+    try {
+      map.put(23, 23);
+      fail("Expected UnsupportedOperationException");
+    } catch (UnsupportedOperationException expected) {
+    }
+
+    Map<Integer, Integer> other = new HashMap<Integer, Integer>();
+    other.put(23, 23);
+    try {
+      map.putAll(other);
+      fail("Expected UnsupportedOperationException");
+    } catch (UnsupportedOperationException expected) {
+    }
+
+    try {
+      map.remove(0);
+      fail("Expected UnsupportedOperationException");
+    } catch (UnsupportedOperationException expected) {
+    }
+
+    try {
+      map.clear();
+      fail("Expected UnsupportedOperationException");
+    } catch (UnsupportedOperationException expected) {
+    }
+
+    Set<Map.Entry<Integer, Integer>> entrySet = map.entrySet();
+    try {
+      entrySet.clear();
+      fail("Expected UnsupportedOperationException");
+    } catch (UnsupportedOperationException expected) {
+    }
+
+    Iterator<Map.Entry<Integer, Integer>> it = entrySet.iterator();
+    while (it.hasNext()) {
+      Map.Entry<Integer, Integer> entry = it.next();
+      try {
+        entry.setValue(0);
+        fail("Expected UnsupportedOperationException");
+      } catch (UnsupportedOperationException expected) {
+      }
+      try {
+        it.remove();
+        fail("Expected UnsupportedOperationException");
+      } catch (UnsupportedOperationException expected) {
+      }
+    }
+
+    Set<Integer> keySet = map.keySet();
+    try {
+      keySet.clear();
+      fail("Expected UnsupportedOperationException");
+    } catch (UnsupportedOperationException expected) {
+    }
+
+    Iterator<Integer> keys = keySet.iterator();
+    while (keys.hasNext()) {
+      Integer key = keys.next();
+      try {
+        keySet.remove(key);
+        fail("Expected UnsupportedOperationException");
+      } catch (UnsupportedOperationException expected) {
+      }
+      try {
+        keys.remove();
+        fail("Expected UnsupportedOperationException");
+      } catch (UnsupportedOperationException expected) {
+      }
+    }
+  }
+
+  private Set<Integer> makeSortedKeySet(Integer... keys) {
+    return new TreeSet<Integer>(Arrays.<Integer>asList(keys));
+  }
+}
diff --git a/java/src/test/java/com/google/protobuf/TestBadIdentifiers.java b/java/src/test/java/com/google/protobuf/TestBadIdentifiers.java
new file mode 100644
index 0000000..6feec4e
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/TestBadIdentifiers.java
@@ -0,0 +1,49 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests that proto2 api generation doesn't cause compile errors when
+ * compiling protocol buffers that have names that would otherwise conflict
+ * if not fully qualified (like @Deprecated and @Override). 
+ *
+ * @author [email protected] (Jon Perlow)
+ */
+public class TestBadIdentifiers extends TestCase {
+
+  public void testCompilation() {
+    // If this compiles, it means the generation was correct.
+    TestBadIdentifiersProto.Deprecated.newBuilder();
+    TestBadIdentifiersProto.Override.newBuilder();
+  } 
+}
diff --git a/java/src/test/java/com/google/protobuf/TestUtil.java b/java/src/test/java/com/google/protobuf/TestUtil.java
index 2b8b2af..7c458d7 100644
--- a/java/src/test/java/com/google/protobuf/TestUtil.java
+++ b/java/src/test/java/com/google/protobuf/TestUtil.java
@@ -214,7 +214,9 @@
 import static com.google.protobuf.UnittestLite.packedEnumExtensionLite;
 
 import protobuf_unittest.UnittestProto.TestAllExtensions;
+import protobuf_unittest.UnittestProto.TestAllExtensionsOrBuilder;
 import protobuf_unittest.UnittestProto.TestAllTypes;
+import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder;
 import protobuf_unittest.UnittestProto.TestPackedExtensions;
 import protobuf_unittest.UnittestProto.TestPackedTypes;
 import protobuf_unittest.UnittestProto.TestUnpackedTypes;
@@ -225,6 +227,7 @@
 
 import com.google.protobuf.UnittestLite.TestAllTypesLite;
 import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
+import com.google.protobuf.UnittestLite.TestAllExtensionsLiteOrBuilder;
 import com.google.protobuf.UnittestLite.TestPackedExtensionsLite;
 import com.google.protobuf.UnittestLite.ForeignMessageLite;
 import com.google.protobuf.UnittestLite.ForeignEnumLite;
@@ -244,9 +247,12 @@
  * set all fields of a message, serialize it, parse it, and check that all
  * fields are set.
  *
+ * <p>This code is not to be used outside of {@code com.google.protobuf} and
+ * subpackages.
+ *
  * @author [email protected] Kenton Varda
  */
-class TestUtil {
+public final class TestUtil {
   private TestUtil() {}
 
   /** Helper to convert a String to ByteString. */
@@ -485,7 +491,7 @@
    * Assert (using {@code junit.framework.Assert}} that all fields of
    * {@code message} are set to the values assigned by {@code setAllFields}.
    */
-  public static void assertAllFieldsSet(TestAllTypes message) {
+  public static void assertAllFieldsSet(TestAllTypesOrBuilder message) {
     Assert.assertTrue(message.hasOptionalInt32   ());
     Assert.assertTrue(message.hasOptionalInt64   ());
     Assert.assertTrue(message.hasOptionalUint32  ());
@@ -682,13 +688,12 @@
   }
 
   // -------------------------------------------------------------------
-
   /**
    * Assert (using {@code junit.framework.Assert}} that all fields of
    * {@code message} are cleared, and that getting the fields returns their
    * default values.
    */
-  public static void assertClear(TestAllTypes message) {
+  public static void assertClear(TestAllTypesOrBuilder message) {
     // hasBlah() should initially be false for all optional fields.
     Assert.assertFalse(message.hasOptionalInt32   ());
     Assert.assertFalse(message.hasOptionalInt64   ());
@@ -838,7 +843,8 @@
    * {@code message} are set to the values assigned by {@code setAllFields}
    * followed by {@code modifyRepeatedFields}.
    */
-  public static void assertRepeatedFieldsModified(TestAllTypes message) {
+  public static void assertRepeatedFieldsModified(
+      TestAllTypesOrBuilder message) {
     // ModifyRepeatedFields only sets the second repeated element of each
     // field.  In addition to verifying this, we also verify that the first
     // element and size were *not* modified.
@@ -1352,7 +1358,8 @@
    * Assert (using {@code junit.framework.Assert}} that all extensions of
    * {@code message} are set to the values assigned by {@code setAllExtensions}.
    */
-  public static void assertAllExtensionsSet(TestAllExtensions message) {
+  public static void assertAllExtensionsSet(
+      TestAllExtensionsOrBuilder message) {
     Assert.assertTrue(message.hasExtension(optionalInt32Extension   ));
     Assert.assertTrue(message.hasExtension(optionalInt64Extension   ));
     Assert.assertTrue(message.hasExtension(optionalUint32Extension  ));
@@ -1567,7 +1574,7 @@
    * {@code message} are cleared, and that getting the extensions returns their
    * default values.
    */
-  public static void assertExtensionsClear(TestAllExtensions message) {
+  public static void assertExtensionsClear(TestAllExtensionsOrBuilder message) {
     // hasBlah() should initially be false for all optional fields.
     Assert.assertFalse(message.hasExtension(optionalInt32Extension   ));
     Assert.assertFalse(message.hasExtension(optionalInt64Extension   ));
@@ -1752,7 +1759,7 @@
    * followed by {@code modifyRepeatedExtensions}.
    */
   public static void assertRepeatedExtensionsModified(
-      TestAllExtensions message) {
+      TestAllExtensionsOrBuilder message) {
     // ModifyRepeatedFields only sets the second repeated element of each
     // field.  In addition to verifying this, we also verify that the first
     // element and size were *not* modified.
@@ -2106,7 +2113,8 @@
    * Assert (using {@code junit.framework.Assert}} that all extensions of
    * {@code message} are set to the values assigned by {@code setAllExtensions}.
    */
-  public static void assertAllExtensionsSet(TestAllExtensionsLite message) {
+  public static void assertAllExtensionsSet(
+      TestAllExtensionsLiteOrBuilder message) {
     Assert.assertTrue(message.hasExtension(optionalInt32ExtensionLite   ));
     Assert.assertTrue(message.hasExtension(optionalInt64ExtensionLite   ));
     Assert.assertTrue(message.hasExtension(optionalUint32ExtensionLite  ));
@@ -2321,7 +2329,8 @@
    * {@code message} are cleared, and that getting the extensions returns their
    * default values.
    */
-  public static void assertExtensionsClear(TestAllExtensionsLite message) {
+  public static void assertExtensionsClear(
+      TestAllExtensionsLiteOrBuilder message) {
     // hasBlah() should initially be false for all optional fields.
     Assert.assertFalse(message.hasExtension(optionalInt32ExtensionLite   ));
     Assert.assertFalse(message.hasExtension(optionalInt64ExtensionLite   ));
@@ -2478,7 +2487,7 @@
    * followed by {@code modifyRepeatedExtensions}.
    */
   public static void assertRepeatedExtensionsModified(
-      TestAllExtensionsLite message) {
+      TestAllExtensionsLiteOrBuilder message) {
     // ModifyRepeatedFields only sets the second repeated element of each
     // field.  In addition to verifying this, we also verify that the first
     // element and size were *not* modified.
@@ -3015,7 +3024,7 @@
      * {@code message} are set to the values assigned by {@code setAllFields},
      * using the {@link Message} reflection interface.
      */
-    public void assertAllFieldsSetViaReflection(Message message) {
+    public void assertAllFieldsSetViaReflection(MessageOrBuilder message) {
       Assert.assertTrue(message.hasField(f("optional_int32"   )));
       Assert.assertTrue(message.hasField(f("optional_int64"   )));
       Assert.assertTrue(message.hasField(f("optional_uint32"  )));
@@ -3248,7 +3257,7 @@
      * {@code message} are cleared, and that getting the fields returns their
      * default values, using the {@link Message} reflection interface.
      */
-    public void assertClearViaReflection(Message message) {
+    public void assertClearViaReflection(MessageOrBuilder message) {
       // has_blah() should initially be false for all optional fields.
       Assert.assertFalse(message.hasField(f("optional_int32"   )));
       Assert.assertFalse(message.hasField(f("optional_int64"   )));
@@ -3405,9 +3414,11 @@
       Assert.assertEquals("123", message.getField(f("default_cord")));
     }
 
+
     // ---------------------------------------------------------------
 
-    public void assertRepeatedFieldsModifiedViaReflection(Message message) {
+    public void assertRepeatedFieldsModifiedViaReflection(
+        MessageOrBuilder message) {
       // ModifyRepeatedFields only sets the second repeated element of each
       // field.  In addition to verifying this, we also verify that the first
       // element and size were *not* modified.
@@ -3543,7 +3554,7 @@
       message.addRepeatedField(f("packed_enum" ),  foreignBaz);
     }
 
-    public void assertPackedFieldsSetViaReflection(Message message) {
+    public void assertPackedFieldsSetViaReflection(MessageOrBuilder message) {
       Assert.assertEquals(2, message.getRepeatedFieldCount(f("packed_int32"   )));
       Assert.assertEquals(2, message.getRepeatedFieldCount(f("packed_int64"   )));
       Assert.assertEquals(2, message.getRepeatedFieldCount(f("packed_uint32"  )));
@@ -3699,7 +3710,7 @@
 
   /**
    * @param filePath The path relative to
-   * {@link com.google.testing.util.TestUtil#getDefaultSrcDir}.
+   * {@link #getTestDataDir}.
    */
   public static String readTextFromFile(String filePath) {
     return readBytesFromFile(filePath).toStringUtf8();
@@ -3728,8 +3739,8 @@
   }
 
   /**
-   * @param filePath The path relative to
-   * {@link com.google.testing.util.TestUtil#getDefaultSrcDir}.
+   * @param filename The path relative to
+   * {@link #getTestDataDir}.
    */
   public static ByteString readBytesFromFile(String filename) {
     File fullPath = new File(getTestDataDir(), filename);
@@ -3749,7 +3760,7 @@
   /**
    * Get the bytes of the "golden message".  This is a serialized TestAllTypes
    * with all fields set as they would be by
-   * {@link setAllFields(TestAllTypes.Builder)}, but it is loaded from a file
+   * {@link #setAllFields(TestAllTypes.Builder)}, but it is loaded from a file
    * on disk rather than generated dynamically.  The file is actually generated
    * by C++ code, so testing against it verifies compatibility with C++.
    */
@@ -3764,7 +3775,7 @@
   /**
    * Get the bytes of the "golden packed fields message".  This is a serialized
    * TestPackedTypes with all fields set as they would be by
-   * {@link setPackedFields(TestPackedTypes.Builder)}, but it is loaded from a
+   * {@link #setPackedFields(TestPackedTypes.Builder)}, but it is loaded from a
    * file on disk rather than generated dynamically.  The file is actually
    * generated by C++ code, so testing against it verifies compatibility with
    * C++.
@@ -3777,4 +3788,24 @@
     return goldenPackedFieldsMessage;
   }
   private static ByteString goldenPackedFieldsMessage = null;
+
+  /**
+   * Mock implementation of {@link GeneratedMessage.BuilderParent} for testing.
+   *
+   * @author [email protected] (Jon Perlow)
+   */
+  public static class MockBuilderParent
+      implements GeneratedMessage.BuilderParent {
+
+    private int invalidations;
+
+    @Override
+    public void markDirty() {
+      invalidations++;
+    }
+
+    public int getInvalidationCount() {
+      return invalidations;
+    }
+  }
 }
diff --git a/java/src/test/java/com/google/protobuf/TextFormatTest.java b/java/src/test/java/com/google/protobuf/TextFormatTest.java
index ad48157..47690d1 100644
--- a/java/src/test/java/com/google/protobuf/TextFormatTest.java
+++ b/java/src/test/java/com/google/protobuf/TextFormatTest.java
@@ -31,14 +31,14 @@
 package com.google.protobuf;
 
 import com.google.protobuf.Descriptors.FieldDescriptor;
-import protobuf_unittest.UnittestProto.OneString;
-import protobuf_unittest.UnittestProto.TestAllTypes;
-import protobuf_unittest.UnittestProto.TestAllExtensions;
-import protobuf_unittest.UnittestProto.TestEmptyMessage;
-import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage;
 import protobuf_unittest.UnittestMset.TestMessageSet;
 import protobuf_unittest.UnittestMset.TestMessageSetExtension1;
 import protobuf_unittest.UnittestMset.TestMessageSetExtension2;
+import protobuf_unittest.UnittestProto.OneString;
+import protobuf_unittest.UnittestProto.TestAllExtensions;
+import protobuf_unittest.UnittestProto.TestAllTypes;
+import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage;
+import protobuf_unittest.UnittestProto.TestEmptyMessage;
 
 import junit.framework.TestCase;
 
@@ -133,40 +133,44 @@
     assertEquals(allExtensionsSetText, javaText);
   }
 
+  // Creates an example unknown field set.
+  private UnknownFieldSet makeUnknownFieldSet() {
+    return UnknownFieldSet.newBuilder()
+        .addField(5,
+            UnknownFieldSet.Field.newBuilder()
+            .addVarint(1)
+            .addFixed32(2)
+            .addFixed64(3)
+            .addLengthDelimited(ByteString.copyFromUtf8("4"))
+            .addGroup(
+                UnknownFieldSet.newBuilder()
+                .addField(10,
+                    UnknownFieldSet.Field.newBuilder()
+                    .addVarint(5)
+                    .build())
+                .build())
+            .build())
+        .addField(8,
+            UnknownFieldSet.Field.newBuilder()
+            .addVarint(1)
+            .addVarint(2)
+            .addVarint(3)
+            .build())
+        .addField(15,
+            UnknownFieldSet.Field.newBuilder()
+            .addVarint(0xABCDEF1234567890L)
+            .addFixed32(0xABCD1234)
+            .addFixed64(0xABCDEF1234567890L)
+            .build())
+        .build();
+  }
+
   public void testPrintUnknownFields() throws Exception {
     // Test printing of unknown fields in a message.
 
     TestEmptyMessage message =
       TestEmptyMessage.newBuilder()
-        .setUnknownFields(
-          UnknownFieldSet.newBuilder()
-            .addField(5,
-              UnknownFieldSet.Field.newBuilder()
-                .addVarint(1)
-                .addFixed32(2)
-                .addFixed64(3)
-                .addLengthDelimited(ByteString.copyFromUtf8("4"))
-                .addGroup(
-                  UnknownFieldSet.newBuilder()
-                    .addField(10,
-                      UnknownFieldSet.Field.newBuilder()
-                        .addVarint(5)
-                        .build())
-                    .build())
-                .build())
-            .addField(8,
-              UnknownFieldSet.Field.newBuilder()
-                .addVarint(1)
-                .addVarint(2)
-                .addVarint(3)
-                .build())
-            .addField(15,
-              UnknownFieldSet.Field.newBuilder()
-                .addVarint(0xABCDEF1234567890L)
-                .addFixed32(0xABCD1234)
-                .addFixed64(0xABCDEF1234567890L)
-                .build())
-            .build())
+        .setUnknownFields(makeUnknownFieldSet())
         .build();
 
     assertEquals(
@@ -409,6 +413,9 @@
       "1:16: Expected \"true\" or \"false\".",
       "optional_bool: maybe");
     assertParseError(
+      "1:16: Expected \"true\" or \"false\".",
+      "optional_bool: 2");
+    assertParseError(
       "1:18: Expected string.",
       "optional_string: 123");
     assertParseError(
@@ -485,6 +492,11 @@
     assertEquals(bytes(0xe1, 0x88, 0xb4),
                  TextFormat.unescapeBytes("\\xe1\\x88\\xb4"));
 
+    // Handling of strings with unescaped Unicode characters > 255.
+    final String zh = "\u9999\u6e2f\u4e0a\u6d77\ud84f\udf80\u8c50\u9280\u884c";
+    ByteString zhByteString = ByteString.copyFromUtf8(zh);
+    assertEquals(zhByteString, TextFormat.unescapeBytes(zh));
+
     // Errors.
     try {
       TextFormat.unescapeText("\\x");
@@ -626,6 +638,13 @@
     }
   }
 
+  public void testParseString() throws Exception {
+    final String zh = "\u9999\u6e2f\u4e0a\u6d77\ud84f\udf80\u8c50\u9280\u884c";
+    TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+    TextFormat.merge("optional_string: \"" + zh + "\"", builder);
+    assertEquals(zh, builder.getOptionalString());
+  }
+
   public void testParseLongString() throws Exception {
     String longText =
       "123456789012345678901234567890123456789012345678901234567890" +
@@ -654,9 +673,80 @@
     assertEquals(longText, builder.getOptionalString());
   }
 
+  public void testParseBoolean() throws Exception {
+    String goodText =
+        "repeated_bool: t  repeated_bool : 0\n" +
+        "repeated_bool :f repeated_bool:1";
+    String goodTextCanonical =
+        "repeated_bool: true\n" +
+        "repeated_bool: false\n" +
+        "repeated_bool: false\n" +
+        "repeated_bool: true\n";
+    TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+    TextFormat.merge(goodText, builder);
+    assertEquals(goodTextCanonical, builder.build().toString());
+
+    try {
+      TestAllTypes.Builder badBuilder = TestAllTypes.newBuilder();
+      TextFormat.merge("optional_bool:2", badBuilder);
+      fail("Should have thrown an exception.");
+    } catch (TextFormat.ParseException e) {
+      // success
+    }
+    try {
+      TestAllTypes.Builder badBuilder = TestAllTypes.newBuilder();
+      TextFormat.merge("optional_bool: foo", badBuilder);
+      fail("Should have thrown an exception.");
+    } catch (TextFormat.ParseException e) {
+      // success
+    }
+  }
+
   public void testParseAdjacentStringLiterals() throws Exception {
     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
     TextFormat.merge("optional_string: \"foo\" 'corge' \"grault\"", builder);
     assertEquals("foocorgegrault", builder.getOptionalString());
   }
+
+  public void testPrintFieldValue() throws Exception {
+    assertPrintFieldValue("\"Hello\"", "Hello", "repeated_string");
+    assertPrintFieldValue("123.0",  123f, "repeated_float");
+    assertPrintFieldValue("123.0",  123d, "repeated_double");
+    assertPrintFieldValue("123",  123, "repeated_int32");
+    assertPrintFieldValue("123",  123L, "repeated_int64");
+    assertPrintFieldValue("true",  true, "repeated_bool");
+    assertPrintFieldValue("4294967295", 0xFFFFFFFF, "repeated_uint32");
+    assertPrintFieldValue("18446744073709551615",  0xFFFFFFFFFFFFFFFFL,
+        "repeated_uint64");
+    assertPrintFieldValue("\"\\001\\002\\003\"",
+        ByteString.copyFrom(new byte[] {1, 2, 3}), "repeated_bytes");
+  }
+
+  private void assertPrintFieldValue(String expect, Object value,
+      String fieldName) throws Exception {
+    TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+    StringBuilder sb = new StringBuilder();
+    TextFormat.printFieldValue(
+        TestAllTypes.getDescriptor().findFieldByName(fieldName),
+        value, sb);
+    assertEquals(expect, sb.toString());
+  }
+
+  public void testShortDebugString() {
+    assertEquals("optional_nested_message { bb: 42 } repeated_int32: 1"
+        + " repeated_uint32: 2",
+        TextFormat.shortDebugString(TestAllTypes.newBuilder()
+            .addRepeatedInt32(1)
+            .addRepeatedUint32(2)
+            .setOptionalNestedMessage(
+                NestedMessage.newBuilder().setBb(42).build())
+            .build()));
+  }
+
+  public void testShortDebugString_unknown() {
+    assertEquals("5: 1 5: 0x00000002 5: 0x0000000000000003 5: \"4\" 5 { 10: 5 }"
+        + " 8: 1 8: 2 8: 3 15: 12379813812177893520 15: 0xabcd1234 15:"
+        + " 0xabcdef1234567890",
+        TextFormat.shortDebugString(makeUnknownFieldSet()));
+  }
 }
diff --git a/java/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java b/java/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java
new file mode 100644
index 0000000..ed5d069
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java
@@ -0,0 +1,152 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import junit.framework.TestCase;
+
+import java.util.Iterator;
+import java.util.ListIterator;
+
+/**
+ * Tests for {@link UnmodifiableLazyStringList}.
+ *
+ * @author [email protected] (Jon Perlow)
+ */
+public class UnmodifiableLazyStringListTest extends TestCase {
+
+  private static String STRING_A = "A";
+  private static String STRING_B = "B";
+  private static String STRING_C = "C";
+
+  private static ByteString BYTE_STRING_A = ByteString.copyFromUtf8("A");
+  private static ByteString BYTE_STRING_B = ByteString.copyFromUtf8("B");
+  private static ByteString BYTE_STRING_C = ByteString.copyFromUtf8("C");
+
+  public void testReadOnlyMethods() {
+    LazyStringArrayList rawList = createSampleList();
+    UnmodifiableLazyStringList list = new UnmodifiableLazyStringList(rawList);
+    assertEquals(3, list.size());
+    assertSame(STRING_A, list.get(0));
+    assertSame(STRING_B, list.get(1));
+    assertSame(STRING_C, list.get(2));
+    assertEquals(BYTE_STRING_A, list.getByteString(0));
+    assertEquals(BYTE_STRING_B, list.getByteString(1));
+    assertEquals(BYTE_STRING_C, list.getByteString(2));
+  }
+
+  public void testModifyMethods() {
+    LazyStringArrayList rawList = createSampleList();
+    UnmodifiableLazyStringList list = new UnmodifiableLazyStringList(rawList);
+
+    try {
+      list.remove(0);
+      fail();
+    } catch (UnsupportedOperationException e) {
+      // expected
+    }
+    assertEquals(3, list.size());
+
+    try {
+      list.add(STRING_B);
+      fail();
+    } catch (UnsupportedOperationException e) {
+      // expected
+    }
+    assertEquals(3, list.size());
+
+    try {
+      list.set(1, STRING_B);
+      fail();
+    } catch (UnsupportedOperationException e) {
+      // expected
+    }
+  }
+
+  public void testIterator() {
+    LazyStringArrayList rawList = createSampleList();
+    UnmodifiableLazyStringList list = new UnmodifiableLazyStringList(rawList);
+
+    Iterator<String> iter = list.iterator();
+    int count = 0;
+    while (iter.hasNext()) {
+      iter.next();
+      count++;
+      try {
+        iter.remove();
+        fail();
+      } catch (UnsupportedOperationException e) {
+        // expected
+      }
+    }
+    assertEquals(3, count);
+
+  }
+
+  public void testListIterator() {
+    LazyStringArrayList rawList = createSampleList();
+    UnmodifiableLazyStringList list = new UnmodifiableLazyStringList(rawList);
+
+    ListIterator<String> iter = list.listIterator();
+    int count = 0;
+    while (iter.hasNext()) {
+      iter.next();
+      count++;
+      try {
+        iter.remove();
+        fail();
+      } catch (UnsupportedOperationException e) {
+        // expected
+      }
+      try {
+        iter.set("bar");
+        fail();
+      } catch (UnsupportedOperationException e) {
+        // expected
+      }
+      try {
+        iter.add("bar");
+        fail();
+      } catch (UnsupportedOperationException e) {
+        // expected
+      }
+    }
+    assertEquals(3, count);
+
+  }
+
+  private LazyStringArrayList createSampleList() {
+    LazyStringArrayList rawList = new LazyStringArrayList();
+    rawList.add(STRING_A);
+    rawList.add(STRING_B);
+    rawList.add(STRING_C);
+    return rawList;
+  }
+}
diff --git a/java/src/test/java/com/google/protobuf/multiple_files_test.proto b/java/src/test/java/com/google/protobuf/multiple_files_test.proto
index 060f159..9a04014 100644
--- a/java/src/test/java/com/google/protobuf/multiple_files_test.proto
+++ b/java/src/test/java/com/google/protobuf/multiple_files_test.proto
@@ -33,6 +33,10 @@
 // A proto file which tests the java_multiple_files option.
 
 
+// Some generic_services option(s) added automatically.
+// See:  http://go/proto2-generic-services-default
+option java_generic_services = true;   // auto-added
+
 import "google/protobuf/unittest.proto";
 
 package protobuf_unittest;
diff --git a/java/src/test/java/com/google/protobuf/nested_builders_test.proto b/java/src/test/java/com/google/protobuf/nested_builders_test.proto
new file mode 100644
index 0000000..abffb9d
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/nested_builders_test.proto
@@ -0,0 +1,53 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: [email protected] (Jon Perlow)
+//
+
+package protobuf_unittest;
+
+option java_multiple_files = true;
+option java_outer_classname = "NestedBuilders";
+
+
+message Vehicle {
+  optional Engine engine = 1;
+  repeated Wheel wheel = 2;
+}
+
+message Engine {
+  optional int32 cylinder = 1;
+  optional int32 liters = 2;
+}
+
+message Wheel {
+  optional int32 radius = 1;
+  optional int32 width = 2;
+}
diff --git a/java/src/test/java/com/google/protobuf/nested_extension.proto b/java/src/test/java/com/google/protobuf/nested_extension.proto
new file mode 100644
index 0000000..9fe5d56
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/nested_extension.proto
@@ -0,0 +1,45 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: Darick Tong ([email protected])
+//
+// A proto file with nested extensions. Note that this must be defined in
+// a separate file to properly test the initialization of the outer class.
+
+
+import "com/google/protobuf/non_nested_extension.proto";
+
+package protobuf_unittest;
+
+message MyNestedExtension {
+  extend MessageToBeExtended {
+    optional MessageToBeExtended recursiveExtension = 2;
+  }
+}
diff --git a/java/src/test/java/com/google/protobuf/nested_extension_lite.proto b/java/src/test/java/com/google/protobuf/nested_extension_lite.proto
new file mode 100644
index 0000000..16ee46e
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/nested_extension_lite.proto
@@ -0,0 +1,48 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: Darick Tong ([email protected])
+//
+// A proto file with nested extensions for a MessageLite messages. Note that
+// this must be defined in a separate file to properly test the initialization
+// of the outer class.
+
+
+package protobuf_unittest;
+
+option optimize_for = LITE_RUNTIME;
+
+import "com/google/protobuf/non_nested_extension_lite.proto";
+
+message MyNestedExtensionLite {
+  extend MessageLiteToBeExtended {
+    optional MessageLiteToBeExtended recursiveExtensionLite = 3;
+  }
+}
diff --git a/java/src/test/java/com/google/protobuf/non_nested_extension.proto b/java/src/test/java/com/google/protobuf/non_nested_extension.proto
new file mode 100644
index 0000000..f61b419
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/non_nested_extension.proto
@@ -0,0 +1,48 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: Darick Tong ([email protected])
+//
+// A proto file with extensions.
+
+
+package protobuf_unittest;
+
+message MessageToBeExtended {
+  extensions 1 to max;
+}
+
+message MyNonNestedExtension {
+}
+
+extend MessageToBeExtended {
+  optional MyNonNestedExtension nonNestedExtension = 1;
+}
+
diff --git a/java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto b/java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto
new file mode 100644
index 0000000..3c82659
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto
@@ -0,0 +1,50 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: Darick Tong ([email protected])
+//
+// A proto file with extensions for a MessageLite messages.
+
+
+package protobuf_unittest;
+
+option optimize_for = LITE_RUNTIME;
+
+message MessageLiteToBeExtended {
+  extensions 1 to max;
+}
+
+message MyNonNestedExtensionLite {
+}
+
+extend MessageLiteToBeExtended {
+  optional MyNonNestedExtensionLite nonNestedExtensionLite = 1;
+}
+
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
new file mode 100644
index 0000000..5cc42af
--- /dev/null
+++ b/java/src/test/java/com/google/protobuf/test_bad_identifiers.proto
@@ -0,0 +1,66 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: [email protected] (Jon Perlow)
+
+// This file tests that various identifiers work as field and type names even
+// though the same identifiers are used internally by the java code generator.
+
+
+// Some generic_services option(s) added automatically.
+// See:  http://go/proto2-generic-services-default
+option java_generic_services = true;   // auto-added
+
+package io_protocol_tests;
+
+option java_package = "com.google.protobuf";
+option java_outer_classname = "TestBadIdentifiersProto";
+
+message TestMessage {
+}
+
+message Deprecated {
+  enum TestEnum {
+    FOO = 1;
+  }
+
+  optional int32 field1 = 1 [deprecated=true];
+  optional TestEnum field2 = 2 [deprecated=true];
+  optional TestMessage field3 = 3 [deprecated=true];
+}
+
+message Override {
+ optional int32 override = 1;
+}
+
+service TestConflictingMethodNames {
+  rpc Override(TestMessage) returns (TestMessage);
+}
+